cacti性能优化笔记(面向大规模集群的监控管理)[转]

目标:

单台Cacti服务器,同时监控1000+ Server,50000+ RRD 文件. 保证图表数据的连续和流畅,每一轮数据采集时间控制在3分钟之内。

硬件环境:

Intel(R) Xeon(R) CPU           E5420  @ 2.50GHz  4 cores

4G memory

normal sata disk

优化步骤:

1,优化数据库schema,建立合理的索引

cacti默认的cacti.sql建立的数据库模型,竟然一个Index都没有建。每次执行poller.php的时候,主要的时间,都花费在数据库查询上。使用下面的sql语句,建立一系列索引,弥补默认的cacti.sql中缺乏index的缺点。可以有效的提高poller.php执行的效率,缩短更新RRD文件所需的时间

2,使用spine替代默认的cmd.php来采集数据

wget http://www.cacti.net/downloads/spine/cacti-spine-0.8.7e.tar.gz

tar zxvf cacti-spine-0.8.7e.tar.gz

cd cacti-spine-0.8.7e

wget http://www.cacti.net/downloads/spine/patches/snmp_v3_fix.patch
wget http://www.cacti.net/downloads/spine/patches/mysql_client_reconnect.patch
wget http://www.cacti.net/downloads/spine/patches/ping_reliability.patch
patch -p1 -N < snmp_v3_fix.patch
patch -p1 -N < mysql_client_reconnect.patch
patch -p1 -N < ping_reliability.patch

./configure –prefix=cacti_install_dir

make

make install

然后编辑cacti_install_dir/etc/spine.conf

修改DB_HOST DB_DATABASE DB_USER DB_PASSWORD几个参数

最后,在cacti的setting->poller页面里,将poller type设置成spine,同时设置spine的Maximum Threads per Process, Number of PHP Script Servers, Script and Script Server Timeout Value几个参数。

通常会把Maximum Threads per Process设置成cpu * 2。在这里,我们设置成8.

3, 重构rra文件的目录结构,为每个device建立单独的rra目录

首先在crontab里禁用poller.php,然后执行cacti_install_dir/cli目录下的 structure_rra_paths.php,它会将所有的RRD文件按照device重新分配目录,并修改数据库中的RRD路径,成功执行后,再恢复poller.php的crontab就可以了。

按照上面3个步骤,710台服务器,24000个RRD文件,完成一次poller.php的时间,缩短到50 seconds。实现了最初的目的。

TODO:

在执行poller.php的时候, 监控服务器的load达到了3,通过vmstat查看,显示负载主要在I/O。在目前的情况,如果再出现瓶颈,可以考虑安装Boost插件来进一步提供性能。

cacti主要通过snmp来采集数据,可以引入collected等客户端,提供数据采集的可靠性。

原文:http://zys.8800.org/index.php/archives/391

nagios监控iostat[技术]

网上NetSeek写了一篇文章就是用nagios监控iostat的,但他使用的都是源码包编译的,具体可以参考他的这篇文章http://bbs.linuxtone.org/thread-2289-1-1.html

但编译的方式对于多机部署上来讲可能会有些不太方便,所以蚊子我在网上查了一下要想让nagios监控iostat需要的rpm包

蚊子我的环境都是centos5,所以我下载的包都是rhel5的,有需要其他环境的,我会在下面把下载地址给出,各位自己去找适合自己的。

http://packages.sw.be/perl-Nagios-Plugin/perl-Nagios-Plugin-0.33-1.el5.rf.noarch.rpm
http://dag.wieers.com/rpm/packages/perl-Class-Accessor/perl-Class-Accessor-0.31-1.el5.rf.noarch.rpm
http://dag.wieers.com/rpm/packages/perl-Config-Tiny/perl-Config-Tiny-2.12-1.el5.rf.noarch.rpm
http://dag.wieers.com/rpm/packages/perl-Math-Calc-Units/perl-Math-Calc-Units-1.06-1.el5.rf.noarch.rpm
http://dag.wieers.com/rpm/packages/perl-Params-Validate/perl-Params-Validate-0.89-1.el5.rf.i386.rpm(32位版)
http://dag.wieers.com/rpm/packages/perl-Params-Validate/perl-Params-Validate-0.89-1.el5.rf.x86_64.rpm(64位版)
http://dag.wieers.com/rpm/packages/perl-Regexp-Common/perl-Regexp-Common-2.120-1.2.el5.rf.noarch.rpm

如果需要其他环境的包可以到以下地址下载

