全部博文(403)
分类: 系统运维
2011-05-24 09:01:52
nagios流程分析
在大部分环境中,nagios是不需要优化的,一来监控这个东西大家都不觉得很重要,二来nagios本身已经是个很轻量级的软件,架构比较合理,三来现在的机器配置都很恐怖,一台普通的pc机撑起上千台host,上三四千service的监控那是小菜一碟,实际环境中有这么大量监控需求的地方本来就不多,就算有这么大量监控需求的公司,用nagios的并不多数吧!
但是某些情况下,还是偶尔碰到需要优化的情况。我需要监控的机器数量就超过了1000台,而且用的nagios,用了被动检查的架构以后,撑起这么多的监控本来是没有问题,但是上周的时候,做nagios和ndotuils 的集成就出现了性能瓶颈,凌晨5点左右,把ndotuils架到两台nagios上之后,应用启动什么的都正常,但是到了上午8点就发现了问题,看到检查结果的last_check时间从7:20到8:20不等,而且是均匀分布,没有办法,只好把ndomod关掉,到了上午10点左右,就恢复正常了。
既然nagios出现了瓶颈,就不妨拿源码来看一下,配置文件的选项优化,在nagios的文档里说了很多,但是还是感觉不是很直观,分析源码,只是属于个人兴趣,这里把流程跟大家分享一下,至于优化方案,还是需要大家多多指点了。
一、nagios在启动以后,载入成daemon,整个的步骤如下:
1、读入配置文件(read_main_config_file)
2、初始化event_broker
3、载入所有broker_mod(ndomod包括在这里面)
4、读入object信息(包括service,host,servicegroup,hostgroup,contact,contactgroup等等)
5、告诉broker,我启动了
6、初始化daemon(例行工作,fork进程,修改根目录,设置信号量等等)
7、打开cmd文件(nagios.cmd)
8、初始化status数据(status.dat)
9、读取保存数据(retention.dat)
10、读取注释数据
11、读取downtime数据
12、读取性能数据
13、初始化event_timing循环
14、初始化check_stats
15、生成status.dat(空的,不写数据)
16、传输event_loop_start信息到broker(ndo:获取scheduling_info中数据)
17、开始event_execution_loop,检查数据,直到捕获重启或者关闭信号
如果接到了重启或者关闭的信号,则继续往下执行
18、通知broker_mod,我要关闭了,或者我要重启了
19、保存retention文件
20、清理性能数据
21、清理downtime数据
22、清理注释数据
23、如果是关闭信号,清理status.dat
24、如果是关闭信号,删除cmd文件
步骤比较简单,其中比较重要的有两个,一个是13,初始化循环,另一个就是17,nagios在作为daemon运行的过程中,就是在不断的执行这个循环。
二、初始化循环
nagios是个基于事件的监控系统,所有的操作都是由事件触发器触发的。
nagios启动的时候,会启动一个init_timing_loop函数,来进行事件循环的初始化。
init_timing_loop主要流程如下:
1、建立scheduling_info表,表的内容有:
总service数(在时间表外的也算)
已经在表中的service数
总host数(时间表外的也算)
在列表中的host数
单个host平均service数(总service数/总host数)
列表中单个host的service数(列表中service数/列表中host数)
平均service检查时间间隔(service检查总间隔/列表中service数)
service检查总间隔
平均service检查延时
host检查总间隔
平均host检查延时
2、计算最优service检查间隔
人工设定(配置文件中service_inter_check_delay_method选项)
智能计算(平均service检查间隔/列表中总service数) ps:呵呵,很傻的办法
3、将service检测插入event队列
4、计算最优host检查间隔(同2)
5、将host检测插入event队列
6、插入misc事件
a、重新排列列表(auto_reschedule_checks)
b、收集检测结果(check_result_reaper_frequency)
c、检查孤儿service和host(配置文件选项:check_for_orphaned_services,check_for_orphaned_hosts)
d、检查service新鲜度(针对被动检测,配置文件选项:check_service_freshness)
e、检查host新鲜度(针对被动检测,配置文件选项:check_host_freshness)
f、回收检查结果到status文件(status_update_interval,配置文件中频率设置选项为:status_update_interval)
g、检查cmd文件(command_check_interval,配置文件中检查频率设置选项为:command_check_interval)
h、日志滚动事件
i、检查结果保存(retention_update_interval,配置文件中保存频率设置选项为:retention_update_interval)
nagios先将初始化的检查插入misc事件链表中,等待nagios完全启动后,主循环对它进行处理。
三、主循环(event_execution_loop)
nagios在进入守护状态以后,会一直运行一个循环event_execution_loop,nagios所有的操作全部在这个循环中得到实现。
循环会不断检查两个event队列,一个是高优先级,包括nagios的除了检查之外的所有任务,另外一个是低优先级,包括host和service的检测。循环会先检测高优先级的event队列,然后一个一个执行完毕,最后再判断下host和service的检测是不是有必要,然后再对其进行检测。在执行event队列的时候,用的函数都是一样的,名字是handle_timed_event,当每个handle_timed_event执行完以后返回,然后再执行下一个事件任务。
handle_timed_event函数的开始是个case语句,对事件进行分类处理,具体event_type如下:
1、event_service_check(检查service)
2、event_host_check(检查host)
3、event_command_check(检查cmd文件,被动监控,cgi发送的命令都会送到cmd文件中)
4、event_log_rotation(日志滚动)
5、event_program_shutdown(nagios关闭)
6、event_program_restart(nagios重启)
7、event_check_reaper(检查结果回收)
8、event_orphan_check(检查孤儿host和service)
9、event_retention_save(保存检查结果到retention.dat,关闭nagios不删除此文件)
10、event_status_save(保存检查结果到status.dat,关闭nagios会删除)
11、event_scheduled_downtime
12、event_sfreshness_check(检查service新鲜度?)
13、event_hfreshness_check(检查host新鲜度?)
14、event_expire_downtime
15、event_reschedule_checks(重新编排event列表,与上文说的初始化循环类似)
16、event_expire_comment
17、event_user_function
对于普通的nagios来说,最常用的只有以下几个:1,2,7,10。
如果设置的是被动监控,那会频繁用到3,孤儿host和service我没玩过,所以也不清楚,把结果保存到retention.dat中,主要是为了关闭 nagios后再启动,可以读入关闭时候的状态,而用不着重新检查,而结果保存到status.dat中,主要是给cgi读取展示用的。downtime 和comment这两个东西我也没玩过。
四、主动监测(event_service_check,event_host_check)
因为主动监测的函数结构,流程都大致相同,所以这两个可以放到一起分析。
我只看了event_service_check,因为相比较起来,肯定是service的检查更复杂,host的检测只会简单。
event_service_check调用了run_scheduled_service_check函数, 然后再调用函数run_async_service_check。
检查的过程其实就是fork一个进程,执行配置好的命令进行检测,然后将检查结果返回,生成check文件,并且将文件名添加到check_result_queue中,生成check.ok文件,等待reaper回收。
如果在配置文件中开启了use_large_installation_tweaks选项,那么在检查的时候,会fork两次子进程,父进程并不会等待检查进程返回结果,只要fork的进程数量不超过配置文件中设置的最大进程数量(max_service_check_spread和 max_host_check_spread),就不会有问题。
很奇怪的是,run_async_service_check函数中没有调用 event_broker的地方,也没有将检查结果进行obsessive,这点很出乎我的意料(看到这里的时候,我才意识到我们的监控架构是不合理的,在最后我会稍微说一下,希望大家不要重蹈覆辙)。
五、结果回收(event_check_reaper)
event_check_reaper调用reap_check_results函数,读取所有检查结果,并且在循环中依次调用process_check_result_queue进行结果回收。
process_check_result_queue负责把保存在文件中的检查结果读出,并且插入check_result_list中,删除结果文件。
然后handle_async_service_check_result再对结果进行处理,这些处理包括了:
更新检查结果到service_list链表,obsessive检查结果,event_broker,报警等等
handle_async_host_check_result_3x 用来检查host状态,和service类似。
如果在reap_check_result中时间超过max_check_reaper_time,则退出循环。 max_check_reaper_time的默认值为30s,在nagios.cfg中,可以通过设置 max_check_result_reaper_time来进行设置。
!!!最终!!!
我们得到了两个链表:host_list和service_list,里面存储了我们所有host和service的最新的检查结果。
在这一步才发现了obsessive结果和event_broker,只拿event_broker来讲,我们的ndomod就是在执行 event_broker的时候被回调的,如果ndo慢了,那回收的结果肯定就慢,如果需要回收的结果很多,回收不过来,那肯定就会超过max_check_reaper_time,然后退出循环,但是因为送回来的检查结果频率是相同的,所以下一次reaper还是不会检查完的,只能导致待回收的结果越来越多,如果是一台只有主动监控的机器,那就会看到checkresult目录下的文件越来越多,甚至会发现一个小时以前的文件,但是如果是一台在被动监控集群中用来做展示和报警用的机器,检查结果的累积就只能在内存中查看了。
我上周遇到的,看到检查的 last_check事件在一个小时的时间段内平均分布,应该就是这个原因了,那时候我不停的修改配置文件,加大它写status文件的频率,但是一直不见好转,现在终于知道原因,是因为handle_timed_event函数还在check_reaper中没有出来,不会再去执行下一条 time_event。
六、结果输出(event_status_save)
event_status_save比较简单,就是将host_list和service_list中的数据同步到status.dat中,不过是全部同步,如果检查结果很多的话,像我每次要写 30M+的文件,虽然不知道具体要占用多少io,但是如果对于数据在页面上的展示没有需求的,只需要检查,处理报警(或许还需要入库)的,就没必要再来这么一步了。
event_status_save执行过程中不会对event_broker进行操作。
七、分析:
我们的监控会用几台机器专门用来进行check,然后将检查结果obsessive到另外一台机器上,由这台机器来进行报警,展示,但是现在看来,这种架构是有问题的,因为在check的时候并不会做任何提交,而是在回收结果的时候才会进行提交,而且在提交之前已经进行了报警,写checkresult文件,读checkresult文件等大量操作,到了主nagios上之后,又要重新进行一次检测报警,造成的资源浪费还是很不小的,而且检查的时间和最后报警的时间相隔比较大。
可以看出来的是,真正花时间的并不是主动监测,因为主动监测只需要fork进程就可以了。