庖丁解牛(一):監控系統

好朋友「雪糕」是前Baidu的高工,當年我們一起參與構建了一個龐大的運維自動化系統Noah。轉載一些他的關於監控系統的感悟,我也深有同感。

我們在後來也用Python寫了個簡易版:51reboot/rebootMon-4 · GitHub

最近借著出去分享的機會,畫了張簡化的監控系統架構圖:

寫在前面

我從事運維自動化相關的工作,也已經8年了。當初剛開始做的時候,運維開發(devops)這詞還不火。很少人知道。國內對運維的理解,也就是機房、伺服器、苦逼的7*24小時值班。甚至當時還流傳著段子,招運維要人高馬大,扛得動伺服器。

很幸運的主導了兩個一線互聯網公司的公司級的運維自動化。這倆公司的伺服器,都是幾十萬台級的,IDC都是幾十個。很多事情都是摸索著來。一路走來也形成了一些對運維自動化的理解。第一家公司好容易做的七七八八的時候,到第二家實施發現原有的很多理解都被打爛了重新來。但這種經歷反而凝練了一些經驗。我個人性格原因,不喜歡到外面去講。偶爾實在沒法推辭過去的,也誠惶誠恐的準備,到最後發現很多乾貨還是沒辦法一言以蔽之。最後留在外面的都是一些隻言片語,不成系統,更不敢說能指導多少。

終於下定決心要把我個人的一些理解,通過系列文章來寫一寫。文筆有限,可能寫的不盡人意。也歡迎大家加入運維開發討論交流群來交流,群號 365534424

廢話少說,直接上文。

我始終認為監控對於運維來說,猶如眼睛對人來說一樣重要。不管說的多麼高大上,運維工作裡面很大一部分還是響應型的工作。來自於架構調整、服務變更或者來自於監控。說監控是整個運維或者服務生命周期裡面最重要的一環都不為過。從事前發現,預警或者故障報警,到事後提供監控現場供回溯追查,監控系統貫穿了運維整個環節。怎麼才能有一副好視力,今天我們就來稍微談談。

正因為監控系統如此重要和通用,所以業內最成熟、最多的產品也是監控系統。商用的、開源的監控系統比比皆是。也有一些很優秀的開源系統應用很廣泛。比如Zabbix、cacti、nagios、ganglia等。對於使用開源系統,還是自己開發(相信看這個文章的,應該都不會有購買監控系統的打算),是使用者自己決定的。業務和團隊規模都不足夠的時候,直接拿來主義,開源的系統能解決基本問題,性價比高。業務如果後期發展的好,規模快速擴大,複雜度迅速增加的情況下,開源系統就難以為繼了。表現在時效性、擴展性、二次開發、支持的服務規模(或者叫系統容量)、良好的許可權控制等各方面。

從上面幾點來看,一個監控系統需要具備的是數據採集、擴展性、告警管理、高可用、歷史數據存儲與展示、許可權管理等幾個方面。

監控本質上就是對被監控對象的狀態進行判定。這個監控對象可以是伺服器、交換機,也可以是一個幾千台伺服器的集群,還可以是帶寬、CPU利用率,甚至深入到服務內部,監控服務內部的進程、線程、cache命中率等。

監控對象多種多樣,既有實體的,也有虛擬的。 被監控對象的狀態進行判定,這句話裡面有三個要素。被監控對象、狀態、判定。所以監控系統要能夠適配足夠多的監控對象類型、收集並能轉換為可衡量的狀態值,才能支持下一步的判定動作。例如,一台伺服器上的nginx服務的連接數。伺服器上的nginx服務,就是被監控的對象;連接數就是被監控對象的指標,那麼狀態呢?我們可以定義為,超過1萬就是不正常,否則是正常。

從這個角度做框,我們來看看監控系統的核心指標都有哪些。首先是能監控的對象範圍要越多越好(當然你可以說小而精的也挺美。但維護多套監控系統也是代價)。也就是數據採集,能採集的渠道、支持的方式、採集的指標越多越好。

關於擴展性的定義

