來自 Google 的高可用架構理念與實踐
我先做一下自我介紹,我是 07 年加入的 Google,在總部任 SRE,今年年初回到 Coding (http://coding.net) 任 CTO。SRE 的全稱是 Site Reliability Engineering ,基本相當于國內的運維,但是更偏開發。
在 Google 我參與了兩個比較大的 Project。
第一個是 油Tube,其中包括 Video transcoding,streaming 等。Google 的量很大,每個月會有 1PB 級別的存儲量。存儲、轉碼后,我們還做 Global CDN。到 2012 年的時候,峰值流量達到 10 TBps,全球 10 萬個節點,幾乎每臺機器都是 16/24 核跑滿, 10G uplink 也是跑滿的狀態。
然后我加入了 Google Cloud Platform Team, 也就是 Borg 團隊。這個團隊的主要工作是就是管理 Google 全球所有的服務器,全球大概有 100 萬臺左右。另外就是維護 Borg 系統,同時我也是 Omega 系統運維的主要負責人,很可惜這個項目最后由于各種各樣的原因在內部取消了。
下面我想跟大家分享的是關于可用性、可靠性上面的一些理念和思考。
一、決定可用性的兩大因素
談可用性不需要繞來繞去,大家只談 SLA 即可。大家可以看下面這個圖:
要談可用性,首先必須承認所有東西都有不可用的時候,只是不可用程度而已。一般來說,我們的觀念里一個服務至少要做到 99.9% 才稱為基本上可用,是合格性產品。否則基本很難被別人使用。
從 3 個 9 邁向 4 個 9,從 8 小時一下縮短到 52.6 分鐘的不可用時間,是一個很大的進步。Google 內部只有 4 個 9 以上的服務才會配備 SRE,SRE 是必須在接到報警 5 分鐘之內上線處理問題的,否則報警系統自動升級到下一個 SRE。如果還沒有,直接給老板發報警。
大家之前可能聽說谷歌搜索服務可用度大概是全球 5 個 9,6 個 9 之間。其實這是一個多層,多級,全球化的概念,具體到每個節點其實沒那么高。比如說當年北京王府井樓上的搜索集群節點就是按 3 個 9 設計的。
有關 SLA 還有一個秘密,就是一般大家都在談年 SLA,但是年 SLA 除了客戶賠款以外,一般沒有任何實際工程指導意義。 Google 內部更看重的是季度 SLA,甚至月 SLA,甚至周 SLA。這所帶來的挑戰就更大了。
為什么說看這個圖有用,因為 99%、99.9% 是基本可以靠運氣搞定的哦。到 3 個 9 可以靠堆人,也就是 3 班倒之類的強制值班基本搞定。但是從 3 個 9 往上,就基本超出了人力的范疇,考驗的是業務的自愈能力,架構的容災、容錯設計,災備系統的完善等等。
說了這么多,作為一個架構者,我們如何來系統的分解“提升 SLA”這一個難題呢。
這里我引入兩個工業級別的概念 MTBF 和 MTTR。
MTBF: Mean time between Failures。 用通俗的話講,就是一個東西有多不可靠,多長時間壞一次。
MTTR: Mean time to recover。意思就是一旦壞了,恢復服務的時間需要多長。
有了這兩個概念, 我們就可以提出:
一個服務的可用度,取決于 MTBF 和 MTTR 這兩個因子。從這個公式出發,結合實際情況,就很好理清高可用架構的基本路數了。那就是: 要么提高 MTBF, 要么降低 MTTR。除此之外別無他法。
要注意的是,考慮 MTBF 和 MTTR 的時候都不能脫離現實。
理論上來說,作為一個正常人類,收到突發報警、能正確的分析出問題所在、找到正確的解決方案、并且 【正確實施】的時間極限大概是 【兩分鐘】。這個標準我個人覺得是高到天上去了。作為一個苦練多年的 Oncall 工程師,我 2 分鐘能看清報警,上了 V*N,找到 dashboard,就不錯了。就算是已知問題,有應對方案,能敲對命令,完全成功,也至少需要 15 – 20 分鐘。所以如果按照這個標準的話,管理的服務如果想達到 4 個 9,那么一年只能壞 1 次,2 次就超標了。實現高可用基本靠運氣~
回過來接著說說 MTBF 吧。請各位想一下,影響服務MTBF的三大因素!
-
發布
-
發布
-
還是發布!
這個術語上叫 Age Mortality Risk。
一般一個服務只要你不去碰他一年都不會壞一次。更新越頻繁,壞的可能性就越大。凡是 Software 都有 BUG,修 BUG 的更新也會引入新的 BUG。發布新版本,新功能是 MTBF 最大的敵人。
二、高可用性方案
說了 MTBF 和 MTTR 這兩個定義,那么具體究竟應該如何落地實踐來提高可用性呢?
首先說幾個大家可能都聽膩了的方案
一、提高冗余度,多實例運行,用資源換可用性。
雖然道理很簡單,實現起來可不簡單,有很多很多細節上的東西需要考慮。
第一個細節:N + 2 應該是標配。
N + 2 就是說平時如果一個服務需要 1 個實例正常提供服務,那么我們就在生產環境上應該部署 1 + 2 = 3 個節點。大家可能覺得 N + 1 很合理,也就是有個熱備份系統,比較能夠接受。但是你要想到:服務 N + 1 部署只能提供熱備容災,發布的時候就失去保護了。
因為剛才說了, 發布不成功的幾率很高!
從另一個角度來講,服務 N + 2 說的是在丟失兩個最大的實例的同時,依然可以維持業務的正常運轉。
這其實就是最近常說的 兩地三中心 的概念有點像。
第二個細節: 實例之間必須對等、獨立。
千萬不要搞一大一小,或者相互依賴。否則你的 N + 2 就不是真的 N + 2。如果兩地三中心的一個中心是需要 24 小時才能遷移過去的,那他就不是一個高可用性部署,還是叫異地災備系統吧。
第三個細節:流量控制能力非常重要。
想做到高可用,必須擁有一套非常可靠的流量控制系統。這套系統按常見的維度,比如說源 IP,目標 IP 來調度是不夠的,最好能按業務維度來調度流量。比如說按 API, 甚至按用戶類型,用戶來源等等來調度。
為什么?因為一個高可用系統必須要支持一下幾種場景:
-
Isolation。A 用戶發來的請求可能和 B 用戶發來的請求同時處理的時候有沖突,需要隔離。
-
Quarantine。用戶 A 發來的請求可能資源消耗超標,必須能將這類請求釘死在有限的幾個節點上,從而顧全大局。
-
Query-of-death。大家都遇到過吧。上線之后一個用戶發來個一個異常請求直接搞掛服務。連續多發幾個,整個集群都掛沒了,高可用還怎么做到?那么,對這種類型的防范就是要在死掉幾臺服務器之后可以自動屏蔽類似的請求。需要結合業務去具體分析。
但是想要達到高可用,這些都是必備的,也是一定會遇到的場景。還是那句話,靠人是沒戲的。
二、變更管理(Change Management)
還記得影響 MTBF 最大的因子嗎?發布質量不提高,一切都是空談。
第一點: 線下測試(Offline Test)
線下測試永遠比線上調試容易一百倍,也安全一百倍。
這個道理很簡單,就看執行。如果各位的團隊還沒有完整的線下測試環境,那么我的意見是不要再接新業務了,花點時間先把這個搞定。這其中包括代碼測試、數據兼容性測試、壓力測試等等。
臺上一分鐘,臺下十年功。
可用性的階段性提高,不是靠運維團隊,而是靠產品團隊。能在線下完成的測試,絕不拍腦門到線上去實驗。
第二點:灰度發布
這個道理說起來好像也很普通,但是具體實施起來是很有講究的。
首先灰度發布是速度與安全性作為妥協。他是發布眾多保險的最后一道,而不是唯一的一道。如果只是為了灰度而灰度,故意人為的拖慢進度,反而造成線上多版本長期間共存,有可能會引入新的問題。
做灰度發布,如果是勻速的,說明沒有理解灰度發布的意義。一般來說階段選擇上從 1% -> 10% -> 100% 的指數型增長。這個階段,是根據具體業務不同按維度去細分的。
這里面的重點在于1%并不全是隨機選擇的,而是根據業務特點、數據特點選擇的一批有極強的代表性的實例,去做灰度發布的小白鼠。甚至于每次發布的 第一階段用戶(我們叫 Canary / 金絲雀) ,根據每次發布的特點不同,是人為挑選的。
如果要發布一個只給亞洲用戶使用的功能,很明顯用美國或歐洲的集群來做發布實驗,是沒有什么意義的。從這個角度來想,是不是灰度發布可做的事情很多很多?真的不只是按機器劃分這么簡單。
回到本質:灰度發布是上線的最后一道安全防護機制。即不能過慢,讓產品團隊過度依賴,也不能過于隨機,失去了他的意義。
總之,灰度發布,全在細節里。
第三點:服務必須對回滾提供支持
這點不允許商量!
這么重要的話題,我還要用一個感嘆號來強調一下!
但是估計現實情況里,大家都聽過各種各樣的理由吧。我這里有三條買也買不到的秘籍,今天跟大家分享一下,保證藥到病除。
理由1:我這個數據改動之后格式跟以前的不兼容了,回退也不能正常!
秘籍1:設計、開發時候就考慮好兼容性問題!!!比如說數據庫改字段的事就不要做,改成另加一個字段就好。數據存儲格式就最好采用 protobuf 這種支持數據版本、支持前后兼容性的方案。最差的情況,也要在變更實施『之前』,想清楚數據兼容性的問題。沒有回滾腳本,不給更新,起碼做到有備而戰。
理由2:我這個變更刪掉東西了!回退之后數據也沒了!
秘籍2:你一定是在逗我。把這個變更打回去,分成兩半。第一半禁止訪問這個數據。等到發布之后真沒問題了,再來發布第二半,第二半真正刪掉數據。這樣第一半實施之后需要回滾還可以再回去。
理由3:我這個變更發布了之后, 其他依賴這個系統的人都拿到了錯誤的數據,再回退也沒用了,他們不會再接受老數據了!
秘籍3:這種比較常見出現在配置管理、緩存等系統中。對這類問題,最重要的就是, 應該開發一種跟版本無關的刷新機制。觸發刷新的機制應該獨立于發布過程。 要有一個強制刷新數據的手段。
以上三個秘籍覆蓋了100%的回滾兼容性問題,如果有不存在的,請務必告訴我!
回滾兼容性問題,是一個整體難題。只有開發和運維都意識到這個問題的嚴重性,才能從整體上解決這個問題。而解決不了回滾難題,就不可能達到高可用。
三、可用性 7 級圖表
說完了變更管理,給大家帶來一個7級圖表,可以看看自己的服務到底在哪個可用性的級別上。
當一個服務掛了的時候……
第一級:Crash with data corruption, destruction.
內存數據庫就容易這樣。出現個意外情況,所有數據全丟。寫硬盤寫到一半,掛了之后,不光進程內數據沒了,老數據都丟光了。碰上這樣的系統,我只能對你表示同情了。
第二級:Crash with new data loss.
一般來說 正常的服務都應該做到這一點…… 。掛了之后最多只丟個幾秒之內的數據。
第三級:Crash without data loss.
要達到這一級,需要付出一定程度的技術投入。起碼搞清楚如何繞過 OS 各種 Cache,如何繞過硬件的各種坑。
第四級:No crash, but with no or very limited service, low service quality.
做的好一點的系統,不要動不動就崩潰了…… 如果一個程序能夠正常處理異常輸入,異常數據等,那么就給剛才說的高級流控系統創造了條件。可以把其他的用戶流量導入過來,把問題流量發到一邊去,不會造成太大的容量損失。
第五級:Partial or limited service, with good to medium service quality.
這一級就還好了,如果多個業務跑在同一個實例上,那么起碼不要全部壞掉。有部分服務,比完全沒有服務要好
第六級:Failover with significant user visible delay, near full quality of service
上升到這一級別,才摸到高可用的門,也就是有容災措施。但是可能自動化程度不高,或者是一些關鍵性問題沒有解決,所以業務能恢復,就是比較慢。
第七級:Failover with minimal to none user visible delay, near full quality
of service.
這邊蝴蝶扇了一下翅膀,天空落了個打雷,打掉了一整個機房,結果業務完全沒受影響。藍翔技校一鏟子下去,互聯網都要抖一抖。嘿嘿, 高可用就是為了這種事情做準備。
Q & A
1. 有什么評測系統嗎?
評測系統的第一步是收集足夠的信息。想知道自己的服務是不是高可用,必須得先監測啊!不光黑盒監測,還要有白盒監測。如果有一個自動化的 SLA 監控系統,能顯示實時的 SLA 變化 ,會對系統的開發計劃有很強烈的指導作用。
2. 能詳細說一下做到 “crash without data loss” 需要在哪些點上下功夫嗎?
這個事情說起來簡單,實際實現起來非常復雜。 因為很多東西深究起來都有無數的坑。 比如說:
-
OS 的 Cache 如何繞過。
-
系統硬件可能也有內置的 Cache,Firmware 也可能有 BUG 等等等等。還有一些集群系統為了所謂的性能實現的 fake sync。
這里是列不全的。我提出這個等級的意思,是想讓大家有這個意識去系統化的應對這個問題。比如說關鍵數據是不是要多存幾分,然后做一些 destruction 測試。比如多模擬斷電等等的極端情況,這樣才能有備無患。掃雷比觸雷要容易多了。
3. 現在 Coding.net 到幾個9了,7張圖中第幾級了,改造花了多長時間,有哪些坑分享下?
首先高可用是按業務來的,不是所有業務都能做到高可用,也不是所有都需要做到高可用。我們下了很大精力在關鍵業務上,比如說 Git 系統的流控,數據安全等等,其他的就沒辦法啦。
4. 開發團隊要怎樣配合?周期怎么樣配合?側重點各自在哪 (開發更側重業務)?
首先就是要確定一個共同目標。高可用是有代價的,你的業務需要做到什么程度,一定是一個系統性的考慮。給大家舉一個例子,油Tube 這么多視頻, 但是每個視頻的每種格式,只存了1份。所以可用性肯定受影響了。但是,數據量實在是太大了,然后各種小貓視頻實在是不重要。相比之下,廣告視頻經常存8 份。所以!想要提高可用性,必須要和開發團隊找到一個共同目標。這里再給大家一個秘籍,那就是 error budget。跟開發團隊確定一個可用度,比如說 99% 。 如果業務團隊搞出來的東西很爛,各種狀況,最后達不到這個標準。那么對不起,暫時別發新功能,只修 BUG。
5. 谷歌的 SRE 工程師用了哪些開源工具來管理百萬機器?
比較慚愧,我們沒用什么開源工具,都是內部自己開發的。企業內部管理用了Puppet,但是生產系統上沒有。
6. 請問一下實現獨立對等的N+2服務使用什么架構比較好,LVS+Keepalive 的雙機熱備是否合適?
莫非現在不都用 haproxy / nginx 之類的7層代理?但是其實這個原理都差不多。只要達到你的目的,可以動態切換就好。
7. “可以把其他的用戶流量導入過來。把問題流量發到一邊去,不會造成太大的容量損失。” 這句話怎么理解呢? 另外如何區分問題流量?
這句話說的是剛才提到的高可用必不可少的流控系統。任何一個系統都不是完美的,總會有各種各樣的 hot spot,weak spot。問題流量的定義是跟業務緊密相關的。我再舉一個例子:當年 油Tube 的 CDN 服務器有個問題,后端存儲慢了之后,前端的請求會聚在一起,就像水管一樣,于是內存就爆了。突然壓力過高,肯定就掛了。如何處理這個問題? 最后流控系統升級,每個實例自己匯報自己的內存狀況,超標之后流控系統自動繞過他。把這個問題變成了自動化處理的方案,問題面大大縮小了。再舉一個例子, 搜索系統是典型的熱點密集型系統。有的冷僻詞, 查一次要去各種讀硬盤。而熱詞,消耗很小。所以流控系統做了個功能每個請求回復都帶了 cost 值,流控系統自動均衡了整個集群。
8. 關于回滾那里,如果我要新增一個刪除功能,怎么做到把這個操作拆成兩半,用戶做了刪除操作,可是禁止刪除數據,那是否會產生數據不一致的?
這個是剛才說的那個秘籍第二條。其實秘籍第二條就是拆!沒有什么發布是不能拆的。 拆到可以安全的往前滾再往后滾。
9. 100W臺服務器如何自動化管理、及時發現故障、自動修復、做出報警,能否簡單介紹介紹?
這個問題其實沒那么復雜。就是在每個機器上運行一個agent,這個agent定期進行檢查操作,有問題就通知管理系統自動下線。只要注意平時收 集問題現象就行了。比如說線上突然出現了一個時間不同的問題,那么就把他加到這個agent里去,下次這個問題就是自動檢測自動修復的了。
10. 有沒有什么好辦法處理 query to death?
這個問題比較難,一般是要做一層智能一點的業務 proxy 。業務 proxy 檢測到請求發到哪,哪個后端掛,就可以進行一些處理了。還有一個辦法是在掛之前后端記log,處理之前記上。我要處理這個請求了,然后處理一半掛掉了。重 啟之后,檢查 log 發現上次是處理這個請求掛了,那么就可以屏蔽掉這個請求。