perl-nagios-plugin                    http://packages.sw.be/perl-Nagios-Plugin/
perl-class-accessor                 http://dag.wieers.com/rpm/packages/perl-Class-Accessor/
perl-config-tiny                           http://dag.wieers.com/rpm/packages/perl-Config-Tiny/
perl-math-calc-units                 http://dag.wieers.com/rpm/packages/perl-Math-Calc-Units/
perl-params-validate                http://dag.wieers.com/rpm/packages/perl-Params-Validate/
perl-regexp-common                http://dag.wieers.com/rpm/packages/perl-Regexp-Common/

接下来出场的就是我们的主角,监控脚本check_iostat,下载地址是

http://www.ofn.dk/files/software/check_iostat

将此文件下载到/path/to/nagios/libexec下,并赋予执行权限,然后可以通过执行此脚本查看返回值

# ./check_iostat
IOSTAT OK – user 0.50 nice 0.01 sys 1.47 iowait 2.09 idle 0.00  | iowait=2.09%;; idle=0.00%;; user=0.50%;; nice=0.01%;; sys=1.47%;;

当然iostat命令是需要的,如果执行中出现以下错误

# ./check_iostat
Can’t exec “iostat”: No such file or directory at ./check_iostat line 69.
IOSTAT UNKNOWN – Error executing iostat command

请查看sysstat包是否安装

对于批量安装我的想法是,首先将check_iostat和所有rpm包打包上传到所有的服务器,这里最好的选择是rsync的方式,如果都是32位或都是64位的系统,可以直接执行rpm –ivh perl-*就行了,如果是两者都有,我这里写了一个脚本用来判断操作系统,脚本如下

#!/bin/bash
banner=$(uname -i)
cd ~/src
rpm -ivh perl-Class-Accessor-0.31-1.el5.rf.noarch.rpm
rpm -ivh perl-Config-Tiny-2.12-1.el5.rf.noarch.rpm
rpm -ivh perl-Math-Calc-Units-1.06-1.el5.rf.noarch.rpm
rpm -ivh perl-Regexp-Common-2.120-1.2.el5.rf.noarch.rpm
case $banner in
i386)
rpm -ivh perl-Params-Validate-0.89-1.el5.rf.i386.rpm
;;
x86_64)
rpm -ivh perl-Params-Validate-0.89-1.el5.rf.x86_64.rpm
;;
*)
exit 1
;;
esac

rpm -ivh perl-Nagios-Plugin-0.33-1.el5.rf.noarch.rpm

这里把所有包,包含32位和64位的同时放到src目录下。

nagios-plugin和nrpe快速部署办法[原创]

最近公司的监控服务器安装的差不多了,接下来的任务就是被监控机上的nagios-plugin和nrpe的安装了,200多台机器真要一台一台手工安装,就真的会死人了,在今天下午找到了个快速部署的办法,特把此方法与给位分享一下,高手可以忽略了。

1,先在任意一台被监控机上安装nagios-plugin和nrpe,安装目录同为/usr/local/nagios或者按各自的实际要求来定,同时这台被监控机上最好是带有mysql,这样监控mysql的脚本就会被编译进去了,这两个软件同在一个目录的一个好处就是两个软件之间可以互相使用对方libexec目录下的程序。

2,修改/etc/xinetd.d/nrpe文件,这里只需要注释掉only_from那行就行了,当然如果你的监控机只有一台,你可以考虑把那个ip改成你监控机的ip

3,修改nrpe.cfg文件,因为我希望我的每台被监控的服务器的监听ip都bind到本机的内网网卡的ip上,所以我会把#server_address=127.0.0.1这行的注释去掉。

修改
command[check_hda1]=/usr/local/nagios/libexec/check_disk -w 20% -c 10% -p /dev/hda1

command[check_disk]=/usr/local/nagios/libexec/check_disk -w 20% -c 10%

而且可以在这里把你需要监控资源都写上。

4,打包处理,接下来的任务就是把nagios目录和xinetd.d目录下的nrpe文件打到一个包中当做分发的基础包

tar zcvf nagios.tar.gz /usr/local/nagios /etc/xinetd.d/nrpe

5,将包分发,并执行配置脚本。将打好的tar包和下面的脚本分发到将要部署的服务器上,并执行脚本,这样被监控服务器就配置完毕了,当然以下脚本可以根据自己的实际应用进行调整。至于分发方法按各位习惯用的就行了,通常常使用的就是rsync,scp,cfengine统一配置等等

下面对上面的脚本做个解释

