Skip to content

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

  • Ops

發行工程(release engineering)

這章開頭介紹了有關「發行」這個新的學科,在 Google 雖由 SRE 以外的特定角色負責,但 SRE 一樣須要與之密切合作與關注;後半部份介紹發行流程、以及 Google 使用的發行系統 Rapid 與建構工具 Blaze (開源版本為 Bazel)

發行工程師在幹麻?

發行工程是一項獨立出來的工作,核心是 build 跟 deliver,由發行工程師負責

發行工程師的工作不是「負責發行」,而是開發工具、制定 best practice,好讓產品研發團隊能自主發行,也就是 self-serving

發行模式有哪些?

發行的步驟由發行工程師、SWE、SRE 三者一起定義,舉幾個例子:

  • 每小時建構一次,再根據變更項目和測試結果挑出要發行的版本
  • Push On Green,測試通過就發行

因規模和軟體性質而異,有的發行時間會需要延長到好幾天(如基礎設施)

怎麼建構?

無論使用什麼建構工具(Google 用 Blaze),建構應該要是一致且可重複的,也就是在兩台不同機器上建構同一個版本,應該得到相同結果

建構過程應是密閉的 (hermetic),不應受機器上安裝的其他函式庫或軟體影響

正式站有 bug 怎麼辦?

Google 通常不在 master 分支直接發行,而是從 master 切新的分支用來發行(避免建構後無關的變更影響到 master)

針對 bug 的修復會先上到 master,在發行分支再把該變更 cherry pick 過來,即可重新建構跟發行

怎麼持續建構/部署?

因為這章提到的工具跟元件比較多,從 Florian Rathgeber 的簡報上找到的一張關聯圖來看可能會比較好理解

image

  • Midas Package Manager(MPM):是 Google 使用的套件管理系統,資料元存在 BigTable 上。我粗淺的理解方式是把它「對應」到容器的映像檔,它們都是利用「名稱 + 標籤」作為唯一識別,但比起容器映像檔,MPM 有更進階的功能,例如 ACL 跟簽章;另外一個從根本上不同的是映像檔是有序的 layers,而 MPM 則是無序的 filegroups
  • Blaze:建構工具,用來建構 MPM、二進制檔案、對應的測試
  • Rapid:分散式發行系統,可執行由建構藍圖 (blueprint) 定義的工作流、利用 Blaze 來跑建構及測試;Worker 是跑在 Borg 上,可以並行處理發行工作
  • Sisphus(希臘神話的薛西佛斯):自動化部署的框架,提供更彈性的部署功能、流程儀表板

一句話概括基本流程:Rapid 負責跑測試、建構 MPM(打上特定標籤),Sisphus 負責部署此 MPM 版本

怎麼管理設定檔?

  1. 使用 master branch 的設定檔:最好理解、但較難做版本更新
  2. 將設定檔與二進制檔案封裝在同一個 MPM:最簡便
  3. 設定檔單獨封裝成一個 MPM:易於回溯、重建
  4. 外部存取:適用於設定須動態變更的場景

這段讓我聯想到 K8S 的 Secrets 跟 ConfigMap,比較像上述第四種作法,當然第二種作法——直接 build 在映像檔中是最快的,但這樣其實缺乏安全性,而且因為設定檔跟應用綁在一起,稍微改個設定值就要全部重 build,對參數類型的設定值更是不方便

簡化

這小節主要提倡幾個面向的簡化:程式碼、複雜度、系統設計、API、發行流程

  • 程式碼
    • 非必要的程式碼都應刪除,具體的做法是對於探索階段而寫的程式,可以先設定一個擱置期,這類程式可以用較寬鬆的標準看待,前提是不能被發布到正式環境
    • 要保守看待任何新功能的增加
  • 意外複雜度
    • 固有的必要複雜度是無法避免的,這個複雜度特指「意外造成的複雜度」,是可以盡量避免和消除的
    • 具體的做法是如果負責的系統被引入意外複雜度,應該提出抗議
  • 系統設計:採 loose coupling 可以提高系統的靈活度和穩定性
  • API:方法和參數越少越容易理解,對於大型系統、複雜度高的 API,可以透過漸進的「版本化」提高靈活度
  • 發行流程:採小批的發行能更快找到錯誤,整體發行速度也會更快

具體實踐

