Go 開源說第八期:Chaos Mesh? 云原生混沌工程測試平臺
點擊藍(lán)字
關(guān)注我們
本文由“GO開源說”第八期 《Chaos Mesh? 云原生混沌工程測試平臺》直播內(nèi)容修改整理而成,視頻內(nèi)容較長,本文內(nèi)容有所刪減和重構(gòu)。
本文將先對混沌工程這一概念進(jìn)行描述,介紹混沌工程的動機(jī)和實踐方式,并介紹 Chaos Mesh 項目的發(fā)展情況。而在后半部分,介紹了 Chaos Mesh 項目本身的架構(gòu),作為一個典型 的云原生環(huán)境下的軟件,同時也涉及到在 Go 的生態(tài)環(huán)境中對容器等基本概念進(jìn)行操作,希望 對讀者將來類似的技術(shù)實踐能夠有所幫助。
混沌工程概述
現(xiàn)在的技術(shù)潮流在向著大規(guī)模集群、超復(fù)雜的分布式系統(tǒng)與微服務(wù)架構(gòu)演進(jìn)。在演進(jìn)的過程 當(dāng)中,雖然給我們帶來了不少的便利,同時也帶來了許多的麻煩。其中之一便是 —— 當(dāng)一個 節(jié)點發(fā)生錯誤的時候,我們無法預(yù)料它將產(chǎn)生怎樣的蝴蝶效應(yīng)。它將只牽涉到部分服務(wù)還是 會讓所有服務(wù)崩潰?它能夠自愈嗎?更可怕的是,隨著計算規(guī)模的擴(kuò)大,故障發(fā)生的可能性也 越來越大了。對于一個個人電腦用戶來說,可能用到更換電腦硬盤也不曾發(fā)生過損壞;而對于 服務(wù)器集群來說,每天都可能會有數(shù)塊磁盤損壞需要更換。

無論是云計算的領(lǐng)頭羊 AWS,面向工程師們的 Github,還是互聯(lián)網(wǎng)巨頭 Google,都無法逃離 故障的命運。
混沌工程是一門新興的技術(shù)學(xué)科,他的初衷是通過實驗性的方法,讓人們建立對于復(fù)雜分布式系統(tǒng)在生產(chǎn)中抵御突發(fā)事件能力的信心。
而混沌工程便是在這樣糟糕的環(huán)境下,讓開發(fā)者、運維對復(fù)雜系統(tǒng)仍然保持信心的方法。
混沌工程歷史
混沌工程已經(jīng)走過了十一個年頭了。從最初 Netflex 提出這個概念,到 16 年 Gremlin給出了 混動工程的商業(yè)產(chǎn)品,試圖形成混沌工程服務(wù)的商業(yè)模式。Chaos Mesh 是在 2019 年末開源的,現(xiàn)在也成為了最受關(guān)注的混沌工程項目之一。
混沌工程步驟
如果想要為你管理的項目引入混沌工程,那么可以依照以下五步的循環(huán):

不斷進(jìn)行這五步的循環(huán),將對工程的穩(wěn)定性產(chǎn)生明顯的提升。以混沌工程在 TiDB 上的實驗為 例:
1. 我們立下期望,TiDB 在刪除一個節(jié)點之后應(yīng)該能夠在短時間內(nèi)恢復(fù)。
2. 進(jìn)行了刪除節(jié)點的混沌實驗
3. 發(fā)現(xiàn)前兩次 TiDB 都在短時間內(nèi)恢復(fù)了,而第三次卻花了很長的時間才恢復(fù)
4. 調(diào)查這之中是否存在一些 Bug,修復(fù)之后再次進(jìn)行實驗,看看還有沒有這個問題

反復(fù)多次地進(jìn)行這樣的步驟,都 TiDB 的穩(wěn)定性就產(chǎn)生了一些幫助。
Chaos Mesh 的社區(qū)
在短短的一年間, Chaos Mesh 的社區(qū)也已經(jīng)不斷壯大了,現(xiàn)在擁有了超過 3400 個星星,近 90 個貢獻(xiàn)者,也是 CNCF Sandbox 項目?,F(xiàn)在 Chaos Mesh 也已經(jīng)擁有了涉及Pod 的生命 周期、IO 、網(wǎng)絡(luò)、資源壓力、公有云等諸多方面的數(shù)十種 不同類型的錯誤注入方式;還擁有一 個功能豐富的儀表盤。這一切成就離不開來自社區(qū)的幫助。無論是對 Bug 的報告、對工程、設(shè) 計的意見,還是直接提交代碼,都是對 Chaos Mesh 項目的巨大貢獻(xiàn)。
Chaos Mesh 整體架構(gòu)
Chaos Mesh 的整體架構(gòu)如圖中所展示,可以自上而下分為三個部分:
1. 用戶輸入、觀測的部分
2. 監(jiān)聽資源變化,進(jìn)行注入/恢復(fù)的 Controller 組件
3. 在具體節(jié)點上進(jìn)行故障注入的 Chaos Daemon

