本人進入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>
好吧,今天先到這裏了。