接下的章節在講每個核心方法的具體細節,有一個重要的觀念是,雖然核心方法有很多,但它們是有重要性的優先順序的,可以畫成一個需求三角形

sre-hierarchy-of-needs

基於時間序列資料的有效警報

監控跟警報是 SRE 最基本的需求,少了這塊的話就維運是作不了的。這章以 Borgmon 來切入 —— Google 內部的監控系統(不太能稱作「事件收集系統」或「時間序列資料庫」,因為這兩者都只是為了服務監控/警報功能)

一個好的監控系統應該要能彙總資料(可以做高階的抽象),應該做的不是「發生一次 404 就發出警報」,而是「每十分鐘請求錯誤速度比率超過 1% 就發出警報」

怎麼收集資料?

很多應用程式會提供方法來匯出內部狀態,例如 MySQL 的 SHOW VARIABLES,所謂「白箱監控」,就是收集這些預先定義好的內部指標

Borgmon 收集的方式是發出 HTTP 請求(這章沒講到為什麼要用 HTTP 及有什麼好處),這邊有個重要前提:「Google 的每個二進制檔案都預設包含一組 HTTP 服務」

Borgmon 會按照設定,定時(例如每分鐘)擷取服務的 /varz URI 來取得監控資料

什麼是 /varz?

Google 的命名慣例,是 variables 的簡寫,特指「服務用來查詢狀態的 HTTP 端點」

時間序列的資料結構長怎樣?

以陣列儲存 (timestamp, value) 格式的資料,由於陣列中每筆資料的時間間隔固定,我們在描述的的時候可以省略掉 timestamp:

由此可見資料的基礎其實是一個很簡單的 1-d 矩陣,由於有分類的標籤需求,再這個基礎上加入 n 個 「lablesets」,而形成一個多維矩陣

一組基本的 lableset 會包含以下幾種標籤:

實際上怎麼儲存這個資料結構?

擷取的資料會先存在一個固定的記憶體區塊,稱作 time-series arena (一般情況下會緩存 12 小時,服務即時的圖表跟線上 dubug),超過這個存放區的舊資料會被轉存到外部的時間序列資料庫中(TSDB),一樣可以查詢,但查詢速度較慢

一個 (timestamp, value) 大概佔用 24 bytes,舉例來說,每分鐘存一個點,要存放 12 小時內的 100 萬個資料點,大概要花 17 GB 的記憶體

資料型態有哪些?

資料模式可以是「累增的計數(counter)」、或者是「任意的測量值(gauges)」

使用計數模式(例如累加的里程數)會比較好,因為如果中間擷取失敗,不會造成 data-loss

怎麼查詢?

Borgmon 的程式碼稱為 rules,其實就是一些簡單的代數運算表達式。很像 unix 的管道這種過濾程式,表達式的 input 是一組時間序列,處理完會緩存一個新的時間序列(output),同時作為下一句表達式的輸入

怎麼觸發警報?

在警報的規則是由查詢的表達式加上邏輯運算,結果只有 true/false,以 RPC 通知外部的警報管理服務去處理

值得注意的是並不是違反了這個規則就觸發警報,而是「持續一段時間都為 true」才觸發(至少兩個計算週期),來避免過於頻繁的變動(flap)

怎麼部署?

每個叢集當中都會有一組「階層化的」 Borgmon,由最末端的 Borgmon 一路彙總上去,例如:

每個階層可以有多個 Borgmon 實例,用來避免單點故障和效能問題,Borgmon 之間可以透過內部串流來分享資料

這種分層很能直接體現於分層 API 與分散式系統的設計上,舉例來說,當一個全域的 API 出錯,我們可以往下查到底層的 Borgmon 所收集的原始資料,來找出問題發生在哪個地區的哪個應用程式實例

擷取失敗怎麼辦?

當服務不可用、佇列滿了,或請求失敗這種狀況發生時,白箱監控就失效了,因此除了擷取結果,Borgmon 會紀錄每個 HTTP 請求的細節(例如請求花費的時間),自動生成新的指標,這樣才能知道一旦擷取失敗,是「DNS 解析失敗」還是「回應超時」等不同情況導致。這些指標可以拿來撰寫檢測「服務是否可用」的規則

除了這些指標,SRE 也常會利用探測程式 (prober) 來做黑箱監控,如果探測失敗,可以直接通知警報管理系統

發佈留言

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