微信月活9億的高效運維之路
今天的主題主要是關于彈性伸縮、擴容和縮容這些方面。
微信業務量增長的時候,我們比較關心的是效率問題。前期可能兩三個月就漲了 1 倍的量,怎么能夠保證我們的運營效率是跟得上的?后期主要關心的是成本問題。
我們在 2014 年以后增長有點放緩,所以主要的精力會在成本這個方面。
如上圖,下面我主要分為四個方面來做分享:
- 運營規范。
- 云化管理。
- 容量管理。
- 自動調度。
運營規范
配置文件規范
先來看配置文件規范,我們前期花了比較多的精力。可能整個系統設計比較復雜,最開始都會有一些配置管理工程師專門來處理,后期我們把這塊搞得比較規范了。
配置文件規范分為下面幾項:
目錄結構標準
這就是一個服務部署上線的時候怎么定義它的目錄結構,大家應該先做好這些目錄結構標準的定義。
跨服務的相同配置項管理
為什么提這一點?可能有些配置項你會在這個服務里需要這幾個配置項,在另一個服務里也需要這幾個配置項。
如果更改這個配置項的時候,你是不是把服務 A 發一遍,服務 B 也發一遍?我們有一套機制,每臺機每個目錄下都會有一個全局共用的配置項,我們會通過一些自動化的灰度的方式來控制這里的發布,這一塊是單獨拎出來的。
同一服務內不同實例的差異配置項管理
不確定大家運營時會不會碰到類似的問題,就算你用 Docker 了你鏡像管理挺好的。
你部署個實例出來,可能你們的業務就需要在實例 1 上做一些調整,當然你也可以用腳本來管理。
但是我們覺得這是比較復雜的狀態,我們會把這些差異性全部給它統一抽取出來,盡量做到所有的環境下它的配置文件的 MD5 都是一致的。
開發/測試/現網的差異配置項管理
前期也會有這個問題,開發環境我們一般不管,測試環境也是運維來負責管理,現網當然是運維管理的。
基本上測試和現網的差異只有路由的差異,其他的配置項我們都保證它是完全一致的。
做這么一個要求,大概實現的效果是不管你通過什么手段擴容,我們直接一個鏡像打過去或者直接一個包拷過去就可以了,不會再有后續的腳本來改動它。
同一服務下同一版本的多個實例,在所有環境下配置文件的 MD5 都嚴格一致。
名字服務規范
名字服務這塊比較重要,分為三層:
- 接入層,類 LVS 實現。
- 邏輯層,類 etcd 實現。
- 存儲層,路由配置自動化。
服務伸縮是運維工程,獨立于研發的變更發布。
接入層和邏輯層,都是類似的實現,我們內部根據我們的業務特性做了一些業務開發。
存儲層跟 QQ 有點不一樣,QQ 好像是邏輯層和存儲層都是用同一個名字服務實現,我們在存儲層這里還沒有做這方面的配置。
我們現在存儲層跟邏輯層隔得比較開,在涉及到數據的方面,我們差不多可以認為是運維能夠配置的方式,但是又不完全是能夠配置,用那些輔助腳本來配置。
服務伸縮是運維工程,我們不希望在服務伸縮時還考慮別的因素,獨立于研發的變更發布。
我們有個特點,研發是通過運維提供的變更系統,他在上面基本上不需要做什么操作,整條鏈就打通了,運維不用關心變更發布,只需要管好服務伸縮就好。
數據存儲規范
數據存儲規范如下:
- 接入層,不帶數據。
- 邏輯層,帶短周期 cache、帶靜態數據、禁止動態數據落地。
- 存儲層,帶長周期 cache、基于 paxos 協議的數據落地。
接入層和邏輯層的服務伸縮,無需考慮數據遷移和 cache 命中率。
數據存儲方面,單獨拎一頁出來會覺得這個場景是比較多人中招的,比如接入層肯定不會帶什么數據的,邏輯層我希望它是不帶數據的,而且我們也嚴格要求不帶數據的。
經常出現這樣的場景,他的邏輯層要自動伸縮,跑了一段時間上線了一些邏輯在上面保存了一些數據,在下面做縮容的時候是不是就中招了,會有這個問題,所以我們會定這個規范。
邏輯層是不帶數據的,會有靜態數據,包括用戶發的消息、用戶發的朋友圈,很明顯應該放到存儲層的。
接入層不帶數據,其實歷史上我們也有一次是帶了數據的,在每次過年搶紅包的時候,所有人都在搖的那一把,那個量是非常恐怖的。
搖紅包那個點我們做設計是每個搖紅包的請求真的打到我們接入層,不會有客戶端搖五次才有一次請求上來。
我們的接入層的性能保證我們能抗住這個量,但是后面的邏輯層肯定撐不了這個量,1 秒鐘幾千萬。
這個時候我們會做一些很特殊的邏輯,在接入層直接帶上紅包數據,當然這是比較特殊的場景,我們只在過年的時候這個規范是沒有執行的。
運營規范小結
運營規范小結:
- 階段目標
服務可運維
- 落實措施
變更系統攔截
全網掃描不規范的服務
這里說了幾點,我們的目標簡單來說就是服務可運維,可運維的意思就是你做擴容縮容的時候不需要做人工的操作,如果做人工的操作就是不可運維。
為了實現服務可運維,我們在變更系統里做了一些攔截,它接下來變更可能不符合我們運營規范或者之前有不符合我們運營規范的地方,我們都會攔下來,要求變更,實現我們的運維規范。
云化管理
接下來說一下云,大家用云也挺多的,近幾年也比較火,可能大家用得比較多的是 Docker,微信這邊我們沒有用 Docker,后面也會說到為什么沒有用的原因。
為什么上云
上云的兩點原因:
微服務總數近 5k。
同物理機上多服務的資源搶占問題。
先說為什么要上云,在 2013 年、2014 年時,我們的微服務已經到差不多 5000 個。
我是近幾年才知道這個叫微服務,我們之前實現方式已經是好多年都算微服務的方式了。
最開始說微服務這個事情的時候,我記得是 QQ 那邊海量運營的一些課程都會提到,我覺得這個思路跟微服務是完全一致的。
我們運維這邊在實現了一套新服務發布的時候,基本上不會給研發有什么限制說你這個服務不要搞太多。
所以整個系統搞下來那個量是比較夸張的,當然就會出現他的多個服務要在同一臺機上部署,因為里面有 5000 個微服務,一定要同物理機部署。部署多個服務就會有資源搶占,基于這個因素,我們就上云。
哪部分上云
哪部分上云:
- 接入層,獨占物理機、 容量充足、 變更少。
- 邏輯層,混合部署、 容量不可控、變更頻繁。
- 存儲層,獨占物理機、容量可控、變更少。
哪一塊上云,剛才也說到,接入層因為要扛住比較猛的量,微信的用戶也比較多,如果它有什么問題,可能接入層會雪崩。
所以我們主要是獨占物理機,容量足,變更也少,暫時沒有上云的需求。
在邏輯層這里,之前提到 5000 多個微服務,所以比較混亂,變更比較多,容量也不可控,所以這塊上了云。
存儲層這里,也沒有上云,但是有單獨的容量管理和數據遷移。
基于 Cgroup 的云化
基于 Cgroup 的云化:
- 虛擬機型定制
VC11 = 1 個 cpu 核 + 1G 內存
VC24 = 2 個 cpu 核 + 4G 內存
- 物理機分片
我們云的方式是直接 Cgroup,Cgroup 這個名字可能有些人不知道,我們用的是內核 Cgroup 這種機制。
在現網的時候,用類似簡單的虛擬機型定制,比如 1 個 cpu+1G 內存。
前期我們也有一些流量因素的考慮,后來把它給取消掉了,我們系統架構上保證的那個流量好像是不怎么會成為問題的。
這里簡單列了一下虛擬機分片的方式,怎么隔的方式都有,我們隔的這么隨意也帶來另外一個問題,系統運轉過程中會有一些類似于磁盤碎片的東西存在,就好像你 Windows 跑了很久以后也會有磁盤碎片存在一樣。
這個我們也有一些比較特殊的方法來處理。
線上未啟用 Docker
我們沒有用 Docker 的幾點原因:
- svrkit 框架 100% 覆蓋全網,已實現標準化規范化。
- 框架本身大量依賴 IPC 交互。
- 自研非侵入式 vs Docker 侵入式。
Docker 太火了,我們差不多在 2014 年、2015 年的時候,線上也少量上線了一輪。
我們這個 svrkit 框架是我們微信內部自研的一套框架,100% 覆蓋全網。
100% 什么意思?一點開源代碼都沒有,包括前面大家可能用得最多的 Nginx 做接入,這個我們也是在自研上切換的,包括后面的存儲,前期用了 MySQL,后期也都換成自己的組件了。
現網用我們自己的框架 100% 覆蓋了,我們的標準化和規范化都是比較好的。
可以說Docker解決的一些問題在我們這里不是問題:
- 我們對 Docker 的需求不是很強烈。
- 框架本身我們用了很多 IPC 交互,這些東西在 Docker 里都做了很嚴格的規范,如果我們用 Docker,可能會把這些 Docker 里的各種機制破壞掉。如果是這種情況,還不如不用 Docker。
- 我們對 Docker 這種接入方式的實現還是有顧慮,早一兩年有人跟我討論比較多,Docker 主進程起來的時候,可能由于要更新 Docker 本身,會導致服務重啟。
好像近期已經解決這個問題,這個在早期來看我們認為是一個比較嚴重的問題,我不希望說因為 Docker 本身的變更,對線上的服務有任何影響。
私有云調度系統
基于上面的點,我們自研了一套云化管理的 Docker 系統。
上圖是我們私有云調度系統:
- 基于 svrkit 框架自研。
- 參考 borg/yarn/k8s/mesos 等主流調度系統的優點。
- 覆蓋 80% 的微服務。
基于我前面提到的 svrkit 框架+自研,調度系統當然也要用我們自己的調度系統覆蓋,目前覆蓋了 80% 的微服務,還差一點點 100% 覆蓋。
私有云調度系統架構
這是我們的架構,熟悉的人一看就知道跟業界的事情沒有什么區別,中間有一些虛擬化的坑,可以認為跟大家用得沒有太大區別,不細講了。
云化管理小結
云化管理小結:
- 階段目標
服務間資源隔離
服務伸縮頁面化操作
- 落實措施
部署系統攔截未上云業務
主動改造核心業務
在云化管理這一塊,我們實現的一個目標就是資源隔離,Docker 服務之間不要因為一個服務異常搞的第二個服務也異常。
第二是服務伸縮頁面化操作,有些服務還是很龐大的,一個服務下幾千上萬臺都可能,這種時候不希望你上機器的,在頁面上點就可以了。
我們為了落實這個目標,會把部署系統,把那些沒有上云的攔住,他要走舊的部署方式上線,他需要走舊的流程才能走舊的商業模式。
前面這些運營規范和云化管理,相信大家多多少少都會有自己的實現方式,也都會有自己的一些總結。后面容量這塊不確定大家都是怎么改的。
容量管理
如何支撐業務發展
這是一個業務量增長的曲線,也是我們容量的曲線。
一般來說你擴了一次容就一直平衡在這里,有一個點你發現撐不住了,要擴容,擴上去,就一直保持這個狀態,跟容量曲線是不怎么匹配的。
第一個點是你這個容量低于現網的業務量的時候,這其實是容量不足的,能不能很快發現這個問題。
第二個點是處理效率,容量不足的時候把曲線打上去,擴容需要多長時間,如果是幾天或者幾分鐘,這個效率是不一樣的。
我們希望實現一個最優容量的方式,它跟業務增長完全是匹配的方式。
這個曲線并不很難實現,只要擴容夠頻繁,跟這個就是完全吻合的最優容量的曲線。
你發現它容量不足是分鐘級的,擴容也是分鐘級的,那你完全可以描出這么一套一模一樣的曲線出來。
使用硬件指標評估容量
我們會說你怎么知道這個服務的容量不足的?一般我們第一個反應就是用硬件指標,包括 CPU 使用率是多少,磁盤空間多少,網卡容量、內存。這也能解決到問題,但它不能解決所有問題。
使用 CPU 評估容量
服務容量 = 當前峰值/經驗 CPU 上限,這是一個使用 CPU 來算服務容量的方式。
這是一個簡單的例子,比如你現在 CPU 當前峰值 40%,你自己的經驗覺得這個能達到 80% 的 CPU,簡單除下就是 50% 的容量。
硬件指標是否可靠
這東西靠譜嗎?我這里畫了兩個示意圖:
- 有些類似左邊這張圖是靠譜的,容量基本上和 CPU 是保持增長的。
- 有些類似右邊這張窄口瓶圖,CPU 漲到一定點的時候,容量就上不去。
真實案例
這是現網打流量的例子,人為把流量打上去,會發現有些服務怎么打都是在 80%;有些怎么都是 60%,上不去。
硬件指標的局限性
硬件指標我們認為有一些局限性:
不同服務它依賴硬件的類型,有些是被 CPU 限制,有些是被內存限制。
臨界點的質量無法預測,當 CPU 接近 80% 或者 100% 這個點的時候,我們觀察到有些服務的質量沒辦法預測,不穩定,有些服務能夠跑到 80%,CPU 還能穩定服務很長時間,有些一到 80%,可能有一些請求就不返回了。
確實,線上服務多了以后,做的事情不大可控。我們覺得應該要通過壓測,才能比較準確的得到容量的模型。
壓測方式
壓側方式分兩種:
- 一種是模擬流量
- 一種真實流量
環境也是分兩種:
- 測試環境
- 現網環境
四種壓測方式:
- 模擬流量在測試環境打的時候,測試團隊內部對質量的一些驗證。這種測試方式是測試團隊負責的,我們沒有太參與。
- 模擬流量打現網有點類似于全鏈路壓測,比如淘寶在雙十一經常這么干。對微信來說,我們只在過年這么干,微信過年好像跟淘寶雙十一差不多,都是一個比較瘋狂的狀態,所以我們會在過年前用模擬的流量打一下現網,這個不是一個很常見的壓測方式。
- 真實流量往測試環境打,一般是我們要驗證一些存儲性能時會這么干,把線上的流量旁路打到測試環境里面,看一下這些存儲性能的情況,可以在測試環境里比較好的模擬出來,不會影響到現網。
- 真實流量打現網環境,我這里主要想說的,我們真的在現網壓測,用真實的流量打現網環境是什么狀況。
現網壓測
現網壓測實現起來非常簡單,當你名字服務這塊實現比較標準的話,都是統一的流程,就是壓側流程,不停的調其中一個服務的權重,觀察它是什么后果就行了。
現網壓測可能導致的異常
這樣壓有什么問題,大家都能想到,你壓那個服務,什么時候停,怎么能保證不要搞出事來。
我們認為這里有以下三點比較關鍵:
- 壓測會不會引發故障,你能不能及時發現。
- 壓測有沒有可能帶來一些底層的問題,其實你的監控是發現不了的。
- 真的出故障以后,你怎么能快速恢復。
服務的自我保護
你的各個服務有沒有做好自我保護。我們引入了一個快速拒絕的概念,這個服務正常的時候,可能你的上線每分鐘 100 萬個請求也是能正常服務的。
上游服務給你 500 萬的時候,你只能處理 100 萬,你能不能把其他 400 萬個請求拒絕掉,這是我們框架實現的能力。
上游服務的重試保護
上游服務重試保護是怎么樣的?比如你在壓其中一個實例的時候,你一直加大它的權重,導致它快速拒絕返回失敗了,那你有沒有一個機制走到其他的實例把整個流程跑完,這也是一個比較關鍵的點,這個我們也是在框架里支持了。
立體化監控
監控體系夠不夠完善,包括硬件的監控,剛才提到快速拒絕的監控,還有前端跟后端這種耗時的監控,還有剛才提到的前面這種,有沒有發生失敗,整條線都是我們需要關注的。
可以這么說,有了服務的自我保護、上游服務的重試保護、立體化監控,我們才敢壓測,如果這三個點搞不定,壓測這個事情做不了。
秒級監控
能不能快的發現你的異常?為了這個問題,我們把分鐘級的監控升級成了秒級的監控,所有的異常差不多都是 10 秒鐘之內就能發現。
整個實現的方式大家應該都差不多,每臺機都有個采集的,之前是每分鐘上報一次到隊列,然后再匯總,然后再入庫。
現在我們改了這個邏輯,6 秒鐘做一次采集,然后數據轉化,然后做一個秒級數據的匯總。上面還是分鐘級的。
放量速率動態控制
這里還有一個速率的動態控制,這里比較復雜,不知道怎么解釋這種情況。
左邊這張圖是我們比較早期壓測的實現方式,它從一個點開始,可能用一個比較快速的調整它權重的方式,直到調用失敗出來了,然后再回退,再繼續往上壓,不停往上不往下,用這種方式來接近你的容量上限。
這個點大家很明顯看到一個問題,運維在壓測的時候其實是給自己找事,每次壓測都打上來,又掉下去,打上來。
失敗曲線就像狗啃的一樣,一直有抖來抖去的曲線。這種壓測方式,運維自己都受不了。
后面我們調了一下,其實這個也跟我們服務框架提供的能力相關,我們整個服務框架會有一個入隊/出隊的機制,每個微服務本身都會有這樣的機制。
這里隊列的積壓情況是比較關鍵的指標,我們會監控入隊延遲,它出隊,發現它一有積壓,性能已經跟不上了,我們通過這種指標來調整壓測的速率,大概實現的效果是右邊這張圖。
前期是比較快的權重,當觀察到隊列已經有積壓有延遲,就開始放緩,一直達到一個點,可能最后那個點是有一點點壓測失敗,實現這么一個效果,整個過程中可能只有幾秒鐘,就得到性能模型,得到壓測真實的值。
現網壓測效果
這是我們真實的線上壓縮的結果。這張圖打出來的斜率是先漲得比較快,后期再慢慢增長,然后把這個壓測點停掉了。
為了打出這張圖的效果,我們還搞了蠻久的,可能大概一年多才達到這種現狀,基本上不會給現網服務帶來失敗的。
容量管理小結
這個事情我們做完了,覺得有幾方面的收益:
- 服務的資源需求可準確量化,剛才提到幾百微服務,每個是怎么算出來的。
- 可確認服務的最優機型,怎么知道你這個微服務適合于哪種機型?有些服務根據我們壓測會發現它有些場景跟別人不一樣,我們只要把自動化壓測的方式實現了,每個模型試一下,壓出來你知道它最優的機型是怎么樣的。
自動調度
業務增長的自動擴容
解決業務增長的自動擴容,因為你的業務量在漲,你的用戶的請求包括各種請求量都是跟著指標在漲的。
你知道業務量怎么增長,知道微服務的增長曲線,前面的壓測又知道每個實例的性能怎么樣,你就能夠準確知道我現在容量大概比例是在哪條線上。
我們一般讓它跑在 50%、60% 這個區間內,66% 是一個容災的預留,我們先部署三個 IDC 里面,你要想掛掉兩個,另外一個也不會有什么異常的話,可能就是這兩個限。
我們會留出一些時間給采購機器給我們,會把一些服務的流量控制在50%這個點。
異常情況的自動擴容
還有一些異常的情況:
- 業務量的突增,這種是某些產品搞活動沒有通知我們,我們會用一些粗暴的方式來解決,包括說 CPU,我們會給它定一個點,當你的 CPU 沖過這個點的時候,我們會自動發起擴容。
- 程序性能下降,你業務沒有太大有變化,但你程序變了,這其實也會導致容量不 ok。這個點我們是能夠監測到這種情況的,就看我們壓測的頻率有多頻繁,如果你每天都壓測,你可以把壓測的曲線描出來。
程序性能下降的合理性評估
我們差不多有些服務是 2015 年每天都在壓,每一天他的性能是怎么樣的,我們描出一條曲線出來。
可能會發現有一些時間點性能下降比較遠,比如這里的例子是他發了個大版本,特性里面增加了一些處理邏輯,所以性能下降比較明顯。
這個事情可能過了一兩個月以后,有些同事出來解決 fix 它,這種性能監控我認為是前面的容量管理和智能交互這一塊的副產品,這個副產品我們用得還比較多的,可以準確知道整個微服務性能變化怎么樣。
性能管理閉環
能不能不要讓它出現這種新的下降的點,我們每天都在壓,能不能再快一點,直接在它上線的時候直接攔住。
比如說開發同學灰度上了一個臺機,我們馬上壓測它灰度上線的這個機器,看它的性能怎么樣,如果發現它的性能下降,就把他拉住。
幾種業務形態
這是我們現網的一些業務形態,可能比較多的就是最上面這種圖,它在晚上 9 點到 10 點迎來它的最高峰。中間這種圖一般跟工作相關的如微信,工作時間段用得比較多的。
還有一種是最底下這種,比較典型的例子是微信活動,微信活動好像是晚上 22 點的時候會來一步。
還有一些比較古老的業務,漂流瓶,用得腦殘粉還挺多的,他們會守著 0 點的時候。
削峰填谷
我們對這些業務曲線希望做什么?
- 峰那么高,我們所有的設備都是為了應付最高的那個峰,這個峰能不能做點特殊處理,不給它們危險設備。
- 晚上零點以后,這么低的業務量,設備挺浪費的。我們希望有一個削峰填谷的作用,原理還是很簡單,就是把你的有些服務跟另外一些服務的峰值錯開。有些服務高峰期過了,把它的資源放棄出來,至于誰去用它不管。
在線業務削峰
在線業務削峰:
- 常規服務,高峰期后釋放資源。
- 錯峰服務,獲取資源。
離線計算填谷
離線計算填谷:
- 離線任務的運行時段
01:00 ~ 08:00 不限任務
08:00 ~ 20:00 任務入隊控制
- 離線任務的資源占用
cpu.shares + memory.limit_in_bytes + blkio
離線任務的優先級最低
離線計算,凌晨那段時間確實都很閑。這個時候比較適合離線計算來調度,微信群之前離線計算的東西比較少,主要是語音輸入等一些人工智能方面的東西。
最近可能有一些看一看、搜一搜的新功能,他們用的離線計算的調度還挺多的。
資源占用方面,這幾個場所的配置基本上是 Cgroup 控制得比較嚴格一些。然后把離線任務的優先級調一下,用離線任務來把我們低峰的谷填平了。
自動調度小結
這在自動調度這塊是我們實現的一個目標:
- 全盤控制所有的在線服務,充分利用資源。
- 離線任務這塊不用單獨申請計算資源,直接用我們在線上的一些 CPU 內存就好,存儲單獨去說。
吳磊,微信基礎平臺運維負責人,2010 年加入 QQ 郵箱運維團隊,從微信第一個版本開始全程支撐從 0 到數億同時在線的野蠻生長。目前負責消息收發、朋友圈等基礎業務運維,專注自動化運維系統建設。