Skip to content

SRE: How Google Runs Production Systems 讀書筆記(二)

  • Ops

第三章在講風險,開頭再度強調了「不該追求極端的可靠性」,我們必須考慮機會成本——可靠性的成本並非線性成長,多追求一個級別的提昇(例如 99% 提昇至 99.9%)說不定成本要提高百倍才做得到,甚至,好不容易達成後對使用者來說根本沒差

怎麼看風險?

可靠性(或者說對故障的容忍程度)是靠風險管理得出的結果。風險是一個非線性的連續體(continuum)的概念,我們要問的問題是:服務合理的風險是多少?

合理的風險更像是一個可選的區間,透過衡量「成本」、「業務風險」與「維運風險」對應後我們可以再選出一個目標值

怎麼量化風險?

故障導致的業務風險很難量化(例如媒體大肆報導導致退訂),最直接的作法是參考指標(延遲、停機時間、請求成功率等)

拿停機時間來看——可用性水準訂為 99.99%,一年當中可以接受的停機時間就是 52.56 分鐘

但對分散式服務來說,停機可能是很久才會發生一次的(較難追蹤校正),可以改用請求成功率——可用性水準訂為 99.99%,如果一天要接受 2.5 M 個請求,錯誤數量少於 250 個即可(要注意不同請求的重要性不同,直接面向使用者的請求會更重要)

可用性目標應該多久更新一次?訂定的依據是什麼?

通常以一季為單位來訂,每週或每天追蹤其變化

成本考量很重要,舉了一個很好的比較:Gmail 跟 Youtube——Gmail 定的可用性目標很高,因為如果服務中斷,會導致大量成本及嚴重後果(例如包含許多企業在內的使用者流失);反之,為 Youtube 設定的可用性目標較低,因為快速發展更重要

可以思考下列這些問題:

  • 使用者是誰?他們期望怎樣的服務水準?
  • 服務是否直接關係到收入?(公司的收入或客戶的收入都算)/ 是付費還是免費?
  • 競爭對手的服務水準如何?
  • 多一個「9」,收益增加多少,成本增加多少?
  • 服務是否為基礎設施?服務水準是否可以適用各個業務端?

如果難以訂定,可以參考「ISP 的服務錯誤率」(大約 0.01%-1%),只要可用性優於此基礎,故障就會被網際網路的噪音(noise)掩蓋掉

持續發生的零星故障 / 全網中斷哪個更糟糕?

看情況。不同故障類型不能以同樣方式來量化,要考慮不同故障會對業務端造成多大影響

同一個服務怎麼滿足不同類型的使用者?

有時服務的特性是雙面刃,書上舉了 BigTable 為例:要低延遲,利用率愈低愈好(可以更快處理),但要低成本,利用率愈高愈好(避免閒置)

我自己在使用 BigTable 也遇過這種競爭約束(competing constraints)的情況:同一個應用要服務 streaming 跟 batch 這兩種不同場景

書上提供一個解決方式:用戶隔離——低延遲使用者跟離線分析使用者分別使用不同叢集


注意:Dev/Ops 的矛盾也會出現在 SRE 與產品研發部門身上

要彌補這種落差(各種人的因素),最好是透過數據來達成共識——例如,兩個部門共同定義一組以 SLO 為基礎的單季錯誤預算

錯誤預算是一個很好的決策協調依據

怎麼計算、運用錯誤預算?

  1. 定義出 SLO,例如定義可容忍的停機時間 Q
  2. 追蹤、測量實際停機時間 P
  3. 計算錯誤預算 B = Q – P,如果 B > 0,代表還有剩餘的預算,就可以拿來發行新版本

也就是說,只要系統符合 SLO,就可以繼續發行新版本;如果頻繁違反 SLO,就減慢、暫停發布作業或甚至將服務退回上一版本


SLI、SLO、SLA

這章更深入討論了三個概念的細節和根本差異。就英文字來看,其實很容易理解:指標(測量什麼)、目標(測量值應該低於多少)、協議(違反目標有什麼後果)

