來自 Google 的高可用架構理念與實踐

葉雨飛發表於2015-12-15

在 Google 我參與了兩個比較大的 Project。

第一個是 YouTube,其中包括 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 分鐘能看清報警,上了 VPN,找到 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, 甚至按使用者型別,使用者來源等等來排程。

為什麼?因為一個高可用系統必須要支援一下幾種場景:

  1. Isolation。A 使用者發來的請求可能和 B 使用者發來的請求同時處理的時候有衝突,需要隔離。
  2. Quarantine。使用者 A 發來的請求可能資源消耗超標,必須能將這類請求釘死在有限的幾個節點上,從而顧全大局。
  3. 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. 開發團隊要怎樣配合?週期怎麼樣配合?側重點各自在哪 (開發更側重業務)?

首先就是要確定一個共同目標。高可用是有代價的,你的業務需要做到什麼程度,一定是一個系統性的考慮。給大家舉一個例子,Youtube 這麼多視訊, 但是每個視訊的每種格式,只存了1份。所以可用性肯定受影響了。但是,資料量實在是太大了,然後各種小貓視訊實在是不重要。相比之下,廣告視訊經常存8 份。所以!想要提高可用性,必須要和開發團隊找到一個共同目標。這裡再給大家一個祕籍,那就是 error budget。跟開發團隊確定一個可用度,比如說 99% 。 如果業務團隊搞出來的東西很爛,各種狀況,最後達不到這個標準。那麼對不起,暫時別發新功能,只修 BUG。

5. 谷歌的 SRE 工程師用了哪些開源工具來管理百萬機器?

比較慚愧,我們沒用什麼開源工具,都是內部自己開發的。企業內部管理用了Puppet,但是生產系統上沒有。

6. 請問一下實現獨立對等的N+2服務使用什麼架構比較好,LVS+Keepalive 的雙機熱備是否合適?

莫非現在不都用 haproxy / nginx 之類的7層代理?但是其實這個原理都差不多。只要達到你的目的,可以動態切換就好。

7. “可以把其他的使用者流量匯入過來。把問題流量發到一邊去,不會造成太大的容量損失。” 這句話怎麼理解呢? 另外如何區分問題流量?

這句話說的是剛才提到的高可用必不可少的流控系統。任何一個系統都不是完美的,總會有各種各樣的 hot spot,weak spot。問題流量的定義是跟業務緊密相關的。我再舉一個例子:當年 Youtube 的 CDN 伺服器有個問題,後端儲存慢了之後,前端的請求會聚在一起,就像水管一樣,於是記憶體就爆了。突然壓力過高,肯定就掛了。如何處理這個問題? 最後流控系統升級,每個例項自己彙報自己的記憶體狀況,超標之後流控系統自動繞過他。把這個問題變成了自動化處理的方案,問題面大大縮小了。再舉一個例子,搜尋系統是典型的熱點密集型系統。有的冷僻詞, 查一次要去各種讀硬碟。而熱詞,消耗很小。所以流控系統做了個功能每個請求回覆都帶了 cost 值,流控系統自動均衡了整個叢集。

8. 關於回滾那裡,如果我要新增一個刪除功能,怎麼做到把這個操作拆成兩半,使用者做了刪除操作,可是禁止刪除資料,那是否會產生資料不一致的?

這個是剛才說的那個祕籍第二條。其實祕籍第二條就是拆!沒有什麼釋出是不能拆的。 拆到可以安全的往前滾再往後滾。

9. 100W臺伺服器如何自動化管理、及時發現故障、自動修復、做出報警,能否簡單介紹介紹?

這個問題其實沒那麼複雜。就是在每個機器上執行一個agent,這個agent定期進行檢查操作,有問題就通知管理系統自動下線。只要注意平時收集問題現象就行了。比如說線上突然出現了一個時間不同的問題,那麼就把他加到這個agent裡去,下次這個問題就是自動檢測自動修復的了。

10. 有沒有什麼好辦法處理 query to death?

這個問題比較難,一般是要做一層智慧一點的業務 proxy 。業務 proxy 檢測到請求發到哪,哪個後端掛,就可以進行一些處理了。還有一個辦法是在掛之前後端記log,處理之前記上。我要處理這個請求了,然後處理一半掛掉了。重啟之後,檢查 log 發現上次是處理這個請求掛了,那麼就可以遮蔽掉這個請求。

相關文章