本人进入IT行业已接近5个年头,刚入行的时候带着仅有网络基础和系统基础,在校外培训过CCNA和CCNP,还有RHCE,顺利的进入了一家规模还算大的企业,由于是零经验,所学的知识也只是基于理论完全没有工作经验,因此是从基础做起,刚开始的职位是运维监控工程师,当时这职位有5个人,一个组长,4个人需要轮流3班倒的值班。那段日子大概经过了1年时间,算是自己继续学习获取工作经验的踏脚石。在那家公司里,接触和学习到了普遍的IT公司都会使用的开源软件,包括了nagios,cacti,rrdtool(画图工具),smokeping。这些软件我都使用过,并且对配置都比较熟悉。其中rrdtool是用于画图的,需要额外的编辑脚本语言来调用,当时公司的系统组大牛就是用perl来写了很多针对业务的数据监控的,那时候我连perl都没敢去学,仅仅粗略的学了shell,在shell里调用过rrdtool api来做一些采集数据的展现(画图),当时仅仅是限于生成静态的png图片,然后通过静态的html把图片输出到浏览器中。

nagios监控执行效率非常高,记得当时监控服务器上部署的监控主机达到了400多台,监控项目达到了6000多个,主要是以监控主机资源和网络为主,画图的任务交给了cacti,当时cacti也是由我来维护,每当在nagios上新加了主机的监控点,我都要人手的在cacti上添加主机的监控项,包括ping,磁盘,cpu,内存,网络接口流量等等,如此的操作非常的不人性化和低效率,可是当时限于技术的有限和工具的限制性,自己也没办法开发出一套完善的监控工具出来,可能有人会说,zabbix可以监控也可以画图啊,是的,当时的技术总监也有打算把监控系统移植到zabbix上,我也试过去搭zabbix的服务器,搭起来容易,但是配置起来也是非常的麻烦(鼠标操作)。

后来我离开公司后听以前同事说,那总监不久也离职了,监控平台还是nagios,不过请了几个开发,重新写了一个自定义的前端模块,还搞起了分布式,几个机房的监控数据都集中输入到数据库里然后在一个前端来展示,我也观摩过他们的产品,人家的监控系统已经做成产品化了,还外包了几个公司的业务来做,那个牛啊,前端写的非常高大上,javascript,jquery这些是肯定用上了,配置文件也做到了只需在浏览器点几下鼠标就能拷贝出同一个模板的监控点出来,后端是mysql来放数据,听说是php来驱动的。唉,看着人家的高大上的NB监控系统,我真是自叹不如啊。

到现在为止,我还在使用的只剩下nagios了。原因很简单,第二家公司没有太多的服务器而且对监控的要求不高,只要有报警出来处理了就OK了。现在的公司在来的时候已经搭起来了nagios了,而且服务器也不少,如果再搭个cacti的话估计人手操作的劳动力得花上不少。而且我发现nagios监控远端的机器资源的获取方式居然还在用nrpe,比如获取磁盘,cpu这些,天啊,snmp不是个好工具嘛,配置又简单,而且不用特意跑到对面主机上来配个nrpe,于是我重新把监控方式大部分都转成了使用snmp,其中花掉不少时间在配置的修改上,而且配置好之后也得向经理汇报一下这么做的好处,因为他是开发出身,可能对snmp这些协议不太了解。

后来经理接着也提出了需求,想把监控的历史数据图形化。我第一时间想到了cacti,可以后来放弃了,原因不多说了,我非常害怕重复的人手操作和后期维护。后来在网上看到有人把pnp4nagios跟nagios整合了,天啊,这就是我想要的产品啊。经过一轮的摸索,千辛万苦,终于把这套系统搭起来了。

主要对nagios改造的地方是把监控的性能数据输出到指定的后台perl脚本处理,在nagios的输出里用管道符号”|“来分隔监控数据和性能数据。现在的系统里几乎所有的监控项都会有图形的输出了,包括通用的ping,磁盘,网络流量,cpu,内存,还有web服务器的apache,lighttp,nginx,数据库服务器的mysql的性能监控等等,部分的监控项需要自己编写脚本来实现。其中还包括自定义画图模板的编写也需要研究一段时间才能摸索出来,主要是里面的php脚本需要修改,比如想把几个分离的数据集中在一张图上画出来,这可没有现成的模板的。