可伸縮性(可擴展性)是一種對軟體系統計算處理能力的設計指標,高可伸縮性代表一種彈性,在系統擴展成長過程中,軟體能夠保證旺盛的生命力,通過很少的改動甚至只是硬體設備的添置,就能實現整個系統處理能力的線性增長,實現高吞吐量和低延遲高性能。

   可伸縮性和純粹性能調優有本質區別, 可伸縮性是高性能、低成本和可維護性等諸多因素的綜合考量和平衡,可伸縮性講究平滑線性的性能提升,更側重於系統的水平伸縮,通過廉價的伺服器實現分散式 計算;而普通性能優化只是單台機器的性能指標優化。他們共同點都是根據應用系統特點在吞吐量和延遲之間進行一個側重選擇,當然水平伸縮分區後會帶來CAP定理約束。

可擴展與過度設計的矛盾

具體討論到監控系統的可擴展性,我們這裡特指系統可以隨著被監控對象的規模擴大而無需對架構做大的變更修改。一千台伺服器的時候,是這個架構,一萬台伺服器的時候還是這個架構,最好十萬台的時候只需要增加伺服器就可以,架構還是那個架構。聽起來很棒吧。這也是每個系統架構設計者的夢想。但現實照進理想的時候,發現理想很殘酷。首先是對於設計者來說,當他在一家只有幾百台伺服器規模的公司時,很難去想到自己的系統可能有一天會在跑在幾萬台的伺服器規模上。這裡面也有一個架構設計裡面的原則,就是要盡量避免過度設計。如果一個幾百台伺服器規模的公司的運維開發,對他的老闆說要做一個系統可以支撐幾萬台伺服器,但因此要多花了多少時間去架構和重構,我想老闆會認為自己的運維開發一定是瘋了。架構原則之一也是要盡量避免過度設計。

但軟體設計依然還是推崇良好的架構、良好的可擴展性。否則架構設計的價值就會打很大的折扣,代碼復用和系統實現成本會隨著規模的擴大而線性增長。良好的架構可以通過迭代得出之後,反過來指導低階的系統設計。但低階的無法預測高階的。這就是架構的用處之一。所以這裡稍後我會介紹一下我對於監控系統架構的一些經驗和心得。

一個稱職的設計者,是可以站在幾百台伺服器的規模時,考慮到幾千台的情況的。但他考慮幾萬台的話就有點過度了。這裡並非說能支撐幾萬台的系統架構不優秀。只是如果他不知道,那也沒必要過度考慮。如果能提前知道,甄嬛就會說,那顯然是極好的。

但很顯然需要考慮的內容會越來越多。幾十台的時候你可能只需要考慮一個機房了,幾百台的時候會有2、3個機房,當幾千台的時候可能依然在10個IDC以內,但當幾萬台的時候很可能已經超過15個IDC了。而且地理位置的分布會更廣泛,由此而帶來的運營商的覆蓋、網路的複雜度、業務的複雜度也會完全不同。非逼著一個運維開發去完全臆想著來做是不現實的。他沒有經歷過實際的這種需求場景,是沒有辦法考慮到這樣那樣的各種問題的。所以我完全理解一些大公司對於開源項目的態度。好一點的可能拿來改改用,進一步可能單獨拉一個分支開始改,更甚的就改的完全和主幹不一樣了 ,其實還有就是自己造輪子的。但有時候就是這樣,自己不造輪子,開源的輪子用著的確不好使。

監控的可擴展性

具體到監控系統,可擴展性體現在哪些方面呢?我們從頭捋一下。監控系統的輸入是監控到的各項監控數據。這些數據經過一系列的處理,最終存儲下來用於事後分析和離線分析,同時更主要的作用是要實時的報警。整個這個過程我們可以視為是一個流式計算的過程。說到流式計算其實大家想到的是storm這些。這倒是另外一個我曾經想過的思路,就是把所有處理過程放到strom上去。balabalabala.... 說遠了。但我們仔細去看,strom也好,流式計算平台也罷,都是分散式的。分散式架構的一個特性就是良好的擴展性。隨著伺服器規模的擴大,對於中間的數據處理層的可擴展性要求,就是計算能力要能具備擴展性。簡單來說就是數據多了,通過加伺服器或者升級伺服器就能搞定。

