點(diǎn) etcd 分裂與網(wǎng)絡(luò)分區(qū)下的控制面自愈測(cè)試)
在現(xiàn)代化云原生高可用架構(gòu)的藍(lán)圖中絕大多數(shù)企業(yè)都將生產(chǎn)級(jí) Kubernetes 集群的控制面Control Plane設(shè)計(jì)為跨三個(gè)獨(dú)立機(jī)房或可用區(qū)部署的奇數(shù)節(jié)點(diǎn)架構(gòu)例如 3 節(jié)點(diǎn)或 5 節(jié)點(diǎn)多 Master 高可用集群。在設(shè)計(jì)之初架構(gòu)師們普遍堅(jiān)信一個(gè)理論前提基于 Raft 共識(shí)協(xié)議的 etcd 集群天生具備容忍少數(shù)派故障的能力即便掛掉一到兩臺(tái)控制節(jié)點(diǎn)整個(gè)集群依然能堅(jiān)如磐石地正常運(yùn)轉(zhuǎn)。然而分布式系統(tǒng)的殘酷之處在于真實(shí)的物理故障極少呈現(xiàn)為“一臺(tái)機(jī)器優(yōu)雅地徹底斷電”更多的是由于網(wǎng)絡(luò)交換機(jī)故障、跨機(jī)房專線單向抖動(dòng)或安全組誤配置所導(dǎo)致的“惡性網(wǎng)絡(luò)分區(qū)Network Partition與不對(duì)稱孤島”。當(dāng)集群控制面被無情切分為多個(gè)無法互通的孤立子集群時(shí)系統(tǒng)是否會(huì)發(fā)生毀滅性的“腦裂Split-Brain”在多數(shù)派重新選舉出新 Leader 的驚險(xiǎn)窗口期內(nèi)殘存的少數(shù)派節(jié)點(diǎn)是否會(huì)向客戶端返回陳舊或損壞的狀態(tài)數(shù)據(jù)而在網(wǎng)絡(luò)分區(qū)終于愈合、兩個(gè)分裂的世界重新連通的瞬間積壓的事務(wù)日志重放是否會(huì)誘發(fā)二次雪崩為了徹底驗(yàn)明真?zhèn)挝覀儾荒芡A粼诶碚撏茖?dǎo)的安樂窩里。本文將詳細(xì)記錄我們?nèi)绾瓮ㄟ^**混沌工程工具Chaos Mesh / iptables 底層流控**在生產(chǎn)鏡像集群中實(shí)施一場(chǎng)真實(shí)的控制面網(wǎng)絡(luò)分區(qū)腦裂演練并給出深水區(qū)集群的自愈加固方案。etcd 共識(shí)機(jī)制與控制面依賴的微觀博弈要設(shè)計(jì)高仿真的腦裂演練必須首先透視 Kubernetes 控制面在發(fā)生網(wǎng)絡(luò)切分時(shí)內(nèi)部各大核心組件的物理行為鏈條Raft 多數(shù)派仲裁法則Quorum Rule對(duì)于包含 $2F1$ 個(gè)節(jié)點(diǎn)的 etcd 集群其法定多數(shù)派要求至少有 $F1$ 個(gè)節(jié)點(diǎn)保持連通。例如在 5 節(jié)點(diǎn)集群中多數(shù)派必須滿足 $\ge 3$ 個(gè)節(jié)點(diǎn)。如果網(wǎng)絡(luò)被切分為一個(gè)包含 3 個(gè)節(jié)點(diǎn)的“多數(shù)派子集Majority Partition”和一個(gè)包含 2 個(gè)節(jié)點(diǎn)的“少數(shù)派子集Minority Partition”根據(jù) Raft 協(xié)議少數(shù)派集群由于無法獲得過半數(shù)節(jié)點(diǎn)的投票響應(yīng)必須立即拒絕所有新的寫事務(wù)請(qǐng)求如 Pod 創(chuàng)建、狀態(tài)更新只讀請(qǐng)求也需根據(jù)線性一致性讀Linearizable Read配置決定是否阻斷。Kube-apiserver 的無狀態(tài)外殼與租約競(jìng)爭(zhēng)kube-apiserver是 etcd 的直接客戶端。在多主架構(gòu)中所有的 apiserver 實(shí)例是對(duì)等的。當(dāng)網(wǎng)絡(luò)分區(qū)發(fā)生時(shí)少數(shù)派機(jī)房中的 apiserver 依然在監(jiān)聽本地 Kubelet 和業(yè)務(wù)客戶端的請(qǐng)求。如果它內(nèi)部維護(hù)的 Controller-Manager 或 Scheduler 的 Leader 選舉租約Lease / Endpoints Lock無法與多數(shù)派 etcd 正常續(xù)期系統(tǒng)將觸發(fā)控制權(quán)爭(zhēng)搶。節(jié)點(diǎn)控制器Node Lifecycle Controller的驅(qū)逐恐慌這是腦裂故障演進(jìn)中最兇險(xiǎn)的次生災(zāi)害。多數(shù)派分區(qū)的控制器由于網(wǎng)絡(luò)中斷無法收到少數(shù)派分區(qū)中成百上千個(gè) Kubelet 上報(bào)的節(jié)點(diǎn)心跳Heartbeat。在默認(rèn)參數(shù)下一旦超過 40 秒未收到心跳控制器就會(huì)將這些節(jié)點(diǎn)標(biāo)記為NotReady若超過 5 分鐘pod-eviction-timeout控制器將毫不留情地向整個(gè)集群下發(fā)大規(guī)模驅(qū)逐指令在多數(shù)派節(jié)點(diǎn)上盲目重建數(shù)千個(gè)重復(fù)的 Pod瞬間引發(fā)算力打滿和 etcd 寫入雪崩混沌演練場(chǎng)景構(gòu)造三維分區(qū)矩陣設(shè)計(jì)在演練平臺(tái)中我們編排了三種從易到難的致命網(wǎng)絡(luò)分區(qū)拓?fù)渫負(fù)?A: 經(jīng)典對(duì)半切分 (3 vs 2 Partition) [ Master 1 ] ─── [ Master 2 ] ─── [ Master 3 ] (多數(shù)派 Quorum: 存活可寫入) ══════════════════ [ 物理網(wǎng)絡(luò)斷裂隔離墻 ] ══════════════════ [ Master 4 ] ─── [ Master 5 ] (少數(shù)派: 拒絕寫事務(wù)降級(jí)) 拓?fù)?B: 不對(duì)稱單向網(wǎng)絡(luò)分區(qū) (Asymmetric Partition) [ Master 1 ] ──────── (只發(fā)不收 TCP ACK) ─────── [ Master 2 ] 拓?fù)?C: 孤立現(xiàn)有 Leader 節(jié)點(diǎn) (Isolated Leader) [ 老 Leader (Master 1) ] (被完全孤立) vs [ Master 2, 3, 4, 5 ] (觸發(fā)極速重選)混沌注入實(shí)戰(zhàn)基于 Chaos Mesh 與 iptables我們通過精準(zhǔn)的混沌編排在 5 節(jié)點(diǎn) Master 集群中將節(jié)點(diǎn) 4 和 5 與前三個(gè)節(jié)點(diǎn)徹底阻斷apiVersion: chaos-mesh.org/v1alpha1 kind: NetworkChaos metadata: name: control-plane-split-brain namespace: chaos-testing spec: action: partition mode: fixed value: 2 selector: namespaces: - kube-system labelSelectors: node-role.kubernetes.io/control-plane: pods: - [etcd-master-04, etcd-master-05] target: selector: pods: - [etcd-master-01, etcd-master-02, etcd-master-03] direction: both duration: 10m底層實(shí)現(xiàn)原理是通過在宿主機(jī)的網(wǎng)絡(luò)命名空間中注入精準(zhǔn)丟棄目標(biāo)端口etcd 對(duì)等通信端口 2380 與客戶端端口 2379TCP 握手包的 iptables 規(guī)則模擬絕對(duì)真實(shí)的物理交換機(jī)隔離。演練現(xiàn)場(chǎng)觀測(cè)與驚魂現(xiàn)象記錄在混沌注入后長(zhǎng)達(dá) 10 分鐘的演練窗口中監(jiān)控大盤與審計(jì)日志記錄下了一系列驚心動(dòng)魄的微觀表現(xiàn)現(xiàn)象一少數(shù)派 etcd 的自閉與線性一致性保障注入生效后 2.5 秒超過默認(rèn)的心跳超時(shí)時(shí)間 1000ms處于少數(shù)派分區(qū)的etcd-master-04和05察覺到丟失了來自主節(jié)點(diǎn)的探活。它們開始頻繁遞增任期編號(hào)Term并發(fā)起選舉但由于始終只能收集到 2 張選票不足法定 3 票兩臺(tái)節(jié)點(diǎn)迅速進(jìn)入持續(xù)候選者Candidate振蕩狀態(tài)。此時(shí)我們向少數(shù)派機(jī)房的kube-apiserver發(fā)起一個(gè)kubectl apply -f nginx-deployment.yaml創(chuàng)建請(qǐng)求apiserver 嘗試向本地 etcd 發(fā)送寫操作etcd 立即返回etcdserver: no leader錯(cuò)誤apiserver 向客戶端嚴(yán)格返回HTTP 500 Internal Server Error成功杜絕了非法狀態(tài)寫入未出現(xiàn)數(shù)據(jù)雙寫腦裂?,F(xiàn)象二節(jié)點(diǎn)誤判引發(fā)的 Pod 大規(guī)模驅(qū)逐風(fēng)暴Eviction Storm然而系統(tǒng)在工作節(jié)點(diǎn)Worker Node生命周期管理上暴露出巨大的隱患多數(shù)派控制器因?yàn)闊o法觸達(dá)少數(shù)派機(jī)房的 120 臺(tái)工作節(jié)點(diǎn)在第 45 秒將這些節(jié)點(diǎn)全部標(biāo)記為Unknown。在演練進(jìn)行到第 5 分鐘時(shí)悲劇準(zhǔn)時(shí)上演控制器認(rèn)定這 120 臺(tái)機(jī)器已徹底報(bào)廢開始瘋狂在多數(shù)派機(jī)房調(diào)度重建這上千個(gè)無狀態(tài) Pod。多數(shù)派機(jī)房原本平穩(wěn)的宿主機(jī)資源瞬間被擠爆kube-scheduler 的調(diào)度延遲暴增 30 倍控制面險(xiǎn)些因?yàn)樽约旱恼`判自殺現(xiàn)象三分區(qū)愈合瞬間的日志重放沖擊當(dāng)演練計(jì)時(shí)結(jié)束、Chaos Mesh 撤銷 iptables 隔離墻的瞬間少數(shù)派節(jié)點(diǎn)重新與主集群連通。由于在隔離期間落后了上萬條 Raft 日志兩臺(tái)節(jié)點(diǎn)開始瘋狂向主 Leader 發(fā)起 gRPC 快照流拉取InstallSnapshot RPC。巨大的網(wǎng)絡(luò)帶寬吞吐瞬間將 Leader 節(jié)點(diǎn)的 CPU 吃滿導(dǎo)致 Leader 節(jié)點(diǎn)在此后的 8 秒內(nèi)短暫出現(xiàn)了心跳丟幀。控制面自愈加固核心防線基于這場(chǎng)殘酷的腦裂演練暴露出的系統(tǒng)缺陷我們針對(duì)性地推行了三條硬核加固策略配置集群級(jí)防驅(qū)逐恐慌閾值Eviction Rate Limiting在kube-controller-manager中顯式配置--large-cluster-size-threshold50與--unhealthy-zone-threshold0.55。當(dāng)集群中處于NotReady狀態(tài)的節(jié)點(diǎn)比例超過 55% 時(shí)控制器判定集群當(dāng)前遭遇了機(jī)房級(jí)網(wǎng)絡(luò)分區(qū)故障自動(dòng)進(jìn)入“恐慌掛起狀態(tài)”將每分鐘允許驅(qū)逐的節(jié)點(diǎn)速率從無限制急剎車限制為每秒 0.01 個(gè)徹底封死了驅(qū)逐雪崩的大門。強(qiáng)制開啟線性一致性讀Linearizable Read確保所有的微服務(wù)客戶端在讀取核心數(shù)據(jù)時(shí)嚴(yán)禁使用非一致性讀取參數(shù)。強(qiáng)制 etcd 在處理讀請(qǐng)求時(shí)必須向多數(shù)派節(jié)點(diǎn)確認(rèn)當(dāng)前 Leader 身份Read Index 機(jī)制杜絕在極端網(wǎng)絡(luò)抖動(dòng)下讀取到已作廢的幽靈數(shù)據(jù)。優(yōu)化 Raft 心跳與網(wǎng)絡(luò)超時(shí)參數(shù)針對(duì)跨機(jī)房專線微抖動(dòng)將 etcd 的--heartbeat-interval調(diào)整為 250ms--election-timeout調(diào)整為 1250ms給予底層網(wǎng)絡(luò)充足的重傳容忍度消除因毫秒級(jí)抖動(dòng)引發(fā)的誤選主現(xiàn)象。真正的系統(tǒng)健壯性是在廢墟的模擬演練中一步步鍛造出來的。只有親手撕裂過自己的控制面并在烈火中見證過它的自愈與極限架構(gòu)師才敢在真正的機(jī)房海嘯來臨時(shí)泰然自若地穩(wěn)坐釣魚臺(tái)。