Categories
程式開發

混沌工程:一年間重啟5000台機器後學到的7個經驗


頭號工程的誕生

在公司”核心產品看穩定性,通用產品看豐富度“的指導思想下,短短一年多的時間,我們對外提供的近200個產品中,公測產品佔比超過30%,這還不算處於內測和孵化期的產品。

產品豐富度是上去了,但大家都擔心一個問題,在這麼短的時間內,上線如此多的產品,質量是否能夠做到最好?外加2018年,公有云廠商發生了至少9次較為嚴重的故障,於是,混沌工程作為提升產品質量的頭號工程,便應運而生。

通過業界標杆確立演進路線

在混沌工程的開始,我們主要參考的是混沌工程的鼻祖Netflix公司,加之Netflix是基於公有云進行的部署,對公有云廠商來講,具有很強的借鑒和推廣意義,因此標杆就這麼愉快的確定了。在確立標杆的同時,我們也持續跟進著業內其他廠商的實踐經驗,避免走太多的彎路。

在對業界廠商的混沌工程落地經驗進行多次復盤後,我們總結出適合於自身現狀的演進路線,分為以下六個階段。這部分我們已經在混沌工程系列的第一篇文章中進行了介紹,詳情參考文章《混沌工程落地的六個階段》。

混沌工程:一年間重啟5000台機器後學到的7個經驗 1

單機破壞進展緩慢

開始做單機破壞的時候,結合Simian Army的功能點和模塊數量較多的情況,我們也做了最壞的打算,通過半年左右的時間把單機問題徹底消滅,事實證明,我們還是too young too simple ,在歷時一年之後,我們也僅僅是解決了單機破壞中關機和重啟兩個場景的絕大部分問題,並開始進行單機房破壞和依賴治理。

讓我們回到Netflix落地混沌工程的時間線,看看我們遇到的問題是否屬於個例。

  • Netflix首次公開Chaos Monkey在2010.12.16,參見此處

  • Netflix首次公開Simian Army在2011.07.19,參見此處

  • Netflix首次公開故障注入測試(FIT)則是在2014.10.23,參見此處

從上述的時間點,看到從一個猴子到一堆猴子,只需要半年,但一堆猴子依然是單機維度的,僅有Simian Army中的Chaos Gorilla是做AZ故障模擬的,類似於文中的單機房場景。但從單機維度到FIT,則足足經過了三年時間。隨著技術的發展,我們在今天重新開始混沌工程的時候,所面對的基礎架構要遠好於Netflix在2010年的情況,同時還有很多業界的分享和實踐,讓我們少走許多彎路,這也是我們得以在一年內做到前人三年工作的原因吧。

單機破壞進展緩慢的原因分析

下圖是我們對單機破壞過程中出現的92個問題的原因分析,在這裡,我們主要對前十類問題進行說明,畢竟同類問題多,原因歸類相對能準確一些。

需要明確的是,下述的問題包含如下破壞場景:

  • 單機重啟

  • 單個機房30%的服務器重啟

  • 單個機房100%的服務器重啟

混沌工程:一年間重啟5000台機器後學到的7個經驗 2

預發環境,是單機破壞問題最多的環節,也是投入時間最長的環節。為避免直接在線上做單機破壞可能出現的風險,我們必須在預發環境確保多次重啟沒有問題後,才能進行線上操作。因此,環境導致的阻塞問題,只能硬著頭皮去解決。以下是預發環境出現高頻問題的羅列:

  • 預發環境套數不足,混沌工程破壞演練和業務線的系統集成測試是互斥的,如果有業務線在單機破壞期間進行上線變更操作,可能會導致非預期的問題,耗費較長的時間去定位;

  • 預發環境的物理機過保比例較高,重啟後經常會出現硬件故障,大部分時候的硬件故障需要搬遷替換機器來解決,因此耗時較高;

  • 服務器的基礎配置不一致,如DNS、NTP、YUM以及部分內核參數的配置,以NTP為例,如果儀錶盤所在節點的時鐘比標準時間快一天,那麼查詢近期數據的結果就全為空,原以為是服務重啟後異常,忙活半天發現是NTP不同步導致。類似的DNS的緩存時間線上線下不一致,DNS切換後久久不生效新IP,或者生效IP的速度相差較大,也帶來很多困擾;

  • 部分實例異常導致的功能偶發性故障,會耗費較長的時間去定位,如配置錯誤,版本不一致,實例掛掉等,這和很多業務在線下環境沒有監控,沒有儀錶盤,報警被屏蔽也有很大關係;

  • 部分實例分佈不均,如三副本在同一個機器或者同一個機架下,這時候就需要協調資源進行服務的遷移;

  • 線上和線下依賴服務的不一致,如開源軟件版本不一致,線下的依賴服務是刪減版等,也會導致一些問題無法在線下復現;