nagios跟pnp4nagios搭配起来后,长期运行了大概距现在也一年时间了,性能非常好,速度也很快,对我们平常的维护工作提供了很有用的数据参考。我们公司是做游戏发行的,基本上一个游戏会分配几台机器,一般两台,压力大的会有4台以上,主要是用于负载均衡和数据库主从。由于所有游戏都是有个生命周期的,因此服务器的压力也是有趋势的。刚开始推广的时候可能线上玩家很多,从监控的数据上看服务器负载一直在升,我们就知道在最优化系统的情况下已无法满足现在流量的需求,就会提出申请新的服务器。同样,当一个游戏从高峰慢慢走下坡的时候,我们也能从监控的数据趋势发现可能减少部分机器也不会影响现在游戏的性能的时候,从节约成本的角度出发,就会考虑减少部分机器,比如减少前端的web服务器数量等等。

这些数据也只能从监控服务器上发掘到,而且nagios不会直接告诉你机器够不够用,需要分析过去和现在的数据结合来得出正确的结论,这里pnp4nagios确实帮我们解决了一个大难题。一般的创业型公司或者非政府性的公司很少会花钱去买一套完善的监控系统的,毕竟他们资源有限,有钱也会花在刀刃上,所以这种开源的软件便成为了不二之选,当然需要你折腾一段长时间才能把它搞懂。

最近几天,经理向我抱怨,在nagios里点击每个监控项的在浏览器里打开几个的分页,特别是在分析一个游戏的服务器集群的性能的时候,比如看cpu,有5台服务器,就得点5次打开5个分页的来回去看,对比各个服务器的性能差别,效率非常的低下,能不能用鼠标点击一个按钮打开一个页面就能看到对应的游戏主机组的所有性能数据呢个?(要知道,我经理是开发出身的,在他眼里,什么东西都做成自动化就最好不过,点一个按钮就能看到所有信息)。当时我想,这个需求能够实现的情况有两种,一种就是网上找类似的nagios web前端,估计网上是一大把的,但是估计又得花很长时间来搜索和研究,而且我不太想动nagios的前端的东西,毕竟那是对自己提高不大,只是把别人写好的东西自己配置一下拿来使用,当然我也不赞成有现成的轮子还非得自己去造。第二种便是自己写个简单的页面来管理数据。我便抱着学习和研究的心态开始了一轮的反复的测试和研究。由于我之前学习了php和javascript,虽然还是处于初级水平,但还是信心满满的觉得自己能够研究出来的。我常常勉励自己的一句话就是别人能做到的我为什么不行?行不行自己试过了才知道。

着手研究的第一点便是去看pnp4nagios的web结构,结果有惊人的发现。我用firefox打开了pnp4nagios的页面,主要是看他的图片是怎么输出来的,结果便发现他已经有现成的api能够直接传递参数就能输出相应的图片。这代表我只需要构建好一个基础的主机组和主机的映射,还有基本的监控参数,便能把余下的工作交给pnp4nagios了 。

具体的画图api是pnp4nagios/image?host=is4&srv=CPU_Usage_Status&view=3&source=0,意味着我只需要传递主机名,监控项目,view代表的是时间段,在pnp4nagios的配置文件里能找到。这个api我是从aNag这个安卓版的nagios客户端里领悟出来的,因为这客户端里就有一个查看pnp4nagios图片的功能,也就是输出了一张图片给对应的监控项,当时我就想是否会有现成的api来做成这个,结果真的发现了。

这下可省去了不少功夫了,起码画图的底层已经有现成的了。

接下来就是要构建一个主机组和主机的对应关系的array了。我们在nagios里的配置是分了组的,根据游戏来划分,比如三国游戏,会有sanguo这个组(hostgroup),下面会有w1.sanguo,w2.sanguo,db1.sanguo,db2.sanguo,四台主机,但是nagios不会自动把这个配置输出给你,因为你在配置文件里写好之后,只会生成nagios自己认识的数据格式,要知道,nagios是C语言写出来的,所以他生成的文件就是为了让自身语言能够读懂。C语言我压根一点也不会,只会写点php和shell。

决定用php了,shell的功能限制太多,实现同样的功能要写的代码多得很多,php已经集成了很多好用的function,所以它是必选的了。

经过读了几个nagios生成的文件,找到了这个objects.cache文件,默认就在/var/nagios/下,里面定义的内容是经过格式化和整理过的text文字,其中就发现了我需要的信息,里面的信息很多,只摘了部分出来:

define hostgroup {
        hostgroup_name restaurant
        alias   restaurant 
        members w1.restaurant
        }
          
define hostgroup {
        hostgroup_name admin
        alias   admin Server
        members backup,dev1,admin.bulk,db2.bulk,db1.bulk,bulk4,bulk3,bulk2,bulk1,admin,external,sl7
        }

define hostgroup { 
        hostgroup_name  bulk-db
        alias   bulk database
        members db2.bulk,db1.bulk
        }