這部分文章將依照這三個部分展開,自上而下梳理 Chaos Mesh 的架構(gòu)。
用戶輸入、觀測的部分
用戶輸入的部分總是以用戶的操作為起點,以 Kubernetes API Server 為重點,不直接和 Chaos Mesh 的 Controller 交互。一切的用戶操作最終都將反映為某個 Chaos 資源的變更(比如NetworkChaos 資源的變更)。這保障了 Chaos Mesh Controller的事件來源的單純性。Chaos Mesh 提供了三種輸入的方式:
通過 kubectl 等命令行工具,將一個 YAML 文件提交至 Kubernetes 服務(wù)器這樣做能夠清晰地知道提交的內(nèi)容,通過 kubectl 這一 Kubernetes 用戶都能熟練使用的工具,將 Chaos Mesh 的資源操作方式和 Pod、Deployment 等其他原生資源的提交方式統(tǒng)一起來,方便用戶上手、嘗試。而除了使用kubectl apply 命令來提交一個混沌實驗之外,還可以通過kubectl describe命令來查詢實驗的狀態(tài)和錯誤信息,使用kubectl patch 來修改實驗將實驗暫停、恢復(fù)。(如圖,一個典型的描述網(wǎng)絡(luò)分區(qū)的NetworkChaos 資源文件) 
通過 kubernetes/client-go等包來對 Chaos 資源進(jìn)行增刪查改。這樣做的好處是能夠方便地集成進(jìn)已有的測試流程。如果用戶已經(jīng)搭建好一個 可編程地測試平臺,那么就可以通過這種方式在任何自己期望的時機(jī)插入、恢復(fù) 混沌實驗,如果說將 Chaos Mesh 提供的豐富的錯誤注入能力作為測試的武器 庫,那么可編程的方式就是使用這些武器的最靈活的方式。通過 Chaos Dashboard 提供的 Web UI 界面對混沌試驗進(jìn)行操作和觀測。Chaos Dashboard 提供了一套友好的用戶界面,同時也提供了依照 RBAC 的權(quán) 限管控機(jī)制,用戶需要輸入自己的 Token 才能夠進(jìn)行操作。在這部分 UI 中用戶 能夠管理和觀察已有的錯誤,同時也能將錯誤注入歸檔并在將來復(fù)用。 
監(jiān)聽資源變化的 Controller
Chaos Mesh 的 Controller 總是只接受來自 Kubernetes API Server 的事件 —— 這種事件會描述“什么資源發(fā)生了什么變化”,比如一個名為network-test 的 NetworkChaos 資源被創(chuàng)建了。在工程實踐中,對資源的具體變化進(jìn)行響應(yīng)太過復(fù)雜了;而往往選擇使用“同步” 的方式 —— 將資源中描述的情形向真實狀態(tài)中同步,比如當(dāng) Pod 建立時會將這種描述給具象化為一個(或多個)容器;反過來的 "同步" 也是存在的,當(dāng)容器意外死亡、
啟動失敗的時候錯誤狀態(tài)也會同步至 Pod 中。所以無論是怎樣的變化(無論是增刪查改
中的哪一個),Controller 要做的都只是決定以下兩件事情:
現(xiàn)在應(yīng)該注入還是應(yīng)該恢復(fù)還是要等待? 如果需要注入/恢復(fù),應(yīng)該怎么做?
對于第一個問題,Controller 將必要的信息存儲在資源中,比如下一次要注入的時間、 下一次要恢復(fù)的時間。這樣在響應(yīng)變化的時候,就能通過當(dāng)前時間來推斷應(yīng)該注入還 是恢復(fù)還是等待。
而對于第二個問題,Controller 需要根據(jù)測試類型的不同進(jìn)行不同的判斷,比如刪除 Pod 就能通過向 Kubernetes API Server 發(fā)送請求直接完成,對于公有云的混沌測試 ——比如起停虛擬機(jī)、移除磁盤則會通過每個公有云不同的 API 來完成,而其他稍稍復(fù) 雜的錯誤注入(比如后文會提到的 時間偏移 和 網(wǎng)絡(luò)延遲 )就需要 Chaos Daemon 的 幫助在每個節(jié)點上進(jìn)行一些操作來注入。
Chaos Daemon 注入實現(xiàn)
要知道如何在云環(huán)境下注入,第一個問題就是弄清楚我們在注入什么 —— Pod 的實體 是 Container 的話那么 Container 的實體是什么呢?在常規(guī)的語境下,Container的一個實體指的是一個進(jìn)程與它所屬的 Namespace 和 Cgroup。其中 Namespace 與 Cgroup 起到了一個與其他進(jìn)程隔離的作用。Namespace 控制著可見性,掌管著這個進(jìn) 程能夠看到哪些東西(看到哪些文件、看到哪些進(jìn)程);而 Cgroup 控制著資源分配:在一 個 cgroup 內(nèi)允許占用多少 CPU 時間、占用多少內(nèi)存、產(chǎn)生多少 Pid。而如果要進(jìn)行錯 誤注入 —— 比如讓一個容器內(nèi)的 CPU 資源被吃滿、讓一個 Pod 與其他的 Pod 的網(wǎng) 絡(luò)連接斷開,需要做的第一件事情就是侵入到對應(yīng)的 Namespace 及 Cgroup 中去。
侵入 Namespace
侵入 Cgroup 的實現(xiàn)是頗為簡單的,在進(jìn)程啟動之后將它加入到對應(yīng) Cgroup 的進(jìn)程
名單中即可。而 Namespace 的注入在 Go 的獨特線程環(huán)境下要困難一些。Chaos
Mesh 借鑒了 runc 的方案,自己實現(xiàn)了一個chaos-mesh/nsexec 來侵入 Namespace 。實現(xiàn)的原理是在進(jìn)程啟動的時候通過設(shè)置 LD_PRELOAD 環(huán)境變量來加載一個名為nsexec.so 的動態(tài)鏈接庫,這個動態(tài)鏈接庫的contructor 中調(diào)用 setns 系統(tǒng)調(diào)用來將自身
設(shè)置到目標(biāo)名字空間中去。執(zhí)行流程如圖所示:

這一工具相比 nsenter 這個現(xiàn)成工具的好處主要有兩點:
擁有更適合 Chaos Mesh 的信號處理機(jī)制,能夠方便地控制子進(jìn)程的死亡。 能夠和 mnt namespace配合工作,只要是當(dāng)前 mnt namespace 存在的二進(jìn)制文件,都能夠在目標(biāo)名字空間中運行,而不需要目標(biāo)名字空間中存在。在常見的distroless 的鏡像中,可能不存在任何常見的二進(jìn)制文件,而nsexec 能夠應(yīng)對這種情況。
具體注入實現(xiàn)的方法
現(xiàn)在 Chaos Mesh 已經(jīng)擁有了非常豐富的功能,受限于篇幅不可能單獨介紹每一種注 入的實現(xiàn),更重要的可能是注入實現(xiàn)背后的方法和路徑。在設(shè)計一個錯誤注入的實現(xiàn)時 ,依照以下的流程進(jìn)行考慮被證實是非常有用的:
考慮正常情況下程序工作的方式 在正常的調(diào)用途徑中有哪些可注入的地方 對這些可注入的地方進(jìn)行注入
以下我們以時間偏移為例,照著這種思考范式來設(shè)計它的實現(xiàn):
考慮正常情況下程序工作的方式
正常情況下,一個程序是如何獲得時間的呢?大部分程序會選擇使用編程語言
標(biāo)準(zhǔn)庫中攜帶的函數(shù),比如 Rust 程序會使用Instant::now(), Go 程序會使用time.Now() ,C 生態(tài)的程序會使用glibc 中的 clock_gettime 函數(shù)。而這些函數(shù)都會以 vDSO 的方式調(diào)用操作系統(tǒng)提供的 clock_gettime。vDSO 也是由操作系統(tǒng)提供的函數(shù)功能。但與系統(tǒng)調(diào)用不同,vDSO 由操作系統(tǒng)
在進(jìn)程啟動的時候自動地加載入進(jìn)程的內(nèi)存空間,格式與動態(tài)鏈接庫的 ELF 格
式相同。應(yīng)用程序只需要解析這段 ELF 的一些段,就能找到clock_gettime 函數(shù)
并調(diào)用它。
在正常的調(diào)用途徑中有哪些可注入的地方?因為 vDSO 是存在于目標(biāo)進(jìn)程的內(nèi)存空間中的,如果我們能夠修改這部分內(nèi)存 空間就好了。而事實上
ptrace系統(tǒng)調(diào)用給了我們修改另一進(jìn)程的內(nèi)存的能力。對這些可注入的地方進(jìn)行注入 于是時間偏移的實現(xiàn)方式呼之欲出——只需要準(zhǔn)備好一個有偏移的
clock_gettime的實現(xiàn),用它覆蓋住原有的clock_gettime函數(shù)的實現(xiàn)就好了。
使用類似的方法,也同樣能夠破解其他類型注入的難題——比如對于文件系統(tǒng)的注入
方式,我們可以通過 FUSE 提供一個存在延遲的文件系統(tǒng),而這一文件系統(tǒng)以真實的磁
盤上的文件系統(tǒng)為存儲后端即可(這樣用戶在恢復(fù)之后仍然能操作這些文件)。
以上為這篇文章介紹的全部內(nèi)容了。如果讀者對 Chaos Mesh 項目感興趣,想要成為用戶或 貢獻(xiàn)者,歡迎加入我們的 Slack Channel (CNCF Slack 的 #project-chaos-mesh 頻道) 來一同 討論如何應(yīng)用混沌工程、如何讓 Chaos Mesh 變得更好。構(gòu)造一個穩(wěn)定安全的軟件服務(wù)使用 環(huán)境,離不開社區(qū)的幫助。無論是使用 Chaos Mesh 還是為 Chaos Mesh 提交代碼,相信都是 在邁向這一美好的目標(biāo)。
— 往期回顧 —
Go 開源說第七期:Harbor助你玩轉(zhuǎn)云原生

Go 開源說第五期:MOSN Go語言網(wǎng)絡(luò)代理軟件

Go 開源說第四期(下):go-zero緩存管理最佳實踐