還有幾個邊界的地方需要把擴展性支持好。第一個就是入口。或者叫做數據的接收口。外面的數據源源不斷的進入,如果要想做到擴展性良好,第一個需要考慮的就是接收環節。數據可以走TCP、UDP、SNMP、HTTP等多種協議進入到監控系統。考慮到數萬伺服器的規模,這個地方比較考驗技術底子。如果走SNMP、HTTP當然可以,但這兩個協議都走在應用層,必然會帶來額外的開銷。拿HTTP舉例子,我們拿Nginx或者apache做server,其實天然帶有可擴展性。數據收到以後,存到一個存儲即可(不管這個存儲是緩存還是永久存儲)。這個過程,不帶有狀態,所以天然具有可擴展性。一個Nginx實例扛不住了,再來一個,再來一個,再來十個。這樣就解決了介面的可擴展問題。

另外一個可擴展是存儲環節。這個存儲主要是監控數據的持久化存儲。前面我們說,數據接收、計算環節都可以通過一些方式支持可擴展。那存儲必然會成為一個瓶頸。這個在很多系統裡面都是這樣,前端可以通過Web Server實現可擴展,但最終大家都跑到一個資料庫上讀寫。哪怕是讀寫分離的,還是一個主庫。主庫壓力山大。

這個地方我推薦用一些分散式存儲來解決這個問題。但不是很推薦mango這種比較奇葩的。因為寫入的能力不是很好。雖然它後來又有一些改進方案來緩解這個問題,但注意,只是緩解。

綜上,對於可擴展性,我們的思路是:分散式、無狀態。

我借用一個高可用性的定義: 高可用性H.A.(High Availability)指的是通過盡量縮短因日常維護操作(計劃)和突發的系統崩潰(非計劃)所導致的停機時間,以提高系統和應用的可用性。它與被認為是不間斷操作的容錯技術有所不同。HA系統是目前企業防止核心計算機系統因故障停機的最有效手段。

那麼高可用,就是高可用性良好的系統。多少算高呢,我以前呆過的BAT某公司喜歡用小數點以後幾個9來衡量。當然小數點前面默認就是99。大家普遍認為4個9是還不錯的,5個9是核心業務應該具備的。怎麼計算呢,這個我後面也可以說一說演算法。

對於運維工程師來說,要是能運維一個可用性非常高的系統,我想是一件幸事。系統能高可用,運維處理報警的優先順序就不用那麼高了。大冬天晚上收個報警,也不用立刻從被窩裡面爬起來連VPN處理了。可用性越高,運維工程師睡覺越安心。

影響高可用的因素與計算方式

影響可用性的因素都有哪些?讓我們來捋捋。一個服務分為軟體、硬體。拿一個網站來解解。

假設有個網站,域名是Reboot教育 - 高效你的學習。部署了一個nginx,部署在一台伺服器上,這台伺服器在一個叫做zw的電信機房。

用戶從瀏覽器輸入Reboot教育 - 高效你的學習開始,直到他在瀏覽器上能打開這個網頁內容為止,有哪些步驟?

第一步,域名解析

第二步,向著伺服器發起HTTP請求

第三步,請求走網路,到達伺服器所在機房的交換機

第四步,數據走幾層交換機之後,到達伺服器網卡

第五步,網卡數據經過OS,到達Nginx

第六步,Nginx收到HTTP請求

第七步,Nginx調用ThinkPHP框架(這裡假定是這個框架)

第八步,PHP連接Mysql資料庫獲取數據

第九步,PHP處理數據

第十步,Nginx返回數據到用戶端

第十一步,用戶端瀏覽器完整接收數據之後,渲染完畢

粗略的分,11個步驟。這裡涉及:DNS、伺服器、交換機、OS、Nginx、ThinkPHP、PHP、Mysql

伺服器又可以分為:磁碟,以及其它部件如CPU、內存。之所以這樣分,是因為磁碟作為存儲部件是最容易壞的一個部件。

好了,我們接下來算一算,51reboot這個網站的可用性是多少。一次可用,相當於上面的十一個步驟都得正常才叫可用。那麼就要考慮了,DNS的可用性是多少,伺服器不宕機(可用性)是多少、Nginx之類的軟體的可用性是多少、Mysql、磁碟的可用性是多少、網路的可用性是多少等等。這些可用性的乘積,就是該網站的可用性。

這裡還沒有考慮更多實際情況。比如,mysql如果和web端不在同一台機器上、甚至不在同一個機房,或者部署的nginx實例不止1個,等等。