服務啟停,因為沒有統一的方案,我們面對的服務來自於上千研發同學開發的模塊,也有很多開源軟件,大家的解法各不相同,因此這裡的坑也格外多:

  • 重啟後出現多進程的;

  • 啟動腳本中需要拉起多個服務,部分服務未成功拉起的;

  • 有臟數據導致重啟失敗,進入死循環無限次重試的;

  • 配置更新不完整,服務器重啟後,服務無法正常啟動;

  • 沒有設置在服務器重啟後自動拉起服務的;

  • Systemd腳本中存在Bug,導致進程沒有被自動拉起的;

  • NGINX存在無效域名,DNS無法解析,重啟機器後NGINX無法正常啟動;

  • 服務重啟時間達到3Min以上的;

  • 磁盤加載失敗導致服務啟動異常的;

  • 使用Systemd拉起服務後,並不知曉/etc/security/limits.conf的限制不會生效,而出現資源使用過量影響其他服務的;

單點,該部分主要存在於線下,但線上也有隱患,我們將單實例部署和單機房部署均置於單點問題之下,發現瞭如下問題:

  • 部分存儲節點的三副本放置於同一台物理機之下;

  • 部分存儲節點的三副本放置於同一個交換機之下;

  • 多個服務未在預發環境的AZ2機房部署,僅在AZ1機房進行部署;

  • 部分服務在每個AZ中僅部署一個實例;

  • 分配四台機器只部署了一台機器;

  • 依賴的底層LB/DNS/NTP服務僅在一個AZ部署;

  • Kafka做雙AZ部署,但其partition分佈不合理,導致部分數據失效;

  • 部分業務使用的下游提供的域名,僅包含單個AZ的機器列表;

  • 部分業務部署了兩個AZ,但LB上僅綁定了一個AZ1的機器,AZ2的設置為了冷備,不接流量;

狀態更新和容錯機制的一些問題:

  • 關閉AZ1後, worker 狀態未更新成功,導致創建任務下發到關機節點,創建任務無法執行;

  • 關閉AZ1後,Mysql的從庫掛掉,系統默認還在AZ1創建從庫,導致創建失敗;

  • 關閉AZ1後,自動觸發failover,但task表中重複插入了failover任務;

  • 關閉實例後,其狀態無法上報,導致控制端無法獲取其狀態,需要人工介入去修改狀態;

  • 關閉實例後,LB探測其狀態仍然屬於正常狀態,或者狀態未及時更新,導致流量受損;

  • etcd的一個節點處於非健康狀態時,其client通過etcd sdk獲取的返回值是500,因此繼續請求該節點;

主備切換的一些問題:

  • 有些新的業務預案情況是這樣的,沒有預案,停留在WIKI上,只有老人知道;

  • 有些新的業務的預案有腳本,需要臨時修改參數和配置,容易搞錯;

  • 有的同學沒有預案平台的權限,不能操作;

  • 有的同學對預案平台不熟悉,操作耗時較長;

  • 有的預案較長時間沒有使用,已經不再適配當前情況;

  • 部分業務的程序DNS緩存時間較長,執行DNS切換預案後,5-10min才能生效;

  • 部分業務無法選主,因為故障機房的實例比例偏大;

關聯關係的一些問題:

  • 在/etc/hosts中將部分域名的地址寫死;

  • 服務配置中寫死下游的IP地址;

  • 對於通過名字服務獲取的地址沒有進行存活性判斷;

上述的這些問題,對我們測試case豐富度也起到了巨大的幫助,從而避免新的業務繼續出現上述問題。

改進思路

仿真環境建設