3行是创建用户
4行是将nagios包加压缩到相应的位置,nagios到/usr/local/nagios,nrpe到/etc/xinetd.d下
6行获取内网网卡的ip
7行首先将server_address的ip改成本机内网ip的地址,同时把allowed_hosts的地址在原有的基础上添加了本机内网的地址和监控机的地址
8行设置nrpe启动监听的ip地址
9行添加nrpe的服务端口
10行重启xinetd服务,也就是启动nrpe的进程

将此脚本存到其他的被监控机,并把那个nagios包也放上去,然后执行脚本,等数秒钟后使用netstat命令查看端口

# netstat -ln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address               Foreign Address             State
tcp        0      192.168.202.129:5666                0.0.0.0:*                   LISTEN
tcp        0      0 0.0.0.0:111                 0.0.0.0:*                   LISTEN
tcp        0      0 :::22                       :::*                        LISTEN
udp        0      0 0.0.0.0:111                 0.0.0.0:*

首先在本机测试nrpe,执行下面的命令

[root@linux02 tmp]# /usr/local/nagios/libexec/check_nrpe -H 192.168.202.129
返回如下结果
NRPE v2.12

再来从监控机上执行命令查看被监控机的nrpe

[root@linux01 ~]# /usr/local/nagios/libexec/check_nrpe -H 192.168.202.129
返回结果如下
NRPE v2.12

由此可以看到,这样的部署方便快捷,到此,快速部署nagios-plugin和nrpe就写完了,当然以上部署还很基础,很多监控脚本都没有集成进去,还需要很多的完善,但此篇文章仅仅是给大家一个引子。

另外。如果有些人不喜欢使用xinetd来启动nrpe的话,可以讲上面的脚本稍微的改改,将

/usr/local/nagios/bin/nrpe -c /usr/local/nagios/etc/nrpe.cfg –d

这条命令添加到rc.local里,这样也可以启动nrpe

nagios平台从apache迁移到nginx

为了学习nginx,又苦于公司生产环境没有打算要换到nginx上,于是就想到拿自己搭建的监控环境开刀了,一路配置下来确实还是遇到不少麻烦,不过还好最终还都是圆满解决掉了,下面就把我这次从apache迁移到nginx的过程整理下来

原来的监控环境是apache+cacti+nagios,按照网上相关的文档,配置起来那真是傻瓜之所及也,我也不太想赘述了,网上google一下后大把大把的。

新的监控环境nginx+cacti+nagios,其实就是把apache换成了nginx而已,cacti和nagios的安装方法我也不多说了,自己去搜好了,就把转换过程和需要注意的地方写下来。

nginx安装我不赘述,首先是cacti相关的配置,详细如下

location /cacti/ {
         alias /data/wwwroot/web/cacti/;
         index index.php index.html index.htm;
         }

location ~ \.php$ {
         root           /data/wwwroot/web/;
         fastcgi_pass   127.0.0.1:9000;
         fastcgi_index  index.php;
         fastcgi_param  SCRIPT_FILENAME  $document_root/$fastcgi_script_name;
         include        fastcgi_params;
        }

下面解释一下上面的配置,因为我的地址是http://ip/cacti/这样的形式,所以我定义了location /cacti/这个,然后把这个目录重定向到了cacti所在的目录,再看后面php的配置,首先定义了root地址是/data/wwwroot/web/这个就是后面$document_root内容,我的访问php的页面是http://ip/cacti/plugins/monitor/monitor.php,在这个url里cacti/plugins/monitor/monitor.php这部分是$fastcgi_script_name的值,因为涉及到了php页面,所以就会到$document_root即/data/wwwroot/web/下去找,这也就是为什么root定义的时候我不是直接的定义到了/data/wwwroot/web/cacti下,之前我就是在这里出的错误,结果就是始终找不到所需要的文件。

因为nginx本身是不支持cgi的,所以需要使用一个perl脚本来进行转换,脚本如下

#!/usr/bin/perl
use FCGI;
use Socket;
use FCGI::ProcManager;
sub shutdown { FCGI::CloseSocket($socket); exit; }
sub restart  { FCGI::CloseSocket($socket); &main; }
use sigtrap ‘handler’, \&shutdown, ‘normal-signals’;
use sigtrap ‘handler’, \&restart,  ‘HUP’;
require ‘syscall.ph’;
use POSIX qw(setsid);

END()   { }
BEGIN() { }
{
 no warnings;
*CORE::GLOBAL::exit = sub { die "fakeexit\nrc=" . shift() . "\n"; };
};
eval q{exit};
if ($@) {
 exit unless $@ =~ /^fakeexit/;
}
&main;

sub daemonize() {
 chdir ‘/’ or die "Can’t chdir to /: $!";
 defined( my $pid = fork ) or die "Can’t fork: $!";
 exit if $pid;
 setsid() or die "Can’t start a new session: $!";
 umask 0;
}