最關鍵的 SLI 是延遲(由於使用者延遲端難以取得,通常會用伺服器端的延遲)
;其他指標包含請求錯誤率(多少請求失敗)、QPS、可用性等,甚至是想辦法取得的特殊資料(例如在瀏覽器埋追蹤工具)

通常最難的是怎麼訂 SLO,但不管怎樣,SLO 一定要有,這樣所有人(包含使用者)的預期才會一樣

因為牽扯到政策、罰款,決定 SLA 通常不是 SRE 的事。要注意的是,當我們講到 SLA,很多時候討論的其實是 SLO

如果服務被廣泛依賴又不太容易故障怎麼訂 SLO?

這邊以 Chubby 舉例,如果每季真實的故障沒有違反 SLO 的話,SRE 會故意弄一個可控的故障讓服務停機,藉此找出依賴端的問題

要用哪些指標?幾個指標才夠?

不要將監控系統的所有指標都列為 SLI,通常 4-5 個有意義(瞭解使用者的需求)的指標就夠了

最好是可以固定一套定義方法:

  • 測量頻率: 10 秒一次
  • 彙總間隔: 60 秒一次
  • 彙總範圍:叢集的所有任務
  • 包含哪些請求:從監控端發來的 HTTP GET
  • 資料來源:透過監控系統訪問服務伺服器取得
  • 延遲定義:服務收到請求至最後一個位元組被送出

指標怎麼彙總/分析?

取平均值會導致某些極端數據變化被淡化

多數的指標都應該採分佈統計(distributions):例如看 50/85/95 百分位,高百分位代表最差情況

回應時間的分佈越分散,表示使用者受到長尾延遲(long-tail latency)的影響愈明顯

訂 SLO 要注意什麼?

這章的結尾提供了一些建議,首先是 SLO 愈少愈好(如果產品開發團隊無法接受某個 SLO,那就不必要)

一開始可以先訂一個比較鬆散的目標,再後期慢慢調整收斂,會比一開始就訂過高的目標來得好

如果 SLO 會公開給使用者知道,對外公開的目標可以比內部的目標還低,保留一點彈性空間

怎麼實際運用 SLO?

除了每季的評量,在日常追蹤 SLI 的時候,也需要參考 SLO,才能知道我們當前該不該行動,例如請求延遲一直上升快超過 SLO 了,就該立即做 scale out 提昇負載能力


減少瑣事

「如果正常運轉的系統需要人為干預,該將此視為一種 Bug。」

什麼是瑣事(toils)?

只跟日常維運工作中有關,有以下特性:手動、重複性、可被自動化、無長遠價值

就統計,瑣事最大來源是 interrupts (非緊急),其次是 on-call(緊急),再來是版本發行和資料更新

流程開銷(Overhead)不是瑣事,例如工作檢討、人員招聘、會議


監控系統

Google 傾向使用簡單的監控系統進行事後分析(post hoc analysis),而不是仰賴 AI

每個 SRE 團隊一般至少有一位「監控專員」

監控系統信噪比要很高,噪音太多或警報太頻繁,員工會進入「狼來了」效應,懷疑警報的有效性,甚至忽略該警報

監控系統應該指出兩個問題:What(現象)、Why(原因)——例如「頁面響應 500/404,因為資料庫拒絕連線」——Why 可以是中間原因,不一定要是問題根源

監控可分成白箱(內部)、黑箱(外部),黑箱通常很少會用到,因為只能反映「現象」,要看到「原因」得依賴白箱

為什麼要監控?

  • 分析長期趨勢,例如「使用者下個月會成長多少?」
  • 時間範圍比較,例如「網站是否比上週快?」
  • 臨時(ad hoc)的回溯分析 / debugging,「剛剛延遲增加了,有沒有其他現象同時發生?」
  • 發出警報
  • 建構儀表板來回答一些重要問題

哪些指標是必要的?

延遲、流量、錯誤率(包含顯式如500、隱式如200但包含錯誤訊息、其他策略性錯誤如響應超出1秒)、飽和度(即容量有多滿)

監控這四個指標並設定警報,是監控系統的基本要求