其中这里面已经有我需要足够的信息了,我最终要得到的数据格式应该像这样子,restaurant=array(0=>w1.restaurant),admin=>array(....)

好吧,一开始我锁定了php的preg_match_all的这个函数,测试了很久发现怎么不是自己想的那样呢?经过不断的翻阅资料(此处省去一万字。。)原来发现自己漏掉了很重要的一个参数,最后还是实现了,具体代码如下:


<?php
define('NAGIOS_OBJECTS_CACHE','/var/nagios/objects.cache');
preg_match_all('/^define hostgroup {\n.*\n.*\n.*\n.*}$/im',file_get_contents(NAGIOS_OBJECTS_CACHE),$match);
##这里的m参数非常重要,表示匹配多行,默认只会匹配单行的字符串,就是这里我测试了一个早上才
##弄明白了不是函数不行是我对函数的功能不够熟悉,所以参数是非常重要的
foreach ($match[0] as $group_member)
        {
                preg_match('/hostgroup_name.*/im',$group_member,$match1);
                $hostgroup_array1=explode("\t",$match1[0]);
                $group=$hostgroup_array1[1];
                preg_match('/members.*/im',$group_member,$match2);
                $hostgroup_array2=explode("\t",$match2[0]);
                $members=explode(",",$hostgroup_array2[1]);
                foreach ($members as $key => $value)
                        {
                                $groups[$group][]=$value;
                        }
}
$machines=$groups;
//var_dump($machines);
?>


结果已经能够得到了,辛苦了半天啊,学习到了不少东西,因此记录一下,也吹了半天的水了。


接下来的工作就是利用上面得到的array数据给个循环函数轮流传递给相应的api,画出对应的图,好吧,到这里我只贴代码记录一下好了:


主页,模仿nagios分成左右两边的页面,index.php:

<html>
<head>
<style>
a.host:link,a:visited {
text-decoration:none;
}
a.host:hover {
text-decoration:underline;
}

a.host:active    {  }
</style>
</head>
<frameset cols="150,*">
   <frame name="list" src="list.php">
   <frame name="main" src="">
</frameset>
</html>
~


左边的导航栏代码,list.php:

<html>
<head>

<style>
a.host:link,a:visited {
text-decoration:none;
color:#686868;
}
a.host:hover {
text-decoration:underline;
}

a.host:active    {  }
</style>

</head>
<?php
require_once("../nagios_group.php");
$request_url="all.php";
echo<<<_HTML_
<div style="float:left;width:60px;">Group-Host</div>
<div style="clear:both"></div>
<br/>
<br/>
_HTML_;

$first = "";
foreach ( $machines as $group => &$hosts ) {
    $_url = $request_url."?group=".$group;
    echo<<<_HTML_
<div style="padding:0px 0px 20px">
<div style="font-size:16px;font-weight:bold"><a style="color:#383838" href=$_url onClick=jump('$_url') target="main">$group</a></div>
_HTML_;
    foreach ( $hosts as &$host ) {
        if ( !empty($host) ) {
            $display_host = str_replace ( ".6waves.com", "", $host );
            //$url = "/pnp4nagios/index.php/graph?host=$display_host";
            $url = $request_url."?host=".$display_host;
            echo <<<_HTML_
<a class="host" href="$url" onclick="return jump('$url');" target="main">$display_host</a><br/>
_HTML_;
            if ( empty($first) ) $first = $_url;
        }
    }
    echo <<<_HTML_
</div>
_HTML_;
}
?>

<script>
function jump(url) {
    var oldurl = parent.main.location.href;
    var page = oldurl.replace(/^.*\/(.*?\.html)$/, "$1");
    if ( page != oldurl ) {
        parent.main.location = url + page;
    } else {
        parent.main.location = url;
    }
    return false;
}
parent.main.location = "<?php echo $first; ?>";
</script>
</html>


右边的内容页,里面用到了cookie的功能,不过这里主要是想讲nagios的cache文件的解释方法,所以这里就不详细解析php知识了,all.php:

<?php
if (isset($_SERVER['PHP_AUTH_USER'])) {
    $user=$_SERVER['PHP_AUTH_USER'];
}
$expire=time()+60*60*24;

if (isset($_COOKIE[$user]))
    {
        $cookie=1;
        //echo $_COOKIE[$user];
        //var_dump($_COOKIE);
    }
//else 
//{
    if (!is_null($_REQUEST['range']))
        {
            if ($_COOKIE[$user]!==$_REQUEST['range'])
                {
                    setcookie($user,$_REQUEST['range'],$expire);
                }
        }