sub main {

 $proc_manager = FCGI::ProcManager->new( {n_processes => 5} );
 $socket = FCGI::OpenSocket( "/usr/local/nginx/logs/cgi.sock", 10 )
 ; #use UNIX sockets – user running this script must have w access to the ‘nginx’ folder!!
 $request =
 FCGI::Request( \*STDIN, \*STDOUT, \*STDERR, \%req_params, $socket,
 &FCGI::FAIL_ACCEPT_ON_INTR );
 $proc_manager->pm_manage();
 if ($request) { request_loop() }
 FCGI::CloseSocket($socket);
}

sub request_loop {
 while ( $request->Accept() >= 0 ) {
 $proc_manager->pm_pre_dispatch();

 #processing any STDIN input from WebServer (for CGI-POST actions)
 $stdin_passthrough = ”;
 { no warnings; $req_len = 0 + $req_params{‘CONTENT_LENGTH’}; };
 if ( ( $req_params{‘REQUEST_METHOD’} eq ‘POST’ ) && ( $req_len != 0 ) )
 {
 my $bytes_read = 0;
 while ( $bytes_read < $req_len ) {
 my $data = ”;
 my $bytes = read( STDIN, $data, ( $req_len – $bytes_read ) );
 last if ( $bytes == 0 || !defined($bytes) );
 $stdin_passthrough .= $data;
 $bytes_read += $bytes;
 }
 }

 #running the cgi app
 if (
 ( -x $req_params{SCRIPT_FILENAME} ) &&    #can I execute this?
 ( -s $req_params{SCRIPT_FILENAME} ) &&    #Is this file empty?
 ( -r $req_params{SCRIPT_FILENAME} )       #can I read this file?
 )
 {
 pipe( CHILD_RD,   PARENT_WR );
 pipe( PARENT_ERR, CHILD_ERR );
 my $pid = open( CHILD_O, "-|" );
 unless ( defined($pid) ) {
 print("Content-type: text/plain\r\n\r\n");
 print
"Error: CGI app returned no output – Executing $req_params{SCRIPT_FILENAME} failed !\n";
 next;
 }
 $oldfh = select(PARENT_ERR);
 $|     = 1;
 select(CHILD_O);
 $| = 1;
 select($oldfh);
 if ( $pid > 0 ) {
 close(CHILD_RD);
 close(CHILD_ERR);
 print PARENT_WR $stdin_passthrough;
 close(PARENT_WR);
 $rin = $rout = $ein = $eout = ”;
 vec( $rin, fileno(CHILD_O),    1 ) = 1;
 vec( $rin, fileno(PARENT_ERR), 1 ) = 1;
 $ein    = $rin;
 $nfound = 0;

 while ( $nfound =
 select( $rout = $rin, undef, $ein = $eout, 10 ) )
 {
 die "$!" unless $nfound != -1;
 $r1 = vec( $rout, fileno(PARENT_ERR), 1 ) == 1;
 $r2 = vec( $rout, fileno(CHILD_O),    1 ) == 1;
 $e1 = vec( $eout, fileno(PARENT_ERR), 1 ) == 1;
 $e2 = vec( $eout, fileno(CHILD_O),    1 ) == 1;

 if ($r1) {
 while ( $bytes = read( PARENT_ERR, $errbytes, 4096 ) ) {
 print STDERR $errbytes;
 }
 if ($!) {
 $err = $!;
 die $!;
 vec( $rin, fileno(PARENT_ERR), 1 ) = 0
 unless ( $err == EINTR or $err == EAGAIN );
 }
 }
 if ($r2) {
 while ( $bytes = read( CHILD_O, $s, 4096 ) ) {
 print $s;
 }
 if ( !defined($bytes) ) {
 $err = $!;
 die $!;
 vec( $rin, fileno(CHILD_O), 1 ) = 0
 unless ( $err == EINTR or $err == EAGAIN );
 }
 }
 last if ( $e1 || $e2 );
 }
 close CHILD_RD;
 close PARENT_ERR;
 waitpid( $pid, 0 );
 } else {
 foreach $key ( keys %req_params ) {
 $ENV{$key} = $req_params{$key};
 }

 # cd to the script’s local directory
 if ( $req_params{SCRIPT_FILENAME} =~ /^(.*)\/[^\/] +$/ ) {
 chdir $1;
 }
 close(PARENT_WR);

 #close(PARENT_ERR);
 close(STDIN);
 close(STDERR);

 #fcntl(CHILD_RD, F_DUPFD, 0);
 syscall( &SYS_dup2, fileno(CHILD_RD),  0 );
 syscall( &SYS_dup2, fileno(CHILD_ERR), 2 );

 #open(STDIN, "<&CHILD_RD");
 exec( $req_params{SCRIPT_FILENAME} );
 die("exec failed");
 }
 } else {
 print("Content-type: text/plain\r\n\r\n");
 print
"Error: No such CGI app – $req_params{SCRIPT_FILENAME} may not exist or is not executable by this process.\n";
 }
 }
}

