庖丁解牛(二)丨360集群控制系統概述
如何在5s之內控制5w台伺服器執行命令
之前在360工作,主導設計了360的伺服器集群控制系統,希望能給大家一點借鑒。
什麼是控制系統
- 快速,安全的進行伺服器任務分配
。最終達到的性能指標是5s對30000台伺服器進行任務分發&執行&結果獲取
- 用要求的許可權進行執行,精確控制任務
- 嚴格的許可權樹限制
。插件審核機制
。用戶只能操作自己有許可權的樹節點
- agent控制信道加密(對稱加密演算法 with salt)
- 能在任務執行的任何時間進行 暫停、繼續、停止
。僅限於子任務(機器)粒度
- HTTP回調介面
。如果在創建任務時提供一個HTTP URL,子任務(機器粒度)的任何狀態變化都會通過這個回調進行通知
- 對任務的輸出,返回值進行收集,匯總,入庫
控制系統架構
由於我們的控制系統屬於基礎服務,一般來說我們對公司的一般業務的可用性要求是要達到99.99%。作為基礎架構類的服務必須比普通業務的可用性要高1~2個數量級,也就是:99.999%~99.9999%。
所以,一個最基本的要求就是,整套系統必須沒有任何單點。
我們整套系統的架構圖如下:
我們為了達到這一點,我們做了如下設計:
- 我們的控制系統本身沒有任何狀態,任何狀態都是保存在資料庫中。
- 我們單獨設計了一個hermes-sitter模塊, 負責健康檢查以及在健康檢查失敗的時候進行必要的觸發錯誤邏輯。
整套系統在各種情況下的故障恢復邏輯如下圖所示:
整套系統的開發是在我們詳細分析並繪製了上述圖表的情況下給出的, 所以整體的Coding時間也只用了一周,在加上我們用gtest做的較為詳盡的單元測試 由於各種錯誤檢查的邏輯在裡面,最後的單測行覆蓋率也就在70%左右, 系統上線後3年,僅修復了一處關於暫停點的bug。
控制系統的實現細節
第三方庫的應用
我們在構建控制系統的時候主要應用的第三方庫有:
- libev
。著名網路事件庫libevent的弟弟,更輕,更快
- c-ares
。非同步DNS解析庫,對DNS over TCP的支持,提高響應速度
。由於只對select模型提供了支持,我們給它加了個patch來配合libev使用
- gtest
。google的單測框架
。單測是一種很好的在軟體構建初期就可以發現潛在的bug的方法
網路模型
- 我們的網路模型是參考memcached的線程模型,對等多線程模型
。對client模型和server模型都給予很好的支持
。「對等」不會由於線程分工造成性能瓶頸,減少內存拷貝
代碼地址:https://github.com/auxten/gko_pool
內存分配優化
在項目優化的後期,我們發現內存分配是我們系統的一個性能瓶頸, 為此,我們特意構建了一個比較專用的內存池。
主要針對我們在創建連接之前所要開闢的國定大小的內存進行加速。
主要目的有如下兩點:
- 減少為每個連接分配初始的r/w buf的開銷
- 實現了一個簡單的內存池,只能分配4K塊大小的內存塊,bucket大小可以配置,stupid but works
代碼地址: https://github.com/auxten/gkoAlloc
瓶頸發現與解決
經過我們利用gpreftools的尋找性能瓶頸,我們發現DNS成為我們系統最大的瓶頸。
我們嘗試過用多線程DNS解析,但收效甚微,就像這樣:
然後,我們又很自然的想到,可以使用DNS Cache來解決。
但是,遇到不能解析正確的域名,我們還是需要走DNS查詢,還是弱爆了, 就像這樣:
我們需要一個突破性的解決方案,像這樣的:
首先,需要解釋一個問題,我們的控制系統由於一些特殊的網路ACL限制原因設計成了:
由我們的中心控制端主動發起連接去連接我們的agent
這點,也讓我們的控制系統成為了一個在普通服務端工程中比較少見的高性能"客戶端"編程模型。
一般來說,"服務端"編程模型的特徵是:監聽在一個埠上等待連接的到來
- 服務端編程模型的流程偽代碼是這樣的:
。短連接:
bind埠listen監聽等待連接while True: accept新連接 read請求數據 處理業務邏輯 write響應數據 close連接
。長連接:
bind埠listen監聽等待連接accept新連接while True: read請求數據 處理業務邏輯 write響應數據
而"客戶端"編程模型的特徵:調用connect(2)來主動發起連接:
短連接:
while True: connect發起連接 write請求數據 read響應數據 close連接
長連接:
connect發起連接while True: write請求數據 read響應數據
我們的控制系統就屬於"客戶端高並發短連接"編程的一個很好的實例。
客戶端網路編程里大家容易忽略,也是glibc給大家無意間誤導的就是:DNS查詢也是一個 請求&響應的網路操作。
通常情況下,DNS都是基於UDP,請求和響應都是各自一個UDP數據包就可以完成的。 其速度也是非常快的。但由於DNS查詢是一個遞歸的過程,如果一個DNS域名查詢的請求在第一次請求的DNS伺服器中沒有被找到,DNS伺服器就會詢問上層DNS……依次遞歸。
這樣就會導致當查詢的域名為不存在的域名的時候就格外的費時,在非同步非阻塞的main loop 中這種費時是致命性的。
所以,我們的解決方案是:
包括DNS在內,全非同步 libev + 狀態機 + c-ares
經過我們的非同步DNS改造,這個問題被完美的解決。
相關網路框架代碼參見:https://github.com/auxten/gko_pool
易用性改造
為了安全性考慮,我們沒有直接的把bash命令調用的功能直接開放給用戶。
因為,我們的控制系統是可以在5s之內給6萬台主機下發命令的, 所以,如果發生誤操作那影響也是相當大的。
我們只允許用戶調用審核過的插件進行任務執行。
為了讓我們的系統更容易的接入,我們為我們的系統包裝了一層RESTful 的API:
http://hermes:8360/TaskInterface/CreateTask.php?task_type=1234& # 插件IDtask_name=$task_name& # 任務名,非必須work_account=$work_account& # 任務執行的用戶concurrency=$concurrency& # 任務執行的並發度plugin_info=$plugin_info& # 給插件傳遞的參數time_out=$time_out& # 任務執行超時時間hostlist=$hostlist& # 任務執行的機器列表callback=urlencode($callback) # 任務執行的回調
通過這種方式,我們就允許用戶用任何的語言進行開發。
推薦閱讀:
TAG:控制系統 |