測量頻率怎麼決定?

系統不同部位應該以不同精度測量。須要考慮成本、服務可用性

例如:對一年停機小於 9 小時的服務(可用性 99.9%),每分鐘一次以上的監控頻率可能太頻繁

監控系統的設計原則

  • 規則愈簡單愈好
  • 不常用的資料收集、彙總、警報應該定期清理(例如一季沒用到就刪除)
  • 只收集但沒有在儀表板顯示或拿來設定警報的規則應定期清理

短期調整還是長期修復?

舉了早期的 BigTable 爲例,先降低 SLO 讓人員有時間去解決真正的問題

另一方面舉了早期 Gmail 為例,有一個 Bug 難以修復,選擇用一個短期 workaround 去解決,但在內部造成反對聲音(有人不想留技術債)

這種衝突是很平常的,找上級討論也許是個好做法

自動化的價值

自動化大部分的好處我們都說得出來(一致性、節省時間)

這邊提到一個特別有趣的觀點,就是「自動化提供一個 platform」:有錯誤只需要修正一次就可以長期適用、可以擴張、有經濟效益

另外一個讓我很有感的價值是「自動化讓我們更深入理解現有流程」

自動化不是萬靈丹。永遠有個最後的理想方案:自治系統(autonomous system)

不是每個系統都適合自動化,例如prototype

對 SRE 而言,用自動化管理的是系統的生命週期(例如部署新的叢集),而非系統內部的資料

Google SRE 用什麼工具?

Puppet、Chef、cfengine:直接操作服務、higher-level abstractions

Perl:支援 POSIX,在系統的 API 層有很好的擴充性

在較上層做操作、封裝好處是易於理解/管理,但代價就是做得不好(leaky abstraction)的時候容易出 bug

尤其實務上會遇到的問題是很複雜的:網路斷線、系統不一致,每個步驟都可能卡住。通常這時候自動化模型只能中止並請求人為干預

內化的自動化更好

與其透過外部膠合(glue)來操作,不如系統自己來做更正確、有效率

這種膠合也是一個問題,例如透過自動化腳本去呼叫服務的 API,但服務更新到下一版後,API 變了,外部腳本也要跟著改

這邊我的理解是某些封裝(抽象)還是盡可能做在系統內部比較好,我可以想到的例子類似在容器映像檔提供的特定腳本(例如 postgresql 提供 update.sh)

當然書中說的自動化演進最終目標是自治,而不只是一連串的腳本,也就是說,解決方案已經內化到程式裡了,例如最佳案例:Borg

成功案例:MoB

Google 想把 MySQL 服務移植到 Borg 上,藉此提高資源利用率/減少成本

克服的難點是移轉的時間:master 移轉時間太長(30-90min),常須人為介入

Google 成功弄了一個自動故障移轉的程式將時間縮短到 30s(滿足錯誤預算),成功後維運成本下降 95%,硬體成本下降 60%

成功案例:自動上線叢集

初期:用 Python 做 Prodtest

Google 會在大量機台上安裝 DNS(同時需要頻繁變動與更新),要確保每個細部設定都是正確的

解法是叢集為單元去跑一連串的 Python 的單元測試(測試有順序,失敗就終止),後來甚至為每個環節都加上了自動修補腳本

雖然有解決當前問題,但更像是 workaround ,因為這種在正式環境的自動修補,其實會讓系統處在不穩定(flaky)的狀態

後期:改以服務導向

因應安全須求,SRE 的權限被降到最低,不再有 root 操作權限

解決方式是認知到這是一個服務導向架構 (SOA),須由服務擁有者建立一組 admin server 提供管理 API(以 ACL 為基礎的認證服務,取代直接 ssh,會記錄所有 RPC 請求和響應),再由 SRE 來呼叫


看到這章的最後,其實對自動化的理解有蠻多改觀的:「自動化只是自治系統的必經之路」

我們甚至也不用擔心高度的自動化會讓人愈來愈難以介入操作(經驗剝奪),因為在未來的自治系統已不需要任何的手動操作

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *