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 版本

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

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 這兩種不同場景

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

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

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

  • Ops
  • Google SRE 全球共計約 1000 人
  • SRE 是一群天生的懷疑論者,懷疑一切「高大尚」技術,以及任何「神奇的產品」
  • SRE 在確保系統可靠性方面沒有什麼萬靈丹,有的只是極度的務實(progmatic)
  • 如果用一個詞來描述 Google 的歷史,那就是不斷的 scaling up
  • 系統維運本質上是人與電腦共同參預的一項系統性工程
  • IT 產業大多自我封閉,交流過少,很多從業人員或多或少受到教條主義的限制
  • 今天我們能感受到整個業界都在鼓吹厚顏無恥的「給我程式碼,其餘免談」;開源社群內部也正在形成一種「別問我問題」的風氣,過於強調平等卻忽略專家的意見
  • 相對於最終的軟體結果、架構設計,真實的設計過程和作者本身的思考歷程更具價值
  • 一套軟體的 40% ~ 90% 成本,其實是花費在建置之後的不斷維護過程

SRE 的工作範疇

  • 維運具體服務
  • 設計研發大型分散式系統
  • 協助產品部門開發其系統的額外元件,如負載平衡,同時盡可能重複使用這些元件
  • 想出各式各樣的方法,利用現有元件解決新的問題
  • 對架構設計、維運流程不斷最佳化,讓這些大型系統有更好的「可靠性」

  • 可靠性(reliability)——系統能夠在指定環境下,在要求的時間內成功持續執行某個功能的機率
  • SRE 的 「S」最開始指的是 google.com
  • SRE 是從 Google 的內部職位、從 Web 社群中誕生的
  • 與 DevOps 一詞不同的是,SRE 同樣認同 IaC,但 SRE 最關注的是可靠性(或者說是風險),甚至可以為了可靠性而「消除維運的需求」
  • 可靠性就像安全性,愈早關注愈好
  • 「無論對一套系統的執行原理掌握得多麼透徹,也不能阻止人為的意外錯誤。」
  • SRE 的 「E」可以指事 —— Engineering,也可以是指人——Engineer

Read More »SRE: How Google Runs Production Systems 讀書筆記(一)

步入中年的轉職馬拉松 / 一個低端工程師的自白

Intro

在 2019 年底,還在上個公司就職的我,應一個面試邀約拜訪了 91APP。因為拜社群之福,我在早期就有「持續面試很重要」這個概念,不管你當下有沒有工作。

當時的我剛完成一個機器學習的基礎設施,讓整個流程得以自動化,頗為自得意滿。我天真地以為,別人會找我去面試,單純是因為我值得。

也是在這個面試中,我踩了人生第一個面試地雷——那就是抱怨當前的工作。我說:公司專案太多又太雜了,又都只有一兩個人負責。

結果可想而知,在隔天收到了感謝函。不過幸運的是,邀約我的前輩順帶給了一些很客氣的提點。他在信裡這麼說:「你的硬實力是不錯的,但你缺乏軟實力。」

同一個時期,想要離職的念頭開始萌芽。當然,我並非討厭這個工作,反而更像是漸漸找不到工作的意義了。上下班的時候,腦中不停發出例外,可以說它是一種 ValueError ,而當時的我並不知道如何去處理這個例外。

造成這個例外的可能情形:

  • 潛意識地知道自己知識量不足(軟實力),但沒花足夠的時間來彌補,大部分時間都花在拼湊和硬幹上面(硬實力)
  • 腦海中有「標準」的藍圖,但不是很清楚,而公司的標準接近能動就好,因此無所適從
  • 開始懷疑自己,懷疑專案的發展性
  • 一直以來的單打獨鬥,嚮往可以激盪討論合作的環境,說白一點就是希望有人帶
  • 不想再扮黑臉(沒錯我是 INTP)

終於,某天公司的舊系統因為太舊出問題了(單純就是因為太舊),雖然我沒接觸過該系統,但身為後端人員,多少還是覺得有點責任。當下去了一趟 Legacy 後,發覺再也忍受不了了。我喪失了所有自信。我只想讓腦袋重新開機。

我必須馬上去談這件事。當天,我跟主管提出離職的想法。我只知道自己該做出一些改變,或者更深入地說,我害怕因為長時間忽略這些內心感受,進而潛移默化成自己不想變成的那個人,再也做不出正確的決定。我也想起有人說三十歲是工程師生涯重要的臨界點。那麼與其不上不下的,不如去尋求一個長遠的突破。

借托爾斯泰之筆,我們可以這樣隱晦描述──「幸福的 RD 總是幸福的,不幸的 RD 各有各的不幸。」

也就是說,即使在別人眼裡,工程師看起來是那樣吃得好穿得暖,但事實是每個工程師和你們一樣,都有各自難以解決的問題。就如同我的 ValueError

Read More »步入中年的轉職馬拉松 / 一個低端工程師的自白

Fluent Python 讀書筆記(八)

  • Python

類別中繼編程

  • 「中繼類別是比 99% 使用者鎖想的還要艱深的魔法。如果你想知道自己是否須要他們,其實你不需要。」
  • 類別中繼邊程是在執行階段建立/自訂類別的技巧
  • 類別是一級物件,無論什麼時候,都可以用函式來建立新的類別(諸如類別修飾器),不需要 class 關鍵字
  • 中繼類別很強大,但很難正確使用,事實上,很難在實際的程式中使用
  • 編寫中繼程式的先決條件是了解匯入階段與執行階段的差異
  • 「如果你不在製作框架,就不該編寫中繼類別」

自訂一個類似 collections.namedtuple 的簡易紀錄型類別工廠

對這種紀錄型的類別而言,我們希望屬性群都是相同的,而且擁有相同順序

此範例建立的類別有一個限制:無法被序列化,即無法與 picle 模組的 dumpload 函式一起使用

在類別中定義 __slots__,等於告訴解譯器「它們都是這個類別的實例屬性」,這些屬性會被存在一個類 tuple 結構,避免每一個實例的 __dict__ 產生記憶體開銷

當類別指定 __slots__ 時,它的實例無法使用任何指定之外的屬性(這是一種副作用/缺點),單純為了做這種屬性限制而使用 __slots__ 並不是一個好的作法。__slots__ 的目標是最佳化,而不是限制開發者的行為


  • 我們通常會把 type 當成函式來用,但它同時是個類別,如果你用三個引數來呼叫,它的行為就像類別,會實體化一個新的類別
  • 避免使用 execeval 來編寫中繼程式是一種好的習慣。如果不受信任的來源傳送字串/段落給這些函式,會造成嚴重的後果;Python 提供足夠的自我檢查工具,execeval 在大部分情況下沒必要使用
  • 類別描述器有一個重大的缺點在於,它們只能在直接套用的類別上動作——被修飾類別的子類別可能不會繼承修飾器所作的改變
  • 「匯入階段(import time)」、「執行階段(runtime)」這些用語沒有經過嚴謹定義,而且它們之間有灰色地帶
  • 在匯入階段,解譯器會完整解析 .py 模組的程式碼,並產生執行的 bytecode——這就是可能會發生語法錯誤的地方(如果在本地的 __pycache__ 有最新的 .pyc,這個步驟會被跳過)
  • 雖然編譯(compiling)的確是匯入階段的動作,但這個階段也會發生其他事情——特別是 import 陳述式,它並非只是一個宣告(對比 Java 的 import)——當程序首次匯入模組時(尚無快取時),它會執行被匯入模組的所有最高層級的程式碼,包含執行階段的行為
  • 最高層級的程式碼特指類別的內文(包含嵌套的類別),解譯器會在匯入階段執行它們;相反地,對函式而言,解譯器只會編譯內文、將函式物件加到全域名稱,但不會執行函式內文
    • 類別若是有使用類別修飾器,該修飾器函式也會被執行
    • 類別的中繼類別的方法 __init__ 也會被執行

Read More »Fluent Python 讀書筆記(八)

Fluent Python 讀書筆記(七)

動態屬性與特性

  • 方法(method)只是一種可以被呼叫的屬性(attribute)
  • 特性(property)可以用來代替公開的資料屬性,不會變動到類別介面
  • 編寫動態屬性,是框架作者會採取的一種中繼編程(metaprogramming)
  • 從任意來源產生或模擬動態屬性名稱,都要處理一個問題;原始資料中的 key 可能不適合當成屬性名稱,例如 key 是關鍵字(keyword.iskeyword())或非法的識別符(s.isidentifier()

範例:使用動態屬性來探索 JSON 格式的資料

透過遞迴來建構,可自動處理嵌套的映射與串列

留意這裡沒有對查詢進行任何快取或轉換


建構實例的特殊方法是類別方法 __new__,他可以回傳完全不同的實例,在這種情況下解譯器不會呼叫 __init__

以下為建構實例的虛擬程式

Read More »Fluent Python 讀書筆記(七)