仿真環境是按照是公有云線上Region來實施的,包含三個AZ,只是他的用戶,僅限於內部測試用途,不會暴露給公有云的對外客戶。考慮到實施成本,我們僅在一個IDC內部搭建了三個AZ區域,在外網接入和跨AZ的專線上有所簡化,除此之外,大部分都和線上完全相同,仿真度達到90 %以上。

因為有線下較多的用戶場景和業務需求,不僅滿足了流量豐富度的問題,而且集群也開始有專人維護,之前預發環境的種種問題,得到了極大改善,從此不在製約混沌工程的發展。

同時,仿真環境的硬件選型以及超售比,都更加激進,線下場景的使用成本也較之前降低了30%。最後,仿真環境的選址遠離在線機房的地方,讓一般業務無法容忍這種延時,並且進行了嚴格的網絡隔離,可以徹底避免線下和線上互聯的風險。

環境一致性維護

引入Puppet對所有線上和線下環境進行持續維護,這樣既可以確保關鍵配置項在各個環境中的一致性,同時Puppet也能將不符合要求的配置進行修復。基於此項能力的落地,類似於NTP問題,DNS問題,/etc/hosts問題等,都被徹底解決。

同時,取消研發和測試在仿真環境的操作權限,僅提供只讀權限賬號供其進行問題分析和定位,從而盡量減少研發和測試同學對仿真環境的非授權修改。

虛擬化和部署系統

首先是仿真環境不允許手工上線和手工修改服務配置,僅支持從部署系統進行發布。通過業務的虛擬化改造,既減少了對基礎環境的依賴,又能複用同一個鏡像來消除線上和線下服務配置的差異。

在部署系統中增加了多個約束策略,如禁止單實例和單機房部署,將一個服務的實例置於高可用組中從而避免部署在同一個機架下,部署系統僅接受來自於公司官方GIT的代碼,在操作系統的基準鏡像中內置Monit對服務進行啟停管理,必須部署完畢仿真環境才能夠進行線上發布等。

代碼掃描

通過部署系統約束GIT的源頭之後,在GIT中我們主要是增加了對關聯關係的靜態掃描,如是否包含IP地址,是否包含主機名地址,域名是否為公司內網域名,諸如此類的策略,從而在源頭將這些依賴關係的問題消滅掉。

服務啟停和啟動自檢

將服務啟停的方式收斂到Systemd方式,然後提供基於Systemd啟停方式的最佳實踐,包含重啟的最大次數,重啟的間隔,資源限制等,進程啟動前的啟動自檢,從而避免之前的各種異常導致的服務啟動失敗。

啟動自檢包括如下內容,如運行賬號是否正確,目錄權限是否正確,配置文件是否合法,資源限制是否生效,硬盤是否掛載,JDK版本是否正確,從而讓進程啟動在一個符合預期的環境中運行。

監控和儀錶盤

通過在仿真環境中建立儀錶盤,具備了對關鍵事件以及系統全貌的掌控能力,例如當前是否有上線和變更,核心功能是否正常(需要通過黑盒監控進行探測),哪些服務和實例有問題,存在哪些歷史報警和新增報警,當前的流量情況,資源使用率,誰登陸了哪個服務器執行了什麼命令,哪些系統文件被修改了等等。在出現問題後,通過儀錶盤,在進行單機破壞前,確認是否具備破壞條件,在單機破壞實施中,能夠快速定位問題、評估影響。

仿真環境的建設方向

仿真環境是混沌工程的起點而非終點,之所以投入較多的精力進行仿真環境的建設,是希望在線上做混沌工程的時候,盡量減少風險和影響。因此,仿真環境的擬真度會成為我們持續提升的一個目標。

如何徹底解決運行時環境的一致性問題呢?按照我們SaaS產品的實踐,一個docker走天下。從效果上看,就是docker鏡像不做任何更改,就可以同時在集成,測試,預發,仿真和線上環境上正常運行。舉一個簡單的例子,我們在公有云上開設兩個賬號,然後部署同一個程序,這時候,其關聯關係不論是IP,域名等,我們都可以保持完全相同,從而實現一個docker走天下的夢想。相信,不遠的將來,環境問題也許就不再是困擾大家的問題了。

參考文章:

混沌工程落地的六個階段

AWS 雲上混沌工程實踐之啟動篇

Netflix 繼續開源,更多猴子進入視野