上面我們講了可用性怎麼計算。下面我們來看看監控系統的高可用。

先定性,再定量

這是我的原則,事情都是先定性,看是否有必要,再定量,看需要定到多少,具體量化。監控系統本身是監控別的服務和系統是否正常運行的。如果監控系統自身可用性不足,會嚴重影響監控效果。甚至可以說就是沒有什麼用處,有大隱患。

這裡還涉及了另外一個問題,就是監控系統自身的監控。以後會開篇幅來講。

前面說了,監控系統本身必須高可用。那下面我們就看這個高可用需要怎麼量化。讀者覺得應該達到小數點之後幾個9?我個人覺得至少2個9.也就是99.99%。否則業務部門的兄弟們急了,因為他們的系統如果要求是99.999%,沒人能證明啊。監控系統本身才是99.99%。

如何達到高可用99.99%

這是今天這篇要討論的另外一個核心問題了。要達到高可用性,還要看系統的架構。按照有沒有單點來區分,系統有兩類,一類是有單點的架構,一類是沒有單點的架構。講到這裡我們有必要說一下什麼叫做單點。單點簡單來說就是系統裡面的某個部位,它在系統裡面部署的時候,是一個唯一存在。這個唯一存在不能擴展性的部署一個兄弟實例出來。這種唯一存在將來就最有可能是系統裡面的老大難,因為它出了問題,沒有兄弟能頂上。

但,我又要說但是了。沒有單點,不代表說完全百分之百的沒有問題。例如,我們採用Hash的辦法,負載均衡的方式,部署了2個Nginx實例。當其中一個實例掛掉的時候,僅影響了50%的請求。不能算整個系統掛掉了。但這個系統的穩定性依然堪憂,我們不能說它的可用性有多高。 如果Hash計算能夠結合了實例的健康狀態,不健康的自動從hash計算的池子裡面摘掉,那可用性就大大提升了。

綜上,以及綜合我們上一篇,監控系統要想達到高可用,必須要採用去中心化的架構來做。就是讓整個系統裡面沒有任何一個環節是單點。因為單點就意味著瓶頸,意味著可用性提升很難很複雜,不容易做高。

具體說說,怎麼才能去單點。我們從監控系統本身的數據流來分析。

數據採集,這個要分類來說。一個是帶內的,跑在OS上的代理Agent。這個的高可用,是另外一個領域的事情,就是怎麼寫一個高可用的、魯棒性非常好的客戶端。我們以後分開篇幅說。

另外是帶外的。比如,HTTP或者埠監控,或者存活監控。我們拿存活監控來舉例吧。比如說,我們通過ping的方式,來監控伺服器是否存活。那麼我們需要一個批量發ping包的探測器。這個也是一個數據採集端。只是沒有在OS上來採集。當然它也存在魯棒性的問題,但這是另外一個領域的事情,我們這一篇不談。這個客戶端,如果它掛了,可想而知,被它監控的伺服器都失去監控了。所以我們要提高Ping監控模塊或者叫環節的可用性。一個最簡單辦法,我們用兩個監控點來監控同一批伺服器。但新的問題又來了,倆監控點監控同一台伺服器,什麼情況下可以斷定這個伺服器掛了呢?這個是另外一個監控數據合併的問題,我們也放到以後的篇章裡面去討論。 另外一個提高可用性的辦法,就是ping監控點部署兩個,但兩個之間不要同時生效,但兩個節點之間有心跳,一個掛了,另外一個接管,也是一個辦法,但切換略複雜。

數據採集回來了,要做處理。這個處理或者叫計算環節的高可用,有不少現成的辦法。第一,部署兩個計算實例,但兩個實例需要能互備或者同時發揮作用。第二,純分散式辦法。

存儲和Web的分散式方案就更多了。

還有一個最關鍵的地方,如何實現無狀態。只有無狀態了,才能達到簡單的部署切換,就可以支撐高可用。這個問題我們留待之後來講。

架構這個詞太大了,這裡我們縮小一下,只來談談宏觀的監控系統整體架構。在這個範圍裡面,web由於負責統一的系統管理和操作功能,縮減為一個模塊。

最簡單的架構如下圖

