2.3 调度
相信各位读者已经对插件的概念以及工作机制有了一定的了解,现在我会阐述Nagios插件调度的过程。Nagios的核心是一个精巧的调度程序,并有大量用户定义选项,允许用户调整任务调度的方式。如果读者需要在自己的环境中配置Nagios并使用,那么理解调度程序的工作机制将非常必要。
2.3.1 检测间隔及状态
Nagios所有的内部进程包括主机检测和服务检测,它们都位于一个全局事件队列中。检测事件的调度是用户预先定义的,但不像cron或Windows计划任务那样使用绝对日期/时间的方式。对于给定的服务而言,严格调度日期和时间是不现实的,因为Nagios无法控制特定插件的执行时间,实际上也无法预测到。但是,读者可以告诉Nagios在插件退出后,等待多久后才再次执行。两个选项组合就定义出了时间间隔。
间隔长度(interval_length)定义了以秒为单位的时间区间,正常检测间隔 (check_interval)定义了等待间隔长度的倍数。因为间隔长度的默认值为60秒,所以读者可以将正常检测间隔作为检测间等待的分钟数。
如图2-2中所述,Nagios将事件插入事件队列进行调度,在执行时会加上时间戳。当Nagios执行某个插件后,它会等待插件返回结果,然后在最近调度运行时间上加上check_interval的时间,从而决定插件下次的运行时间。需要强调的是,Nagios总是使用插件的本次调度时间来计算下次运行时间,因为有时Nagios需要重新调度检测,原因一般分为两种情况。
图2-2 事件调度
第一种情况,如果由于Nagios繁忙,无法在某个预定时间点执行检测,那么插件的调度就会被“错过”。当错过调度时,Nagios使用首次调度时间来计算下次执行时间,这比采用插件的实际运行时间更加合理。如果调度延迟非常严重,而且当前时间已经超过了正常检测间隔,那么Nagios将会重新调度插件。
第二种情况,无论是因为网络延迟还是利用率过高,有时插件的运行时间比期望时间更长。在这种情况下,插件执行时间超出正常检测间隔,Nagios将会重新调度插件。
如果读者认为既然有术语“正常检测间隔”的存在,就意味着存在异常检测间隔,没错!当服务检测返回代码不是0(正常)时,Nagios使用不同的间隔来重新调度检测。这个名为“重试间隔”(retry_interval)是在对服务进行再次检测时使用的。其实,读者们可以配置Nagios在通知联系人前,进行多次服务的重试,以绝对确保服务故障或者确保服务故障持续了指定的一段时间。
“最大检测尝试次数”(max_check_attempts)选项用于定义Nagios重试服务的次数。之前的“正常”检测事件作为尝试的第一次,所以当最大检测尝试次数设置为1时,Nagios就不会对服务进行重试。图2-3表示的事件时间轴对应的“最大检测尝试次数”为3。
图2-3 故障时的事件调度
读者们可以使用下述公式计算,从服务首次检测故障到Nagios决定发送通知的时间长度(单位为分钟):
((重试检测间隔*间隔长度)*最大检测尝试次数)/60
上述公式用文字表述就是:重试的次数乘以重试等待的分钟数。Nagios在进行重试时,会先将服务状态设置为“待定状态”(Soft State)。当证明服务故障时,Nagios会将其设置为“确定状态”(Hard State)。待定状态有两种类型:待定错误状态和待定恢复状态,前者是在服务或主机的状态由正常变成非正常状态时使用,而后者在服务或主机从非正常状态恢复时使用。待定状态在进行自动化的修复步骤时能够有效地提供一个缓冲时间,因为待定状态也会被记录在日志中,它们可以用来检测服务状态趋势:即正常或故障(或者振荡),而无须进行不必要的告警。在Nagios中,自动化修复通过事件句柄(Event Handler)的方式完成。事件句柄所对应的命令能够在主机或服务的状态发生变化时执行。通常,从实际情况来看,命令语法是由用户定义的,所以用户可以根据需求定义任何命令。Nagios中还有全局事件句柄,它会在程序范围内的所有状态变化时执行,和事件句柄一样,读者可以以逐个主机或服务为基础进行定义。除了故障修复,用户们常常使用这些挂钩,以记录自定义日志或通知其他监控系统发生的变化。
2.3.2 分散负载
我对Nagios服务调度的介绍存在一个问题。因为调度是基于服务检测的最近执行时间决定的,所以调度算法整体是依赖于服务开始的时间。如果Nagios在同一时间开始对所有服务的检测,并且所有服务都有相同的正常检测间隔,那么调度执行的时间也会完全相同。如果在大规模环境中发生了这种情况,Nagios很快就会不可用,为了避免监控服务器及其客户端的负载过大,在时间约束的范围内,Nagios尝试尽可能地通过远程主机将负载分散。Nagios通过智能调度算法的组合来达到这一目的,比如服务交错(Service Interleaving)以及检测间延迟(Inter-Check Delay)。
当Nagios首次运行时,通常有一份很长的主机和服务列表。Nagios的任务就是尽快确定列表上所有元素的状态,从理论上讲,它应该按照列表逐条检测,直到列表中的元素都检测完成,然后从头开始。但这种算法是未经优化的,因为按照列表从头到尾进行检测,将使个别远程主机产生很大的负载。如果服务器在列表的顶端,配置了18个服务,Nagios会立即需要所有18个服务的状态。而实际是,Nagios使用了一个交错因子(Interleave Factor),如图2-4所示。
如果交错因子为3,第一轮检测时,Naiogs将会每隔2个项目检测一次,直到列表尾部:如1、4、7,依此类推。在第二轮检测时,将会从列表的第2个项目开始:2、5、8,依此类推。通过这种方式,检测的负载不会集中在某一台服务器上,而是分散在多台不同的服务器中,同时获取整体网络状态的时间也不会比从头到尾的方式多。交错因子是由用户定义的,但默认情况下Nagios使用下述公式自动计算,而其基本上能够自适应与所有情况,可以说是最佳选择:
交错因子=(服务总数/主机总数)
但是降低远程主机的负载对于Nagios服务器而言没什么作用,它依旧需要在启动时处理大量检测的发送和接收。为了减轻启动后一段时间内负载过高的问题,并确保对于诸如日志轮换(Log Rotation)等重要任务的循环能够有效执行,Nagios在每次检测之间插入了一小段时间,以避免并发执行。这段时间称为检测间延迟(inter-check),可由用户定义,但是如果设置过高,可能会导致错过调度计划。如果检测间隔延迟设置为自动(smart,即默认值),那么Nagios会使用下述公式自动计算检测间隔延迟:
检测间隔延迟=(所有服务的平均检测间隔/服务总数)
图2-4 当交错因子为3的时候,Nagios检测服务时总是会每隔2个检测一次
各位读者应当意识到,有很多选项能够影响计划程序,特别是在启动阶段。比如,最大检测尝试次数,用于限制Nagios首次获得所有主机状态的时间。很明显,如果设置这个选项,将会对如上所示的检测间隔延迟的公式产生影响。为了理清这些设置信息,读者们在执行Nagios程序文件时加上“-s”参数,可以显示基于当前配置的调度信息。在Nagios XI中,同样内置了很棒的调度程序状态的可视化信息,这些信息能够更方便地帮助用户诊断调度问题。
2.3.3 信息采集和并发执行
在队列中处理的事件可以分为两类:能够并发执行的和不能并发执行的。Nagios中的服务检测是可以并发执行的,所以当队列中发现服务检测事件时,进程将会分叉(forked),Nagios继续处理队列中下一个事件。分叉处的服务检测进程将会执行插件,并将其错误代码(即插件的退出代码)和输出提交给消息队列,直到一个称为“信息采集”(Reaper)事件发生并收集这些信息。
信息采集事件相当于Nagios的心跳(Heartbeat)。它们在事件队列中出现的频率也是可由用户定义的,是非常重要的一个选项。无论插件能够多快完成其工作并报告其状态,只要服务信息采集事件没有发生,也没有在消息队列中发现这些信息,Nagios就不会对插件进行处理。
Nagios允许并发执行的服务事件数量是根据最大并发检测(Max Concurrent Check)选项设置的。这个变量也很重要,如果设置过高,监控服务器的资源会被服务检测完全耗尽。另一方面,如果设置过低,所调度的服务检测就会错过。Nagios文档对如何优化并发检测变量提供了一些入门信息,基于执行插件的平均时间,地址 是:
http://nagios.sourceforge.net/docs/3_0/checkscheduling.html#max_concurrent_checks
Nagios在Web界面中提供了一些工具,用来量化无法按期调度的检测数量。其实,一般情况下用户没必要关心这个问题。某些事件必然会错过它们的首次调度窗口,因为这些事件被某些状态为待定错误的服务的重试检测抢占,或由于某些检测执行的时间太长,再或者因为很多其他的原因。所以当读者看到某些调度错过的问题出现或重复出现时,不用太担心。我认为,Nagios的调度程序因为其任务的混沌(不确定)性质,只会做正确的事。