目录:
为什么要做监控?
监控系统业务需求分析;
监控系统架构设计;
监控系统表结构设计;
一、为什么要做监控系统?
市面上已经有很多成熟的监控系统,例如zabbix、nagios,为什么自己开发监控系统?
1.提示个人开发能力;
2.熟悉成熟监控系统的设计思想、架构、解耦原则;
3.调用 zabbix/openfalcon/nagios的API进行个人开发定制;
二、监控系统业务需求分析
1、可以监控常用系统服务、应用、网络设备等;
硬件层面:
服务器温度、磁盘RAID阵列......
系统层面:
存活状态、CPU、RAM、load负载
应用层:
mysql、Nginx、Django、LVS、HAProxy
业务层面:
PV、UV、订单
2、1台服务器上可以监控N个不同的服务、不同的服务汇报监控数据的时间间隔也不同;(例如CPU负载过高60分钟汇报1次、Nginx服务端口是否关闭3分钟汇报1次)
3.同1个服务在不同被监控机器的监控间隔、报警的阀值可不同(不同被监控机器的性能不同)
4.可以批量的给一批机器 添加、删除、修改监控项;
5.告警级别:
- 不同的服务 因为业务重要程度不同,如果出了问题可以设置不同的报警级别
- 可以指定特定的服务或告警级别的事件通知给特定的用户
- 告警的升级设定
- 实现用最少的空间占用量存储最多的有效数据
- 如何做到1s中之内取出一台主机上所有服务的5年的监控数据?(趋势图展示)
7.数据可视化,如何做出简洁美观的用户界面?
6.安装、部署,支持主动/被动监控。
a.可在client端上安装agent插件,向server端主动汇报监控信息(主动监控)
b.可以通过server端去抓取client端监控数据(被动监控)
三、监控系统架构设计
client 向server 获取自己的监控配置项
1.server DB存储客户端配置信息
2.client定时去服务器获取配置信息(更新配置项)
client 向server 汇报监控数据
1.客户端通过命令获取返回值
2.组织数据汇报监控数据给Django,然后Django打时间戳把监控信息存到redis
3.Django在把监控信息存到redis的同时会触发 1个数据优化存储程序、阀值判断和报警程序
4.Monitor Data Handler去redis轮训检测哪个主机 没有汇报信息;
四、监控系统表结构设计
from django.db import models from django.db import models from django.contrib.auth.models import User class Host(models.Model): '''被监控主机表 ''' name=models.CharField(max_length=64,unique=True,verbose_name='主机名') #唯一 ipaddr=models.GenericIPAddressField(unique=True,verbose_name='IP地址') host_groups=models.ManyToManyField('HostGroup',blank=True,verbose_name='主机组') #1台主机可以同时属于多个whatever 主机组 templates=models.ManyToManyField('Template',blank=True,verbose_name='所属模板') #主机和模板直接 多 对 多,可以给单台主机 定制模板 monitored_by_choices = ( ('agent', 'Agent'), ('snmp', 'SNMP'), ('wget', 'WGET'), ) monitored_by = models.CharField(max_length=64, choices=monitored_by_choices,verbose_name='监控方式') status_choices = ( (1, 'Online'), (2, 'Down'), (3, 'Unreachable'), (4, 'Offline'), (5, 'Problem'), ) status = models.IntegerField(choices=status_choices, default=1,verbose_name='存活状态') host_alive_check_interval = models.IntegerField(default=30, verbose_name='主机存活状态检测间隔') memo = models.TextField(blank=True,null=True,verbose_name='备注信息') class HostGroup(models.Model): '''被监控主机 组 ''' name = models.CharField(max_length=64,unique=True) templates = models.ManyToManyField("Template",blank=True) #组可以有模板,方便批量管理、操作; 主机也可以有模板 memo = models.TextField(blank=True,null=True,verbose_name="备注") class ServiceIndex(models.Model): '''监控的服务的 正常指标列表''' name = models.CharField(max_length=64,verbose_name='指标名称') key =models.CharField(max_length=64,verbose_name='cpu idle') data_type_choices = (#客户端汇报的监控信息的数据类型 ('int',"int"), ('float',"float"), ('str',"string") ) data_type = models.CharField(max_length=32,choices=data_type_choices,default='int',verbose_name='指标数据类型') memo = models.CharField(max_length=128,blank=True,null=True,verbose_name="备注") class Service(models.Model): '''监控的某项服务''' name = models.CharField(max_length=64,unique=True,verbose_name='服务名称') interval = models.IntegerField(default=60,verbose_name='监控间隔') plugin_name = models.CharField(max_length=64,default='n/a',verbose_name='插件名') items = models.ManyToManyField('ServiceIndex',blank=True,verbose_name="指标列表",)#1个CPU服务可能有多个指标 has_sub_service = models.BooleanField(default=False,help_text="如果一个服务还有独立的子服务 ,选择这个,比如 网卡服务有多个独立的子网卡") #如果一个服务还有独立的子服务 ,选择这个,比如 网卡服务有多个独立的子网卡 memo = models.CharField(max_length=128,blank=True,null=True,verbose_name="备注",) class Template(models.Model): name = models.CharField(max_length=64,unique=True,verbose_name='模版名称') services = models.ManyToManyField('Service',verbose_name="服务列表") #1个模板监控多个服务,1个服务属于多个模板 triggers = models.ManyToManyField('Trigger',verbose_name="触发器列表",blank=True)#哪个服务 class TriggerExpression(models.Model): ''' 触发器表达式 if cpu.idle(avg(5mins)) < 80% and cpu.iowait(hit(10,3)) > 50% = warning 1 trigger1 cpu.idle(avg(5mins)) < 80% and 2 trigger1 cpu.iowait(hit(10,3)) > 50% 通过trigger关联:查询出当前trigger的所有触发器表达式,从上到下按顺序执行! ''' trigger = models.ForeignKey('Trigger',verbose_name="所属触发器") #1个触发器表方式(条件)只能属于1个触发器 service = models.ForeignKey(Service,verbose_name="关联服务") service_index = models.ForeignKey(ServiceIndex,verbose_name="关联服务指标") specified_index_key = models.CharField(verbose_name="只监控专门指定的指标key",max_length=64,blank=True,null=True) operator_type_choices = (('eq','='),('lt','<'),('gt','>')) operator_type = models.CharField("运算符",choices=operator_type_choices,max_length=32) data_calc_type_choices = ( ('avg','Average'), ('max','Max'), ('hit','Hit'), ('last','Last'), ) data_calc_func= models.CharField(verbose_name="数据处理方式",choices=data_calc_type_choices,max_length=64) data_calc_args = models.CharField(verbose_name="函数传入参数",help_text="若是多个参数,则用,号分开,第一个值是时间",max_length=64) threshold = models.IntegerField(verbose_name="阈值") logic_type_choices = (('or','OR'),('and','AND')) logic_type = models.CharField(verbose_name="与一个条件的逻辑关系",choices=logic_type_choices,max_length=32,blank=True,null=True) class Meta: pass #unique_together = ('trigger_id','service') class Trigger(models.Model): '''触发器表 ''' name = models.CharField(u'触发器名称',max_length=64) severity_choices = ( (1,'Information'), (2,'Warning'), (3,'Average'), (4,'High'), (5,'Diaster'), ) #expressions = models.ManyToManyField(TriggerExpression,verbose_name=u"条件表达式") severity = models.IntegerField(u'告警级别',choices=severity_choices) enabled = models.BooleanField(default=True) memo = models.TextField(verbose_name="备注",blank=True,null=True) class Action(models.Model): '''报警方式 ''' name = models.CharField(max_length=64,unique=True) host_groups = models.ManyToManyField('HostGroup',blank=True) hosts = models.ManyToManyField('Host',blank=True) triggers = models.ManyToManyField('Trigger',blank=True,help_text="想让哪些trigger触发当前报警动作") interval = models.IntegerField(u'告警间隔(s)',default=300) operations = models.ManyToManyField('ActionOperation',verbose_name='允许报警升级的设置!') recover_notice = models.BooleanField(u'故障恢复后发送通知消息',default=True) recover_subject = models.CharField(max_length=128,blank=True,null=True) recover_message = models.TextField(blank=True,null=True) enabled = models.BooleanField(default=True) class ActionOperation(models.Model): name = models.CharField(max_length=64) step = models.SmallIntegerField(verbose_name="第n次告警",default=1) action_type_choices = ( ('email','Email'), ('sms','SMS'), ('script','RunScript'), ) action_type = models.CharField(verbose_name="动作类型",choices=action_type_choices,default='email',max_length=64) notifiers= models.ManyToManyField('UserProfile',verbose_name="通知对象",blank=True) _msg_format = '''Host({hostname},{ip}) service({service_name}) has issue,msg:{msg}''' msg_format = models.TextField(verbose_name="消息格式",default=_msg_format) class Maintenance(models.Model): '''预维护计划 ''' name = models.CharField(max_length=64,unique=True) hosts = models.ManyToManyField('Host',blank=True) host_groups = models.ManyToManyField('HostGroup',blank=True) content = models.TextField(verbose_name="维护内容") start_time = models.DateTimeField() end_time = models.DateTimeField() class UserProfile(models.Model): user = models.OneToOneField(User) name = models.CharField(max_length=32) phone = models.BigIntegerField(blank=True,null=True) weixin = models.CharField(max_length=64,blank=True,null=True) email = models.EmailField(blank=True,null=True)
五、项目实战
5.1:监控客户端开发
功能:
1.监控客户端定时向server端更新监控配置信息;
2.server端响应client{插件名称:汇报间隔},客户端 定期执行该插件向server端汇报监控数据(开始监控);
5.2:接收监控数据汇报API开发
功能
1.server端接收到监控数据之后 加上时间戳,存入redis,并进行数据优化存储;(优化:计算N个点的平均值、max、min、中位数,合并N个点为1个点)
2.存储优化数据之后,定时 遍历实时数据和触发器正在做对比,判断是否需要触发报警?触发报警通过redis队列发布一条消息到报警中心;
5.3:报警中心开发
功能
1.报警信息汇报之后,信息优化存储、对比触发器阈值,触发报警;(每次客户端主动汇报监控信息时,被动触发。)
2.主动监测客户端是否按时向server端汇报数据?报警信息合并、收敛;(主动触发报警)
3.根据不同报警通知到相关责任人
5.4: 图表展示优化后的监控数据
参考: