建設(shè)七大策略:從監(jiān)控到混沌工程的運(yùn)維實(shí)踐)
1. 內(nèi)容整體設(shè)計(jì)與思路拆解1.1 高效運(yùn)維的本質(zhì)把“救火”變成“防火”干了這么多年運(yùn)維我一直覺得“高效運(yùn)維”這四個(gè)字被很多人誤解了。有人覺得運(yùn)維效率高就是腳本寫得溜、工單處理得快、半夜報(bào)警響應(yīng)及時(shí)這套說法聽起來熱血但本質(zhì)上還是在“救火”。真正的運(yùn)維效率是把功夫下在“防火”上——讓系統(tǒng)壓根就不出故障或者出了故障能自動恢復(fù)、能快速定位而不是靠人肉盯著屏幕輪番值班?!案呖捎孟到y(tǒng)”這個(gè)話題每個(gè)團(tuán)隊(duì)都在喊但真正落地的少。原因在于高可用不是一個(gè)開關(guān)打開就有了它是一整套工程體系的組合拳監(jiān)控、容量、架構(gòu)、發(fā)布、數(shù)據(jù)、故障演練、復(fù)盤改進(jìn)缺一環(huán)都不行。我這篇文章把這套體系收斂成七大核心策略不是拍腦袋想的而是這些年團(tuán)隊(duì)踩坑、復(fù)盤、優(yōu)化后沉淀下來的有效做法。寫這篇文章之前我先把讀者對象梳理了一遍既有剛?cè)胄袃扇?、想把系統(tǒng)穩(wěn)定性做扎實(shí)的初中級運(yùn)維工程師也有帶團(tuán)隊(duì)、需要從體系層面搭建高可用方案的Tech Lead。這兩類人的訴求不一樣前者需要“具體怎么做”后者需要“為什么這么做、整體怎么串起來”。我盡量在每一節(jié)里把實(shí)操和思路都覆蓋到方便不同基礎(chǔ)的讀者各取所需。1.2 七大策略的定位與整體協(xié)同關(guān)系在正式展開每一節(jié)之前我先把七大策略的整體邏輯擺出來方便大家有一個(gè)鳥瞰圖。策略模塊核心作用解決的核心問題章節(jié)號監(jiān)控與可觀測性系統(tǒng)狀態(tài)的“眼睛”故障發(fā)生時(shí)能否第一時(shí)間發(fā)現(xiàn)并定位2容量規(guī)劃與彈性伸縮系統(tǒng)的“呼吸能力”突發(fā)流量下能否扛住而不被打掛3冗余架構(gòu)與故障切換系統(tǒng)的“備用心臟”單點(diǎn)故障時(shí)服務(wù)能否繼續(xù)工作4自動化發(fā)布與變更管理系統(tǒng)的“穩(wěn)定方向盤”變更和發(fā)布能否不引入新的故障5數(shù)據(jù)可靠性與備份恢復(fù)系統(tǒng)的“記憶保險(xiǎn)柜”數(shù)據(jù)損壞或丟失時(shí)能否恢復(fù)6故障演練與混沌工程系統(tǒng)的“壓力測試儀”預(yù)案在真實(shí)故障面前是否有效7復(fù)盤機(jī)制與持續(xù)改進(jìn)系統(tǒng)的“進(jìn)化引擎”每一個(gè)故障能否轉(zhuǎn)化為系統(tǒng)能力的提升8這個(gè)結(jié)構(gòu)不是隨便排的。你可以把它理解成一個(gè)人體的健康體系監(jiān)控是感官神經(jīng)容量是心肺功能冗余是免疫系統(tǒng)備份自動化是肌肉記憶數(shù)據(jù)是大腦記憶演練是疫苗復(fù)盤是新陳代謝。任何一個(gè)器官出問題整個(gè)人都會垮。高可用系統(tǒng)的建設(shè)也是同理別指望靠某一項(xiàng)“神操作”一勞永逸。2. 高效運(yùn)維的生命線監(jiān)控體系與可觀測性建設(shè)2.1 從“看指標(biāo)”到“看故事”監(jiān)控的三個(gè)層級監(jiān)控這個(gè)話題老生常談但我發(fā)現(xiàn)很多團(tuán)隊(duì)的監(jiān)控還停留在“紅燈亮了才知道出事了”的階段。我自己的理解是監(jiān)控體系要分三個(gè)層級往上搭。第一層是基礎(chǔ)監(jiān)控CPU、內(nèi)存、磁盤、網(wǎng)絡(luò)這一層解決的是“機(jī)器死了沒有”的問題工具用Zabbix、Prometheus node_exporter都行。第二層是應(yīng)用監(jiān)控接口QPS、RT、錯(cuò)誤率、JVM指標(biāo)、慢SQL等這一層解決的是“服務(wù)活著但是否健康”的問題通常需要接入SkyWalking、Micrometer或者通過日志埋點(diǎn)實(shí)現(xiàn)。第三層是業(yè)務(wù)監(jiān)控訂單量、支付成功率、用戶登錄失敗率等這一層解決的是“用戶體驗(yàn)是否受損”的問題往往需要研發(fā)和運(yùn)維一起定義關(guān)鍵業(yè)務(wù)指標(biāo)。我見過太多團(tuán)隊(duì)在第二層和第三層基本是裸奔的。機(jī)器指標(biāo)一切正常但用戶已經(jīng)在報(bào)障說頁面打不開了為什么因?yàn)閼?yīng)用線程池被打滿了或者數(shù)據(jù)庫連接池耗盡這些是基礎(chǔ)監(jiān)控看不出來的。所以監(jiān)控建設(shè)一定要從“看機(jī)器”向“看應(yīng)用”“看業(yè)務(wù)”演進(jìn)。2.2 指標(biāo)采集到底該怎么做黃金四信號實(shí)戰(zhàn)Google SRE圈子里有個(gè)經(jīng)典說法叫“黃金四信號”延遲、流量、錯(cuò)誤、飽和度。這四個(gè)信號覆蓋了用戶可感知的絕大部分問題我強(qiáng)烈建議每個(gè)團(tuán)隊(duì)在搭建應(yīng)用監(jiān)控時(shí)優(yōu)先把這幾類指標(biāo)做全。以我這邊一個(gè)典型訂單服務(wù)的指標(biāo)定義為例延遲接口P50、P95、P99響應(yīng)時(shí)間其中P99最敏感尾延遲往往是系統(tǒng)劣化的先兆流量每秒請求數(shù)QPS、活躍連接數(shù)、網(wǎng)絡(luò)吞吐量錯(cuò)誤HTTP 5xx比例、業(yè)務(wù)錯(cuò)誤碼比例、RPC調(diào)用失敗率飽和度CPU使用率、內(nèi)存使用率、線程池活躍線程數(shù)、數(shù)據(jù)庫連接池使用率有些人會問為什么P99比平均值重要一個(gè)很形象的類比平均值是“全班平均水平”但只要有尖刺就會有一批用戶是“不及格”的。平均RT 100ms看著很健康但P99可能已經(jīng)到了2秒意味著1%的用戶正在忍受卡頓。在互聯(lián)網(wǎng)系統(tǒng)里這部分用戶很可能就是流失的那批人。采集和存儲選型上現(xiàn)在Prometheus Grafana Alertmanager基本是事實(shí)標(biāo)準(zhǔn)。如果你維護(hù)的是Kubernetes環(huán)境Prometheus Operator更是標(biāo)配。指標(biāo)保留策略通常是原始數(shù)據(jù)保留15天降精度數(shù)據(jù)保留6個(gè)月以上。別把所有數(shù)據(jù)都無限期存著成本扛不住也沒那個(gè)必要。2.3 日志與鏈路追蹤排障時(shí)的第二雙眼睛光有指標(biāo)還不夠指標(biāo)能告訴你“哪里出了問題”但很難告訴你“為什么出問題”。這時(shí)候就要靠日志和鏈路追蹤。日志這塊我強(qiáng)烈建議走ELK或者Loki這套路徑。ELKElasticsearch Logstash Kibana功能全、查詢能力強(qiáng)但資源消耗也大Loki是Grafana出品的輕量方案與Prometheus天然集成學(xué)習(xí)成本低。選哪個(gè)不重要重要的是必須集中采集別等到故障時(shí)逐臺登錄服務(wù)器看日志。鏈路追蹤方面SkyWalking是國內(nèi)用得比較多的一套開源免費(fèi)支持Java、Go、Python等多語言探針能清晰展示一次請求在網(wǎng)關(guān)、服務(wù)A、服務(wù)B、數(shù)據(jù)庫之間的耗時(shí)分布。接入鏈路追蹤的價(jià)值在排查跨服務(wù)故障時(shí)體現(xiàn)得最明顯用戶反饋一個(gè)功能很慢你通過Trace ID可以立刻定位到慢在哪一環(huán)——是下游接口慢了還是數(shù)據(jù)庫查詢慢了一次就能定位不用再像以前那樣靠猜。3. 容量規(guī)劃與彈性伸縮讓系統(tǒng)學(xué)會呼吸3.1 容量規(guī)劃不是“算命”是有方法論的成本控制很多團(tuán)隊(duì)的容量規(guī)劃只有三個(gè)字——“加機(jī)器”。流量漲了加機(jī)器雙十一了加機(jī)器加完發(fā)現(xiàn)沒流量了再下線。這樣做不能說錯(cuò)但效率極低成本也沒譜。真正高效的容量規(guī)劃得從“被動響應(yīng)”轉(zhuǎn)向“主動預(yù)測”。我推薦的做法是從三個(gè)維度做容量評估歷史基線過去30天/90天的業(yè)務(wù)峰值、增長趨勢、同比環(huán)比數(shù)據(jù)業(yè)務(wù)預(yù)測運(yùn)營活動排期、新功能上線計(jì)劃、市場投放節(jié)奏鏈路傳導(dǎo)單核心接口QPS變化會傳導(dǎo)到下游數(shù)據(jù)庫、緩存、MQ的調(diào)用量變化具體計(jì)算上有一個(gè)樸素但實(shí)用的公式預(yù)估峰值QPS 歷史日均QPS × 突發(fā)系數(shù)建議2~4視業(yè)務(wù)而定 × 預(yù)留冗余系數(shù)1.5左右如果你們系統(tǒng)歷史日均QPS是5000突發(fā)系數(shù)取3冗余系數(shù)取1.5那預(yù)估峰值就是22500 QPS。然后根據(jù)單機(jī)壓測數(shù)據(jù)——比如單機(jī)能扛1000 QPS——就能粗略算出至少需要23臺實(shí)例。這只是入口QPS下游DB和緩存還要單獨(dú)算。3.2 彈性伸縮的具體落地HPA與定時(shí)伸縮有了容量預(yù)估下一步就是“怎么把資源用起來又不會浪費(fèi)”。彈性伸縮是解決這個(gè)問題的核心手段。在Kubernetes環(huán)境里HPAHorizontal Pod Autoscaler是最常用的方案。配置其實(shí)不復(fù)雜核心是指標(biāo)選擇與閾值設(shè)定。我以一個(gè)Web服務(wù)為例給個(gè)參考配置apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: order-service-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: order-service minReplicas: 3 maxReplicas: 30 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 65 - type: Resource resource: name: memory target: type: Utilization averageUtilization: 75注意幾個(gè)關(guān)鍵點(diǎn)第一個(gè)是目標(biāo)利用率不要設(shè)得太低比如50%以下會導(dǎo)致擴(kuò)容過于靈敏流量一抖就瘋狂擴(kuò)成本飆升第二個(gè)是minReplicas必須保證大于等于2這是高可用的底線第三個(gè)是HPA生效有延遲從指標(biāo)采集到擴(kuò)容完成通常需要3~5分鐘所以對突發(fā)行流量HPA往往是來不及的。對于可預(yù)期的流量高峰——比如電商大促、秒殺活動、每周一早晨的集中訪問——我建議直接用定時(shí)伸縮方案。KEDA這個(gè)開源項(xiàng)目支持Cron觸發(fā)器可以設(shè)定每天10:00自動擴(kuò)容到20個(gè)Pod22:00再縮回來這種“預(yù)知未來”的擴(kuò)容方式比HPA更主動也更省錢。3.3 別忽略容量陷阱那些“看不見”的瓶頸容量規(guī)劃最常見的坑是只盯著應(yīng)用實(shí)例忽視了鏈路上下游的容量瓶頸。真實(shí)案例是這樣的某團(tuán)隊(duì)給前端應(yīng)用加了20個(gè)Pod結(jié)果數(shù)據(jù)庫連接池被打滿數(shù)據(jù)庫CPU飆到100%整個(gè)服務(wù)直接雪崩。容量規(guī)劃必須“端到端”地做入口網(wǎng)關(guān)、應(yīng)用層、緩存層、消息隊(duì)列、數(shù)據(jù)庫每一層的容量和限流閾值都要評估。數(shù)據(jù)庫這塊我建議壓測時(shí)重點(diǎn)關(guān)注連接數(shù)和慢查詢數(shù)量。連接數(shù)一旦打滿應(yīng)用層會拿不到連接表現(xiàn)為接口RT飆升、報(bào)獲取連接超時(shí)。而慢查詢的殺傷力更隱蔽一條慢SQL能把CPU打滿把整個(gè)庫拖垮。所以容量規(guī)劃不僅要看流量峰值還要同步推動研發(fā)把慢SQL治理掉這是“降本增效”的一個(gè)暗招。4. 冗余架構(gòu)與故障切換讓單點(diǎn)不再是單點(diǎn)4.1 單點(diǎn)故障的高可用設(shè)計(jì)原則高可用架構(gòu)的核心原則就一句話任何一臺機(jī)器、任何一個(gè)組件掛掉系統(tǒng)都要能繼續(xù)運(yùn)行。反過來說凡是存在單點(diǎn)的地方就是系統(tǒng)的命門。以常規(guī)的微服務(wù)部署為例至少要做到這套底線應(yīng)用層至少2個(gè)副本分布在不同的物理機(jī)/可用區(qū)負(fù)載均衡至少2個(gè)節(jié)點(diǎn)前面用VIP或DNS輪詢數(shù)據(jù)庫主從復(fù)制至少1個(gè)從庫主庫故障時(shí)可切換緩存采用集群模式如Redis Sentinel或Redis Cluster消息隊(duì)列集群部署至少3個(gè)節(jié)點(diǎn)可能有人覺得“我們系統(tǒng)沒那么重要掛了也沒什么”但我想說一個(gè)殘酷的事實(shí)高可用不是大廠的專利任何線上系統(tǒng)對用戶來說都是“100%或0%”的體驗(yàn)差距。你半夜掛了一個(gè)小時(shí)可能白天就有一批用戶流失了。寧可平時(shí)多花點(diǎn)成本維護(hù)冗余也不要在關(guān)鍵時(shí)刻賭運(yùn)氣。4.2 數(shù)據(jù)庫高可用的主流選型與切換機(jī)制數(shù)據(jù)庫是整個(gè)系統(tǒng)里最脆弱也最重要的一環(huán)。我以MySQL為例講講幾種高可用方案。最簡單的是主從復(fù)制 MHAMaster High Availability自動切換。MHA能在主庫故障后30秒內(nèi)完成從庫提升和VIP漂移成本低、部署簡單適合中小團(tuán)隊(duì)。缺點(diǎn)是切換過程中可能會有少量數(shù)據(jù)丟失取決于半同步復(fù)制是否開啟同時(shí)從庫要承擔(dān)讀流量的話切主后需要重新評估負(fù)載。再進(jìn)階一點(diǎn)是MySQL InnoDB Cluster基于Group Replication內(nèi)置了故障檢測和自動切換能力配合MySQL Router可以實(shí)現(xiàn)應(yīng)用無感知的主從切換。這個(gè)方案的優(yōu)點(diǎn)是官方支持、運(yùn)維相對省心缺點(diǎn)是對版本和配置有要求需要一定的學(xué)習(xí)成本。如果是金融級場景數(shù)據(jù)零丟失是硬指標(biāo)那就要上“兩地三中心”這類容災(zāi)架構(gòu)了。這種方案比較復(fù)雜需要結(jié)合業(yè)務(wù)重要性和預(yù)算來權(quán)衡但至少你要理解數(shù)據(jù)一致性要求越高高可用的實(shí)現(xiàn)成本就越高這是繞不開的。4.3 故障切換的常見誤區(qū)和切換后的“二次災(zāi)害”故障切換做得好不好不是看切換多快而是看切換后系統(tǒng)是否穩(wěn)定。我見過不少團(tuán)隊(duì)主庫一掛腳本自動切到從庫以為萬事大吉結(jié)果發(fā)現(xiàn)從庫只有一半的數(shù)據(jù)業(yè)務(wù)邏輯大面積報(bào)錯(cuò)。為什么因?yàn)閺膸觳⒉灰欢ㄗ飞现鲙斓淖钚氯罩緩?qiáng)制切換之后丟數(shù)據(jù)是難免的。這類問題的解法一是開啟半同步復(fù)制降低主從切換時(shí)的數(shù)據(jù)丟失概率二是在切換前由腳本自動檢查從庫的Seconds_Behind_Master如果延遲過大寧可報(bào)警等人工決策也不要自動切換。自動化不是目的自動化之后依然安全才是目的。另外還要提防故障切換引發(fā)的“二次災(zāi)害”主庫掛了流量全部打到從庫從庫瞬間被讀流量壓垮或者切完后應(yīng)用連接池還持有舊主庫的連接導(dǎo)致大量報(bào)錯(cuò)。針對前者切換前一定要評估從庫容量必要時(shí)先限流針對后者應(yīng)用側(cè)連接池要配置自動重連或者切換腳本里先重啟應(yīng)用實(shí)例讓連接池重建。5. 自動化發(fā)布與變更管理變更是故障的最大源頭5.1 為什么發(fā)布和變更最危險(xiǎn)根據(jù)我自己的統(tǒng)計(jì)線上故障中有相當(dāng)高的比例是由變更觸發(fā)的——發(fā)版本、改配置、擴(kuò)縮容、數(shù)據(jù)庫變更每一樣都可能在瞬間把系統(tǒng)搞掛。不是代碼寫得爛而是“變更”這個(gè)動作本身就意味著系統(tǒng)在從一種狀態(tài)向另一種狀態(tài)遷移遷移的中間態(tài)是最脆弱的。所以高可用體系里變更管理不是流程負(fù)擔(dān)而是保命環(huán)節(jié)。你把發(fā)布控制好了系統(tǒng)穩(wěn)定性就贏了一半。5.2 一套可落地的灰度發(fā)布方案我推薦在團(tuán)隊(duì)里推動“灰度發(fā)布 快速回滾”的發(fā)布模式這是效率和風(fēng)險(xiǎn)的平衡點(diǎn)。具體分三步第一步小流量驗(yàn)證把新版本部署到1%的實(shí)例上讓少量真實(shí)流量過一遍觀察核心指標(biāo)是否有異常。如果異常指標(biāo)超過閾值立即觸發(fā)自動回滾。第二步逐步放量5%→20%→50%→100%每一檔停留一段時(shí)間觀察錯(cuò)誤率和延遲曲線沒有異常再繼續(xù)。第三步全量發(fā)布后觀察至少再盯30分鐘黃金指標(biāo)確保沒有延遲性問題。這里給一個(gè)很實(shí)用的下沉部署方案——在Kubernetes里通過Service的標(biāo)簽選擇器控制流量權(quán)重。假設(shè)你有兩個(gè)Deployment一個(gè)穩(wěn)定版一個(gè)金絲雀版可以通過如下方式逐步調(diào)整Service后端的Pod數(shù)量比例# 穩(wěn)定版 99 個(gè)副本金絲雀版 1 個(gè)副本即 1% 流量打到新版本 kubectl scale deployment order-service-stable --replicas99 kubectl scale deployment order-service-canary --replicas1 # 觀察無異常后調(diào)整比例 kubectl scale deployment order-service-canary --replicas20這種方式不需要額外引入Istio這類服務(wù)網(wǎng)格純粹利用Kubernetes原生能力清爽直接。如果你的團(tuán)隊(duì)用Spring Cloud那可以借助Nacos或Eureka的權(quán)重配置來調(diào)節(jié)流量比例原理相似。5.3 發(fā)布過程中的“人的因素”自動化發(fā)布推進(jìn)過程中最大的障礙往往不是技術(shù)而是人的習(xí)慣。有些老工程師習(xí)慣于“登錄服務(wù)器、拉代碼、重啟服務(wù)”覺得這樣最快。但從高可用的角度這種方式極度危險(xiǎn)人為操作容易漏步驟、難審計(jì)、不可回滾一旦出問題連操作記錄都很難還原。我建議團(tuán)隊(duì)用Jenkins或GitLab CI/CD把發(fā)布流程做成模板所有發(fā)布必須走流水線。流水線里強(qiáng)制包含編譯構(gòu)建→單元測試→鏡像構(gòu)建與掃描→部署到測試環(huán)境→自動化冒煙測試→灰度環(huán)境部署→生產(chǎn)發(fā)布。每一步失敗都會中止后續(xù)步驟發(fā)布結(jié)果有據(jù)可查。推行初期會有人抵觸但堅(jiān)持兩三個(gè)月后大家會發(fā)現(xiàn)“標(biāo)準(zhǔn)化的發(fā)布”反而更省事因?yàn)槌鲥e(cuò)概率大幅下降了。6. 數(shù)據(jù)可靠性與備份恢復(fù)高可用的最后防線6.1 備份的本質(zhì)是“可恢復(fù)”而不是“有備份”很多團(tuán)隊(duì)有一個(gè)致命誤區(qū)以為做了備份就等于數(shù)據(jù)安全了。但備份不經(jīng)過恢復(fù)演練約等于沒有備份。我見過一個(gè)典型案例某團(tuán)隊(duì)每天自動備份數(shù)據(jù)庫文件都完整地存在對象存儲里大家都覺得萬無一失。后來機(jī)房斷電導(dǎo)致磁盤損壞恢復(fù)時(shí)才發(fā)現(xiàn)的備份文件從某天起因?yàn)闄?quán)限變更不再更新而恢復(fù)流程也因?yàn)槿鄙傥臋n沒人會操作。最后只能依賴遠(yuǎn)程異地副本多花了十幾個(gè)小時(shí)才恢復(fù)業(yè)務(wù)損失嚴(yán)重。把這句話刻在腦子里備份的驗(yàn)收標(biāo)準(zhǔn)不是“備份任務(wù)成功”而是“能從備份中成功恢復(fù)出完整數(shù)據(jù)”。6.2 MySQL的備份恢復(fù)實(shí)操指南以MySQL為例我推薦“全量備份 binlog增量”的組合策略。全量備份每天一次使用物理備份工具如Percona XtraBackup因?yàn)槲锢韨浞荼萴ysqldump邏輯備份快得多尤其適合上GB級的數(shù)據(jù)量。binlog增量則通過開啟MySQL的binlog并設(shè)置合適的保留天數(shù)通常至少7天來實(shí)現(xiàn)。每次備份后我強(qiáng)烈建議做一次“備份有效性檢查”自動將備份文件恢復(fù)到一臺臨時(shí)實(shí)例上執(zhí)行幾個(gè)關(guān)鍵表的COUNT(*)校驗(yàn)對比源庫的行數(shù)是否一致。這個(gè)檢查要寫進(jìn)腳本每天自動跑失敗就報(bào)警。這算是運(yùn)維里性價(jià)比最高的一道保險(xiǎn)了?;謴?fù)演練至少一個(gè)季度做一次。我這邊幾個(gè)關(guān)鍵數(shù)據(jù)庫的恢復(fù)時(shí)長參考單實(shí)例500GB數(shù)據(jù)量從備份和binlog恢復(fù)到故障前時(shí)間點(diǎn)目標(biāo)RTO控制在4小時(shí)內(nèi)。如果你們團(tuán)隊(duì)還沒做過一次真正的恢復(fù)演練我建議從這個(gè)季度開始補(bǔ)上。6.3 數(shù)據(jù)變更的安全底線先備份再操作除了日常備份還有一類高危操作需要單獨(dú)防護(hù)開發(fā)或DBA直接在線上執(zhí)行DELETE、UPDATE、ALTER TABLE等變更。這類操作一旦寫錯(cuò)條件比如忘加WHERE那幾條SQL就能把整張表清空而且很難閃回。我的經(jīng)驗(yàn)是給這類操作加三道鎖第一道操作前必須對目標(biāo)表做一次快速備份比如用mysqldump只導(dǎo)出該表的數(shù)據(jù)保存到安全位置第二道操作必須在自動化平臺上執(zhí)行平臺會做語法檢查、影響行數(shù)預(yù)估、超時(shí)機(jī)制第三道高危語句如無WHERE的DELETE/UPDATE必須經(jīng)過雙人審批才能執(zhí)行第三道這道防線尤其重要。有一次我們團(tuán)隊(duì)來了個(gè)實(shí)習(xí)生在測試環(huán)境模擬數(shù)據(jù)清理寫了一條沒有WHERE的DELETE如果不是平臺攔截他差點(diǎn)在測試庫把整個(gè)用戶表清空。測試環(huán)境也就罷了生產(chǎn)環(huán)境一旦發(fā)生可能就得靠備份來“擦屁股”了。7. 故障演練與混沌工程在平時(shí)準(zhǔn)備好戰(zhàn)時(shí)的預(yù)案7.1 故障演練為什么不可或缺有一個(gè)很扎心的規(guī)律沒演練過的故障預(yù)案在真實(shí)故障發(fā)生時(shí)大概率是不好用的。原因很簡單預(yù)案是“寫出來的想象”而真實(shí)故障充滿了意外——網(wǎng)絡(luò)分區(qū)、磁盤慢IO、內(nèi)存泄漏、證書過期——每一種故障都不會按照你寫的預(yù)案發(fā)生。所以高可用體系的第七大策略就是“平時(shí)多流汗戰(zhàn)時(shí)少流血”。通過主動制造故障、演練整個(gè)團(tuán)隊(duì)的處理流程你會發(fā)現(xiàn)很多平時(shí)根本注意不到的坑。7.2 混沌工程實(shí)操從“殺一個(gè)Pod”開始混沌工程聽起來高大上實(shí)際可以從小處著手。我建議剛接觸混沌工程的團(tuán)隊(duì)從Kubernetes環(huán)境里的基礎(chǔ)故障注入開始。最基礎(chǔ)的一步手動殺一個(gè)Pod驗(yàn)證自愈能力。# 刪除一個(gè)業(yè)務(wù)Pod觀察Kubernetes是否自動重建 kubectl delete pod order-service-xxxxx正常情況下Deployment控制器會在幾秒內(nèi)拉起一個(gè)新Pod。在這個(gè)演練中你需要觀察的第一件事是整個(gè)過程中服務(wù)是否持續(xù)可用如果服務(wù)有多個(gè)副本且負(fù)載均衡工作正常用戶應(yīng)該無感知。如果發(fā)現(xiàn)某個(gè)副本被殺后開始出現(xiàn)請求失敗說明你的負(fù)載均衡配置或者Pod優(yōu)雅終止設(shè)置有問題需要立刻修復(fù)。再進(jìn)一步引入Chaos Mesh或者Litmus這類工具做自動化故障注入。Chaos Mesh是CNCF項(xiàng)目支持Pod故障、網(wǎng)絡(luò)延遲、網(wǎng)絡(luò)丟包、磁盤故障等多種注入場景。配置一個(gè)網(wǎng)絡(luò)延遲的例子apiVersion: chaos-mesh.org/v1alpha1 kind: NetworkChaos metadata: name: delay-order-service spec: action: delay mode: one selector: namespaces: - production labelSelectors: app: order-service delay: latency: 2000ms jitter: 500ms duration: 5m這個(gè)配置會讓order-service的請求延遲2秒持續(xù)5分鐘模擬網(wǎng)絡(luò)抖動的場景。注入之后重點(diǎn)觀察下游調(diào)用方是否觸發(fā)了超時(shí)重試、熔斷降級以及系統(tǒng)從故障中恢復(fù)的速度。7.3 故障演練的“度”與實(shí)施節(jié)奏混沌工程要講分寸別一上來就把生產(chǎn)環(huán)境搞崩。我建議節(jié)奏是這樣的先在測試環(huán)境驗(yàn)證所有故障注入工具和用例先在低峰期做演練確認(rèn)無誤后再嘗試高峰期演練從影響面小的場景開始如殺一個(gè)Pod逐步升級到網(wǎng)絡(luò)隔離、節(jié)點(diǎn)宕機(jī)等大故障每次演練前必須有明確的回滾方案演練過程中如果發(fā)現(xiàn)無法控制立即終止有人會擔(dān)心混沌工程會不會“沒病找病”我的理解是如果你不主動找病病就會在你最忙的時(shí)候找上你。與其在業(yè)務(wù)大促當(dāng)天被動遭遇故障不如在一個(gè)可控的時(shí)間點(diǎn)主動暴露問題、修復(fù)問題。8. 復(fù)盤機(jī)制與持續(xù)改進(jìn)讓每一次故障都成為系統(tǒng)進(jìn)化的契機(jī)8.1 復(fù)盤的目的是“改進(jìn)系統(tǒng)”不是“追責(zé)個(gè)人”故障復(fù)盤在很多團(tuán)隊(duì)里變了味變成了“誰的問題、誰背鍋、誰寫檢查”。這種文化下沒有人愿意如實(shí)上報(bào)故障細(xì)節(jié)隱藏的問題越來越多系統(tǒng)就越發(fā)脆。高效的運(yùn)維團(tuán)隊(duì)復(fù)盤文化一定是“對事不對人”的。我在團(tuán)隊(duì)里推行復(fù)盤時(shí)會強(qiáng)調(diào)一個(gè)原則如果一個(gè)故障是因?yàn)槿说氖韬鰧?dǎo)致的那一定是流程或工具讓人覺得“疏忽也沒關(guān)系”。所以復(fù)盤的關(guān)注點(diǎn)不是某個(gè)人做錯(cuò)了什么而是系統(tǒng)在哪一環(huán)缺失了防護(hù)流程在哪一步缺了檢查。8.2 一份實(shí)用的復(fù)盤報(bào)告模板復(fù)盤報(bào)告不用寫得很長但要覆蓋核心要素。我常用的模板結(jié)構(gòu)是這樣的模塊關(guān)鍵內(nèi)容故障時(shí)間線從故障發(fā)生到恢復(fù)的完整時(shí)間線精確到分鐘影響范圍哪些服務(wù)、哪些用戶、持續(xù)多久、有沒有數(shù)據(jù)丟失根因分析用5Why法或FTA法找出真正的根因不止停留在直接原因觸發(fā)條件什么條件下會觸發(fā)是否有前兆信號修復(fù)動作短期止血措施和長期修復(fù)措施分開列行動清單每項(xiàng)措施指定責(zé)任人、截止日期、驗(yàn)收標(biāo)準(zhǔn)這里尤其想講講5Why法。比如故障表面原因是“磁盤滿了”繼續(xù)問為什么磁盤滿了——因?yàn)槿罩疚募鬄槭裁慈罩疚募蟆驗(yàn)闆]有日志輪轉(zhuǎn)策略為什么沒有日志輪轉(zhuǎn)策略——因?yàn)椴渴饡r(shí)沒有把日志輪轉(zhuǎn)納入標(biāo)準(zhǔn)化配置。這樣追問五層才能真正觸及需要改的根子。8.3 復(fù)盤后的“閉環(huán)動作”復(fù)盤的最終價(jià)值體現(xiàn)在行動上。每一次復(fù)盤結(jié)束后我會要求團(tuán)隊(duì)輸出一個(gè)“穩(wěn)定性改進(jìn)項(xiàng)清單”并且把它納入下一迭代的開發(fā)計(jì)劃。沒有行動的復(fù)盤等于白做。舉一個(gè)我們自己的例子某次故障是因?yàn)橐粋€(gè)Redis實(shí)例內(nèi)存滿了導(dǎo)致緩存失效流量全部打到數(shù)據(jù)庫數(shù)據(jù)庫負(fù)載過高。復(fù)盤后我們落地了三個(gè)改進(jìn)項(xiàng)一是給Redis加告警內(nèi)存使用率超過80%就觸發(fā)二是給數(shù)據(jù)庫連接池配置了最大等待時(shí)間和快速失敗機(jī)制三是為緩存增加多級降級方案Redis不可用時(shí)可以降級到本地緩存。三個(gè)改進(jìn)項(xiàng)在一周內(nèi)完成開發(fā)上線之后同類故障再也沒有復(fù)發(fā)過。9. 最后分享一點(diǎn)個(gè)人體會高可用系統(tǒng)的建設(shè)不是“項(xiàng)目制”不能指望等架構(gòu)師畫完圖、運(yùn)維部署完工具系統(tǒng)就自動高可用了。它更像是一種持續(xù)運(yùn)作的機(jī)制監(jiān)控發(fā)現(xiàn)薄弱點(diǎn)→容量和架構(gòu)補(bǔ)強(qiáng)→發(fā)布流程防患→數(shù)據(jù)備份兜底→演練驗(yàn)證效果→復(fù)盤沉淀經(jīng)驗(yàn)→再發(fā)現(xiàn)新的薄弱點(diǎn)。這七個(gè)策略不是獨(dú)立的銀彈而是互相咬合的齒輪只有一起轉(zhuǎn)起來系統(tǒng)的穩(wěn)定性才會穩(wěn)步提升。在這個(gè)行業(yè)里待得越久我越覺得運(yùn)維的成就感不是“處理了多少故障”而是“系統(tǒng)平靜得有點(diǎn)無聊”——一切自動化在穩(wěn)穩(wěn)運(yùn)行監(jiān)控大屏一片綠色用戶無感、業(yè)務(wù)正常。這種“無聊”才是高效運(yùn)維真正該追求的狀態(tài)。希望這篇文章對你有所啟發(fā)也歡迎在評論區(qū)聊聊你在高可用建設(shè)中踩過的坑。