這是監控系統第一層的架構。比照百度地圖的話,我們可以認為這個是全國地圖。最粗粒度的幾個模塊就是這三個。web、數據採集、數據處理。

PUSH PULL

我們先來關注數據採集模塊到數據處理和報警模塊的這個環節

推和拉,技術選型裡面常常遇到的一個選擇題。 在Client/server結構中,信息獲取方式是按「拉」(Pull)的模型進行的:伺服器根據用戶終端發送的服務請求進行處理並返回用戶所需 的結果。在Push模型中,伺服器把信息「推」給Client。雖然兩者數據傳輸的方向都是從伺服器流向Client,但操作的發起者是不同的。從「信源」與「用戶」的關係來看,信息的流動可分為兩種模式,即信息推送與信息拉取模式。

兩種模型的對比見表格

其中PUSH的好處是及時性好。但缺點是服務端要有比較複雜的狀態管理。同時在到達率等方面都會有一些糾結的地方。而PULL的好處則是服務端簡單,狀態管理簡單,但缺點是時效性上不可控。體現在監控系統上,如果所有要監控的監控項,都是需要Server端PUSH給Client,假設Client所在伺服器關機了,那PUSH的時候就是不可達的。Server端就得想辦法記錄下來,並且再做重試等失敗處理。而如果是Client端主動來PULL就好辦了,伺服器開機啟動之後,Client立刻來拉取。到達率肯定要好,對Server的管理也簡化了。但缺點就是想生效一個監控項,只能等著Client來PULl,而無法立即生效。

這裡還有一個比較經典的例子,也是我面試別人的時候總喜歡問的一個問題。當然我問面試者的時候主要是想去看看TA的邏輯思維能力。

題目:微博大家都用過。裡面你可以關注一個人,也可以被人關注。當你發一條微博時,關注你的人都會收到一條提示。當你關注的人發一條微博時,你會收到一條提示。 請問這個提示,是PUSH 還是 PULL到你的微博客戶端(瀏覽器或者手機微博)上的?

面試者:肯定會有人說,PUSH唄。

面試官:OK,然後我就會問了,姚晨在新浪微博上的粉絲數是5000多萬,她發一條微博,是不是得PUSH 5000多萬個消息到各個賬號去?

面試者:額,那就是定時PULL

面試官:確定嗎?幾千萬個客戶端都PULL?

面試者:額。。。 面試者開始額頭黑線了。

面試官:請問該怎麼辦?

PUSH的話,姚晨的一條微博,在系統裡面就要產生5000萬條消息要處理。如果她一天發個100條,估計新浪微博瘋了。這還沒有考慮很多客戶端不登陸,消息就得緩存著。還有很多客戶端一下子通知不到,還得處理失敗。

PULL的話,如果大量用戶在使用的生產系統,對存儲和緩存是一個很大的挑戰。

具體的,大家可以再去google一下,這個事情其實有很多方案。

經驗比較豐富的研發一定會同意我的一個說法:兩個爭論不休的技術方案,最終能達成一個融合了二者的第三個方案。就好像兩個特別對立的談判方,到最後談判結果是一個融合或者叫妥協的方案。PUSH和PULL也可以二者融合,將做到取長補短,使二者優勢互補。根據推、拉結合順序及結合方式的差異,又分以下四種不同推拉模式:

  • 先推後拉——先由服務端PUSH,再由Client端有針對性地拉;

  • 先拉後推——根據Client端PULL的信息,服務端進一步主動PUSH與之相關的信息;

  • 推中有拉——在數據推送過程中,允許Client隨時中斷並PULL更有針對性的信息;

  • 拉中有推——根據Client端PULL的過程,Server主動推送相關的最新信息

幾個開源監控系統的PUSH PULL選擇

zabbix : 帶agent方式。agent主動推送數據到服務端。 從client的角度看,是PUSH數據到Server

Cacti : SNMP協議,無Client,或者說Client是SNMP Client. 從Client角度看,是PULL

ganglia: 從Client角度看,是PUSH

在我過去生產環境所構造的監控系統裡面,我們採用了PUSH 和PULL結合的方式來達到及時性、到達率的同時解決。我們站在Client的角度來描述這個解決方案。對於監控項的生效,web端變更之後立即使用PUSH的方式來通知Client。但這裡一定有達到率的問題。比如Client所在伺服器死機了、重啟了、當時網路有問題不可達了等等。所以我們在Client端,支持定時PULL。定時去主動聯繫Server端,獲取自己應該生效的監控內容。