把上面这个脚本存放在/usr/local/bin下或者你习惯放置的位置,起名cgiwrap-fcgi.pl,当然你也可以使用其他的名字。当执行这个pl脚本的时候会在/usr/local/nginx/logs/生成一个cgi.sock文件,这个生成的文件要对nginx运行用户有访问的权限,因为我nginx用daemon这个用户跑的,所以我设置这个文件对daemon用户有访问的权限。

这个脚本设置好了之后再来看下nginx里的配置

location ~\.cgi$ {
         rewrite ^/nagios/cgi-bin/(.*)\.cgi /$1.cgi break;
         fastcgi_pass unix:/usr/local/nginx/logs/cgi.sock;
         fastcgi_param  SCRIPT_FILENAME  /usr/local/nagios/sbin/$fastcgi_script_name;
         include        fastcgi_params; 
         fastcgi_index  index.cgi;
         auth_basic      "nagios";
        auth_basic_user_file    /usr/local/nagios/etc/htpasswd.users; 
         }

location /nagios/ { 
         alias /usr/local/nagios/share/; 
         index index.html; 
         auth_basic      "nagios"; 
        auth_basic_user_file    /usr/local/nagios/etc/htpasswd.users; 
         }

location /pub/images/ {
                        alias /usr/local/nagios/share/docs/images/; 
         auth_basic      "nagios";
            auth_basic_user_file    /usr/local/nagios/etc/htpasswd.users; 
         }

接下来就是需要生成nagios执行cgi的用户和设置密码

/usr/local/apache2/bin/htpasswd -c /usr/local/nginx/conf/htpasswd.users nagiosadmin

接下来修改/usr/local/nagios/etc/cgi.cfg文件,在其中找到#default_user_name=guest这行,将其改为default_user_name=nagiosadmin,到此迁移工作就已经完成了,下面来启动相应的程序来测试吧,再次我写了一个nginx的启动脚本,内容如下

#!/bin/sh

spswfile=/usr/local/php529/bin/spawn-fcgi
phpcgi=/usr/local/php529/bin/php-cgi
nginxfile=/usr/local/nginx/sbin/nginx
cgifile=/usr/local/bin/cgiwrap-fcgi.pl

isrun() {
    if
        (ps auxf|grep nginx|grep -v grep >/dev/null 2>&1) && (ps auxf|grep php-cgi|grep -v grep >/dev/null 2>&1)
    then
        return 0
    else
        return 1
    fi
}

nginx_start() {
    if isrun
    then
        echo "nginx already start"
        exit 0
    else
        killall -9 php-cgi >/dev/null 2>&1
        killall -9 nginx >/dev/null 2>&1
                killall -9 perl >/dev/null 2>&1
        $spswfile -a 127.0.0.1 -p 9000 -f $phpcgi >/dev/null 2>&1
                /usr/bin/perl $cgifile >/dev/null 2>&1 &
        $nginxfile >/dev/null 2>&1
                sleep 10
                /bin/chown daemon /usr/local/nginx/logs/cgi.sock
    fi
}

nginx_stop() {
        killall -9 php-cgi >/dev/null 2>&1
        killall -9 nginx >/dev/null 2>&1
                killall -9 perl >/dev/null 2>&1
        rm -rf /usr/local/nginx/logs/nginx.pid
}

case $1 in
    start)      nginx_start;;
    stop)       nginx_stop;;
    restart)    nginx_stop
                nginx_start
                ;;
    *)          echo "useage: {start|stop}";;
esac

这样只需要执行这个脚本就可以启动fastcgi和cgi及nginx了,接下来就可以进行真正的测试了

以上文章参考了:

http://wiki.nginx.org/NginxSimpleCGI
http://www.lazysa.com/2009/05/392.html

另外有一点需要注意的就是cgi.cfg的修改,网上很多文章都没有提到,就是我上面写的那个是有默认用户的,如果那个不做修改的话,访问nagios的主机和服务的时候都显示如下,更改之后就没有问题了

无权查看任何主机的信息…
请检查HTTP服务器关于该CGI的访问权限设置。