由于工作上的需要,最近被分派到公司的系统运维部混了一段时间,也就是在这期间让我遇到了Nagios——一款开源的系统监控软件。不过在我接触系统运维工作之前我还真的不知道Nagios这个名字,也不知道它是何方神圣,作为一名资深的码农竟然不知有如此神器,还真是羞愧不已!不过羞愧管羞愧,日子还是照样要过,拿人钱财与人消灾,既然被派到系统运维部,怎么说也要找点事情做做。于是乎就想编写几个监控插件,能让领导在监控大屏上看看实时监控图表,那也算得上是一个亮点。关于Nagios的强大功能这里就不多介绍了,相关的安装和配置也不是本文的关注重点,我们主要讲解如何写Nagios的监控插件。
Nagios本身并没有什么特别的功能,所有的监控功能均来自各种插件,Nagios只是一个平台并制定了一套统一的标准,插件只要按照标准开发就能相互协作并形成生产力,这点上与Maven倒有点类似。本次我们开发的是一个MySQL数据库的监控插件,该插件能监控MySQL的当前连接数和长时间执行的SQL语句数并形成统计图形。首先介绍一下我们的开发环境:
操作系统:Redhat Linux AS 5.8 64位 数据库:操作系统自带的MySQL服务器 Nagios:3.5版本,使用被动监控模式 图形展现:pnp4nagios插件 插件开发语言:Linux C 编译器:Linux下的GCC 4.1.2 外部函数库:MySQL官方提供的mysqlclient库
关于mysqlclient函数库其实在Linux的安装光盘上就能找到,没必要一定要去下载,以下是我机器上安装的RPM包:
图上红色框中的就是RPM安装包,它们可以在你的Linux安装光盘中找到。
接下来我们来介绍一下Nagios的数据格式,下面是一个例子:
OK: MySQL alive: Threads_connected=1 | 'Threads_connected'=1;25;30;
Nagios的数据格式分成2部分,中间用管道符分割,前半部分会被Nagios显示在监控前台页面上,后半部分则用于图形的展现。我们主要关注后半部分,我们可以看到用分号间隔的3个数字,1表示当前值,即MySQL当前的连接数,25表示图形上的警告线,在图形上会用一条黄色线表示,30表示图形上的危险线,在图形上会用红色线表示。我们先来看看图形的展示效果,如下图所示(有些网上下载的插件并没有提供后半部分的数据,我们称之为prefdata,故而在显示图形时会报找不到xxxx.xml文件的错误信息,这时你就应该看看你使用的插件是否支持prefdata的输出):
接着我们来看一下数据的前半部分是如何在Nagios前台页面上被展示的,如下图:
红色框中的就是被展现的数据前半部分。
当我们了解了Nagios采集的数据格式之后,就可以着手编写监控插件了。其实我们的MySQL监控插件非常的简单,首先通过mysqlclient库提供的函数连接上指定的MySQL服务器,然后执行“show status like '%xxxx%'”语句,该SQL语句用于查询MySQL服务器当前的状态信息,其中XXXX就是要查询的状态属性变量名称。例如,我们上例中的Threads_Connected变量名,用于查询MySQL服务器当前的连接数。当然还有其他很多的状态变量名,可以通过查询MySQL的使用手册获取更多的信息。最后将查询结果按照Nagios规定的数据格式输出就可以了。另外插件程序需要返回3个整形数告诉Nagios的最终状态,在接下去的内容中会加以说明。
终于到了展示代码的时候了,由于我们的插件程序非常的简单,所以我只提供了头文件的展示,实现代码可以下载附件查看。头文件定义如下:
#ifndef MYSQL_HEALTH_CHECK_H_ #define MYSQL_HEALTH_CHECK_H_ //nagios return status value #define OK 0 #define WARNING 1 #define CRITICAL 2 #define UNKNOWN 3 #ifdef WINNT #include <winsock2.h> #endif #include "mysql.h" /** * define data struct to mysql status values */ typedef struct { int success; char *variable_name; int variable_value; } MysqlStatus; /** * define data struct to connection mysql parameters */ typedef struct { char *host_name; char *user_name; char *password; char *db_name; } MysqlParameter; /** * print this plugin help message */ void usage(); /** * check mysql status then return nagios prefdata format string */ int check_mysql_health(const MysqlStatus *, int, int); /** * query mysql current status, return NULL is occur errors */ MysqlStatus query_mysql_status(const MysqlParameter, char *); #endif /* MYSQL_HEALTH_CHECK_H_ */
我定义了2个结构体,MysqlStatus用于存储查询结果,MysqlParameter用于存放数据库连接参数数据(主机IP、用户名、密码等信息)。还定义了4个常量宏,用于向Nagios返回检查的结果,它们分别是:
OK - 0:检查状态为正常时返回;
WARNING - 1:检查状态为警告时返回;
CRITICAL - 2:检查状态为危险时返回;
UNKNOWN - 3:检查状态为未知时返回;
Nagios会根据插件程序的返回值进行不同状态的显示,如发生警告时会显示黄色,发生危险时会显示红色。除此之外还定义了3个函数,usage函数用于用户提供-h参数时显示插件的使用方法。作为惯例,每个插件都应该实现帮助方法,并提供-h参数选项,告知用户你插件的调用方法和参数含义。query_mysql_status函数会根据传提的参数进行数据库连接和执行“show status”语句,并将查询状态结果赋值给MysqlStatus结构体并返回。check_mysql_health函数根据query_mysql_status函数返回的结构体并与用户提供的-w和-c参数进行比较,如果大于-w参数值,且小于-c参数值,则该函数返回1(WARNING),如果大于-c参数值,则该函数返回2(CRITICAL),否则返回0(OK),如果该函数在执行过程中发生错误,则返回3(UNKNOWN) 。
好了,大致的程序实现算法就是这些,的确非常的简单。最后,我们要写一个Makefile文件,用于在linux环境中编译并生成可执行文件,Makefile文件的内容如下:
all: check_mysql_health # Which compiler CC = gcc #Options for release CFLAGS = -O2 -Wall -c -fmessage-length=0 check_mysql_health: main.o check_mysql_health.o $(CC) -L/usr/lib/mysql -L/usr/lib64/mysql -o check_mysql_health main.o check_mysql_health.o -lmysqlclient check_mysql_health.o: check_mysql_health.c check_mysql_health.h $(CC) -I/usr/include/mysql $(CFLAGS) check_mysql_health.c main.o: main.c check_mysql_health.h $(CC) -I/usr/include/mysql $(CFLAGS) main.c clean: -rm -Rf main.o check_mysql_health.o check_mysql_health
将所有文件上传到linux的某个目录中,然后在该目录中执行make命令,如果连接库时发生错误,则需要检查系统中是否安装了mysqlclient库。如果编译完成,则会在当前目录下生成check_mysql_health文件。使用chmod +x命令给该文件添加执行属性,最后输入如下命令进行测试:
./check_mysql_health -h
该命令会调用usage函数,显示插件的调用方法和参数说明:
从上图中我们可以看到,该插件一共有8个参数选项,各个参数的含义可以查看说明。下面是一个实际的使用例子:
./check_mysql_health -H xxx.xxx.xxx.xxx -u root -p password -d test -w 30 -c 40
最后将该插件复制到Nagios的libexe目录中,这样就可以像Nagios自带的插件一样使用它了。
结束语:
Nagios就像一个舞台,让形形色色的演员尽情的发挥自己的表演才能。Nagios的插件除了可以用C/C++编写外,还可以用shell和perl,当然Java也可以,不过我对于perl编写的插件就不那么喜欢,因为要让perl插件run起来着实是一件不太容易的事情,你必须为安装各种依赖的perl模块而被折磨的筋疲力竭。最后附上完整的插件源代码,以供各位同学参考学习和交流。