HASH

怎麼突然又說到HASH了呢。 HASH先來個概念普及吧。看完概念還是不了解的同學,自行面壁去,你計算機數據結構一定沒好好學。

我說HASH是因為要為後面介紹高可用性架構有關係的。

HASH你別直接拿去搜,用百度的結果就是哈士奇。

關鍵詞可以是哈希。

Hash,一般翻譯做「散列」,也有直接音譯為「哈希」的,就是把任意長度的輸入(又叫做預映射, pre-image),通過散列演算法,變換成固定長度的輸出,該輸出就是散列值。這種轉換是一種壓縮映射,也就是,散列值的空間通常遠小於輸入的空間,不同的輸入可能會散列成相同的輸出,所以不可能從散列值來唯一的確定輸入值。簡單的說就是一種將任意長度的消息壓縮到某一固定長度的消息摘要的函數。

Hash在演算法裡面是很基礎但使用非常廣泛的。特別是在大數據量的情況下。

我這裡強調Hash,是想說它的一個作用之一就是散列。把輸入散列到幾個地方去。提到Hash不得不提一個詞叫做一致性Hash,這個演算法對於解決緩存命中率有很大好處。在內存緩存、CDN等存儲系統中經常使用。

Hash的精髓之一就是按照某種計算規則,把輸入散列到不同的輸出通道上去。

無狀態和有狀態

我們拿無狀態協議來體驗一下無狀態是個什麼概念。

協議的狀態是指下一次傳輸可以「記住」這次傳輸信息的能力。典型的如HTTP協議是不會為了下一次連接而維護這次連接所傳輸的信息,

由於Web伺服器要面對很多瀏覽器的並發訪問,為了提高Web伺服器對並發訪問的處理能力,在設計HTTP協議時規定Web伺服器發送HTTP應答報文和文檔時,不保存發出請求的Web瀏覽器進程的任何狀態信息。這有可能出現一個瀏覽器在短短几秒之內兩次訪問同一對象 時,伺服器進程不會因為已經給它發過應答報文而不接受第二期服務請求。由於Web伺服器不保存發送請求的Web瀏覽器進程的任何信息,因此HTTP協議屬 於無狀態協議(Stateless Protocol)。

監控系統裡面的HASH和狀態

監控系統對數據的處理,主要是過濾異常數據出來並報警。比如某個伺服器的CPU利用率超過了95%,需要報警。但這個時候突然數據處理模塊所在伺服器宕機了。那麼這個異常數據很有可能就丟掉了。

監控系統常見的報警條件是: CPU利用率超過95%,算一次異常。如果5分鐘內有3次異常,報警給運維。

這裡就有幾個數字需要處理,5分鐘,3次。前面提到的宕機,會導致一次異常數據丟掉了。假設5分鐘內出現了3次,丟掉了一次,那自然不會報警出來。這就是一個有狀態的場景。

有狀態的情況下,做自動切換或者負載均衡,需要把狀態也帶過去才行。

比較典型的還有session的問題。如果web是多台主機負載均衡的時候,session存本地是會出問題的。因為用戶有可能通過負載均衡的調度,多次請求落在不同的主機上。 本來HTTP協議是無狀態的,支持負載均衡的調度。但因為session這個有狀態的產物,必須要把session放在公共存儲上才行。

結合前面提到的那個架構圖。數據進入到了數據計算和報警模塊。我們如何保證這個數據計算和報警模塊是個高可用的架構。

答案是,把輸入的監控數據Hash到不同的數據計算和報警模塊實例上去,並且最好是無狀態或者弱狀態的計算過程。

本文原載於: Reboot運維開發 。作者書面授權發表,拒絕任何形式的轉載。

歡迎大家加入運維開發討論交流群來交流,群號 365534424,本文僅授權51reboot、51cto、知乎auxten專欄 上發布。


推薦閱讀:

docker並不是萬能的
linux 軟體安裝用編譯還是用rpm?
伺服器購置,電費,帶寬費之間什麼比例?
運維工程師需要具備哪些性格特質?

TAG:服务端 | 运维 | Linux |