//}
?>
<html>
<head>
<style >
.service_title{
    color:#000;
    font-size:18px;
    text-align:center;
    font-weight:strong;
    }
img.service_graph_org {
    width:597px;
    height:212px;
}
img.service_graph_medium {
    width:447.7px;
    height:159px;
}
img.service_graph_small {
    width:298.5px;
    height:106px;
}
a.host:link,a:visited {
text-decoration:none;
}
a.host:hover {
text-decoration:underline;
}
a.host:active    {  }

</style>
</head>
<body>
<?php
//require_once("monitor/config.inc");
require_once("monitor/service.inc");
require_once("../nagios_group.php");
$now=date(time());
$_24hourago=$now-86400;
//echo $now;
$period=array ("start"=>$_24hourago,"end"=>$now);
$view=array("4 hours"=>0,"12 hours"=>1,"24 hours"=>2,"1 week"=>3,"1 month"=>4,"3 months"=>5,"1 year"=>6);
$view_default=3;

if (!is_null($_REQUEST['group'])){
    $group=$_REQUEST['group'];
    $servers=$machines[$group];
}
else {
    $group='pop';
    $servers=$machines[$group];
}
if (!is_null($_REQUEST['host'])){
    $host=$_REQUEST['host'];
}
else {
    $host='w1.pop';
}

if ($cookie==1)
    {
        if (!is_null($_REQUEST['range']))
            {
                if ($_COOKIE[$user]!==$_REQUEST['range'])
                    {
                        $range=$_REQUEST['range'];
                    }
                else $range=$_COOKIE[$user];
            }

        else $range=$_COOKIE[$user];

    }
else 
    {
        if (!is_null($_REQUEST['range']))
            {
                $range=$_REQUEST['range'];
            }
        else     {
                $range=$view_default;
            }
    }
?>


<div>
<form action=all.php method="post">
<input type=text style="display:none" value=<?php if (is_null($_REQUEST['host'])) {echo $group." name=group";}else echo $_REQUEST['host']." name=host";?>>
<label for=select>select a period:</label>
<select name="range">
<?php
    foreach ($view as $desc => $value)
        {
            if ($cookie==1)
                {
                if ($value==$range)
                    {
                        //if ($range!==$_REQUEST['range'])
                        //{
                            echo "<option value=".$range." selected>".$desc."</option>";
                        //}
                    }
                else echo "<option value=".$value.">".$desc."</option>";
                }
            else
            {
            if (is_null($_REQUEST['range']))
                {
                if ($value==$view_default)
                     {
                        echo "<option value=".$value." selected>".$desc."</option>";
                    }
                else echo "<option value=".$value.">".$desc."</option>";
                }
            else 
                {    
                if ($value==$_REQUEST['range'])
                    {
                        echo "<option value=".$_REQUEST['range']." selected>".$desc."</option>";
                    }
                else echo "<option value=".$value.">".$desc."</option>";
                }
            }    
        }
?>
</select>
<input type=submit value="submit">
</form>
</div>
<?php
if (!is_null($_REQUEST['group']))
    {
        echo "<div><h2 style=\"color:#383838;text-align:center\">".$group."</h2></div>";
        foreach ($service as $k=>$value)
        {
        
        echo "<table><tr><th colspan=11 class=\"service_title\">".$k."</th></tr><tr>";
        $total=count($servers);
        if ($total>8)
            {$img_class="service_graph_small";}
        else if ($total>4)
            {$img_class="service_graph_medium";}
            else 
            {$img_class="service_graph_org";}
        foreach ($servers as $key=>$host)
            {
                $host=preg_replace('/.company.com/','',$host);
                $url="/pnp4nagios/image?srv=".$value;
                $click_url="/pnp4nagios/graph?srv=".$value;
                $url.="&host=".$host."&view=".$range."&source=0";
                $click_url.="&host=".$host."&source=0";
                echo "<td><a href=".$click_url."><img class=".$img_class." src=".$url."></a></td>";
            }
        echo "</tr></table>";

        }
    }
else 
    {
        echo "<div><h2 style=\"color:#383838;text-align:center\">".$host."</h2></div>";
        foreach ($service as $k=>$value)
            {
        
                echo "<table><tr><th colspan=11 class=\"service_title\">".$k."</th></tr><tr>";
                $url="/pnp4nagios/image?srv=".$value;
                $click_url="/pnp4nagios/graph?srv=".$value;
                $url.="&host=".$host."&view=".$range."&source=0";
                $click_url.="&host=".$host."&source=0";
                echo "<td><a href=".$click_url."><img class=service_graph_org src=".$url."></a></td>";
                echo "</tr></table>";
            }

    }
?>
</body>
</html>

好吧,今天先到这里了。