
案例:etcd 腦裂故障復盤一句話定位:3 節點 etcd 集群因機房網絡分區導致 2 節點失聯,集群瞬間不可寫,核心業務全掛 23 分鐘——一次典型 Raft 多數派失效故障的完整應急復盤。寫在前面etcd 是 K8s 的心臟,所有集群狀態都存在它里面。平時我們聊 etcd 高可用,大多停留在3 節點能掛 1 個的理論層面,真遇到網絡分區導致多數派失效,很多團隊的第一反應是慌。2024 年 6 月,我們生產集群經歷了一次 etcd 腦裂:3 節點中 2 節點因機房網絡設備故障失聯,Raft 協議要求多數派(2/3)在線才能寫入,瞬間整個集群 apiserver 全部 5xx,核心業務全掛。這篇文章復盤這次故障的全過程:從告警觸發、影響評估、緊急恢復,到數據一致性校驗和長期改進。etcd 腦裂不是重啟就好的故障,恢復過程中如果操作不當,可能導致數據丟失或腦裂后雙主。我把我們踩過的坑、查過的資料、最終的操作步驟都記錄下來,希望對大家有幫助。etcd 腦裂的本質是 Raft 協議的多數派失效,這是設計上的保護機制,不是 bug。理解這一點,是正確處置這類故障的前提。案例概覽維度內容集群規模生產 K8s 1.30,1200 節點,3 控制面etcd 版本v3.5.13,3 節點,部署在控制面節點故障時間2024/06/21 14:32:08 - 14:55:17(共 23 分鐘)故障現象etcd 無主,apiserver 全部 5xx,業務 Pod 無法調度/重啟影響范圍全集群,新建/調度/擴容全部失敗,運行中 Pod 不受影響根因機房核心交換機故障,etcd-1/etcd-2 之間網絡分區恢復方式隔離 etcd-2,member remove 后重新加入,數據校驗業務影響約 8 萬筆交易延遲,無數據丟失故障時間線:14:32:08 告警:etcd cluster has no leader 14:32:15 告警:apiserver 5xx 率 100% 14:32:20 值班 SRE 確認,拉應急群 14:35:00 初判:etcd 腦裂,2 節點失聯 14:38:00 決策:隔離 etcd-2,單節點恢復(錯誤決策,后修正) 14:42:00 嘗試單節點啟動失敗(數據不一致) 14:45:00 改策略:用 etcd-0(健康節點)數據恢復 14:48:30 etcd-0 單節點成集群,apiserver 恢復 14:52:00 etcd-1 重新加入 14:55:17 etcd-2 重新加入,集群 3 節點正常,業務全恢復 15:30:00 數據一致性校驗完成,無丟失一、背景與挑戰1.1 集群架構┌──────────────────────────────────────────────────────┐ │ K8s 控制面 │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ cp-node-0│ │ cp-node-1│ │ cp-node-2│ │ │ │ apiserver│ │ apiserver│ │ apiserver│ │ │ │ etcd-0 │ │ etcd-1 │ │ etcd-2 │ │ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ └──────交換機A─┴──交換機B─┘ │ └──────────────────────────────────────────────────────┘ 機房 A 機房 B3 個控制面節點分布在機房 A 的兩個交換機域:cp-node-0/etcd-0 在交換機 A,cp-node-1/etcd-1 cp-node-2/etcd-2 在交換機 B。這個拓撲是后來出事的伏筆——兩個節點在同一個交換機域,交換機 B 故障直接帶走 2 個 etcd 成員。1.2 故障現象6/21 14:32,值班手機被告警刷屏:[FIRING:1] EtcdClusterHasNoLeader (critical) etcd_cluster_has_leader{jobetcd} 0 etcd-0, etcd-1, etcd-2 全部無 leader [FIRING:1] ApiserverDown (critical) apiserver_request_total{code~5..} rate 1.0 所有 apiserver 5xx 100% [FIRING:1] PodScheduleFail (critical) kube_pod_status_unschedulable 持續增長業務側反饋:新訂單創建失敗、Pod 擴容失敗、kubectl 全部超時,但已運行的 Pod 業務正常(因為 kubelet 不依賴 etcd)。1.3 應急挑戰etcd 不可用,所有 kubectl 命令失敗:常規 K8s 運維手段全部失效,必須直接操作 etcd。腦裂狀態下數據可能不一致:恢復時選哪個節點的數據?選錯會丟數據。操作風險高:etcdctl member remove 操作不當,可能把健康成員也踢出去,雪上加霜。業務壓力:每分鐘影響約 4000 筆交易,老板每 5 分鐘問一次好了沒。二、方案設計(應急流程)2.1 etcd 腦裂應急決策樹1 個2 個是否告警:etcd 無 leader確認網絡分區范圍幾個節點失聯多數派還在,集群自愈多數派丟失,集群不可寫定位健康節點健康節點數據是否最新用健康節點重建集群選數據最新的節點member remove 失聯節點健康節點單點啟動apiserver 恢復失聯節點修復后逐個加入數據一致性校驗2.2 關鍵決策點決策選項風險選擇用哪個節點恢復etcd-0(健康)數據可能不是最新選(網絡分區前是 leader)是否直接刪除失聯節點member remove刪錯會永久丟成員謹慎,先 backup單節點能否撐業務單點 etcd再掛就全完臨時撐,立即擴回 3 節點三、實施過程3.1 第一步:確認故障范圍(14:32 - 14:35)# 1. 直接連 etcd(繞過 apiserver)exportETCDCTL_API3ETCD_ENDPOINTShttps://10.0.1.10:2379,https://10.0.1.11:2379,https://10.0.1.12:2379etcdctl--endpoints$ETCD_ENDPOINTS\--cacert/etc/etcd/ca.pem\--cert/etc/etcd/etcd.pem\--key/etc/etcd/etcd-key.pem\endpoint status --write-outtable# 輸出:# -----------------------------------------------------------------------# | ENDPOINT | ID | VERSION | DB SIZE | IS LEADER |# -----------------------------------------------------------------------# | https://10.0.1.10:2379 | 8e9e05c52164694d | 3.5.13 | 4.3 GB | false |# | https://10.0.1.11:2379 | 91bc3c398fb3c146 | 3.5.13 | 4.3 GB | false | ← 超時# | https://10.0.1.12:2379 | fd422379fda50e85 | 3.5.13 | 4.3 GB | false | ← 超時# -----------------------------------------------------------------------# 三個節點都 false,無 leader,確認腦裂# 2. 查網絡連通性ping-c310.0.1.11#不通ping-c310.0.1.12#不通ping-c310.0.1.10#通(本機)# 3. 確認 etcd-0 本地服務狀態systemctl status etcd# active (running),但日志瘋狂報:# failed to connect to peer fd422379fda50e85結論:etcd-0 健康,etcd-1/etcd-2 因網絡分區失聯,腦裂確認。3.2 第二步:關鍵決策與錯誤嘗試(14:36 - 14:45)3.2.1 錯誤決策:直接 member remove我們第一反應是把失聯的兩個節點踢了,讓 etcd-0 單節點成集群:# ?? 這個操作后來證明是錯的etcdctl--endpointshttps://10.0.1.10:2379\--cacert...--cert...--key...\member remove 91bc3c398fb3c146# 報錯:# Error: etcdserver: request timed out# 原因:Raft 協議要求多數派同意才能執行 member remove# 當前只有 1/3,無法達成多數派,命令失敗教訓:腦裂狀態下,member remove 也需要多數派,直接 etcdctl 刪不掉。必須先讓健康節點單節點啟動(脫離原集群配置),再操作。3.2.2 正確做法:單節點強制啟動# 1. 先備份 etcd-0 數據(救命稻草)etcdctl--endpointshttps://10.0.1.10:2379\--cacert...--cert...--key...\snapshot save /backup/etcd-snapshot-$(date%s).db# 2. 停止 etcd-0systemctl stop etcd# 3. 修改 etcd 配置:從集群模式改為單節點模式# 關鍵:--force-new-cluster,用現有數據啟動新集群cat/etc/etcd/etcd.conf.ymlEOF name: etcd-0># 4. 啟動 etcd-0(單節點)systemctl start etcdsleep5etcdctl--endpointshttps://10.0.1.10:2379\--cacert...--cert...--key...\endpoint status --write-outtable# 輸出:IS LEADER true,集群恢復(單節點)關鍵點:--force-new-cluster用本地數據創建新集群,member list 里只有自己。這一步必須確認數據是正確的,因為后續都基于這份數據。3.3 第三步:恢復 apiserver(14:48)etcd-0 單節點起來后,apiserver 自動恢復:# 驗證 apiserverkubectl get nodes# 全部 Ready,apiserver 恢復kubectl get pods-nprod|head# 業務 Pod 正常列出# 業務側確認:新訂單創建恢復14:48:30,apiserver 恢復,業務新建/調度恢復,但 etcd 還是單點,風險極高,必須立即擴回 3 節點。3.4 第四步:修復失聯節點并重新加入(14:50 - 14:55)3.4.1 修復 etcd-1網絡分區原因是交換機 B 故障,網絡團隊 14:45 修復了交換機,etcd-1/etcd-2 網絡恢復。但它們的數據可能和 etcd-0 不一致(分區期間各自有寫入嘗試),不能直接加入,必須清空數據重新同步。# 在 etcd-1 上操作systemctl stop etcd# 清空舊數據(?? 確認 etcd-0 數據正確后才做)rm-rf/var/lib/etcd/member# 修改配置:作為成員加入 etcd-0cat/etc/etcd/etcd.conf.ymlEOF name: etcd-1># 在 etcd-0 上把 etcd-1 加為成員(先加 member 再啟動)etcdctl--endpointshttps://10.0.1.10:2379\--cacert...--cert...--key...\memberaddetcd-1\--peer-urlshttps://10.0.1.11:2380# 啟動 etcd-1systemctl start etcd# 驗證:etcd-1 自動從 etcd-0 同步數據etcdctl--endpointshttps://10.0.1.10:2379\--cacert...--cert...--key...\endpoint status --write-outtable# etcd-0 leader,etcd-1 follower,數據同步中3.4.2 修復 etcd-2(同樣流程)# etcd-2 上systemctl stop etcdrm-rf/var/lib/etcd/member# etcd-0 上加成員etcdctl--endpointshttps://10.0.1.10:2379\--cacert...--cert...--key...\memberaddetcd-2\--peer-urlshttps://10.0.1.12:2380# etcd-2 上改配置并啟動(同 etcd-1 流程)systemctl start etcd14:55:17,3 節點全部 online,leader 選舉完成,集群恢復。3.5 第五步:數據一致性校驗(15:00 - 15:30)腦裂期間,etcd-1/etcd-2 可能接受過少量寫請求(雖然無法 commit,但 WAL 日志可能有臟數據)。必須校驗數據一致性。3.5.1 集群哈希對比# etcd 提供了 hash 命令,對比各節點數據哈希etcdctl--endpointshttps://10.0.1.10:2379\--cacert...--cert...--key...\endpoint hashkv--cluster# 輸出:# ------------------------------------------------------# | ENDPOINT | ID | HASHKV |# ------------------------------------------------------# | https://10.0.1.10:2379 | 8e9e05c52164694d | 2834567891 |# | https://10.0.1.11:2379 | 91bc3c398fb3c146 | 2834567891 | ← 一致# | https://10.0.1.12:2379 | fd422379fda50e85 | 2834567891 | ← 一致# ------------------------------------------------------# 三個節點哈希一致,數據一致3.5.2 關鍵資源完整性校驗# 對比關鍵資源數量etcdctl--endpointshttps://10.0.1.10:2379\--cacert...--cert...--key...\get /registry/pods--prefix--keys-only|wc-l# 28456 條(與故障前監控記錄一致)etcdctl--endpointshttps://10.0.1.10:2379\--cacert...--cert...--key...\get /registry/services--prefix--keys-only|wc-l# 1842 條# 業務側校驗:抽樣確認訂單、支付關鍵數據# DB 層面數據未受影響(業務數據不在 etcd)校驗結論:數據零丟失,一致性正常。四、踩坑與應急4.1 踩坑1:member remove 在腦裂下失敗(見 3.2.1)教訓:腦裂狀態下任何需要多數派的操作都會失敗,必須先--force-new-cluster單節點啟動。4.2 踩坑2:etcd-1 重新加入報 “database schema incompatible”現象:etcd-1 啟動后報錯,無法同步。定位:etcd-1 舊數據沒清干凈,/var/lib/etcd/member下還有 wal 文件。修復:# 徹底清空,包括 walsystemctl stop etcdrm-rf/var/lib/etcd/* systemctl start etcd4.3 踩坑3:apiserver 緩存導致業務偶發異常現象:etcd 恢復后,部分 apiserver 請求返回舊數據。定位:apiserver 本地有緩存,etcd 恢復后緩存沒刷新。修復:# 重啟所有 apiserver,強制刷新緩存kubectl-nkube-system rollout restart deploy kube-apiserver# 或直接 ssh 到控制面節點systemctl restart kube-apiserver4.4 踩坑4:監控告警風暴影響判斷現象:故障期間收到 200 條告警,真正的根因告警被淹沒。改進:后續做了告警收斂,etcd/apiserver 故障時只推一條聚合告警,其他依賴告警靜默。五、復盤與改進5.1 故障影響總結指標數據故障時長23 分鐘業務影響約 8 萬筆交易延遲,無數據丟失RTO23 分鐘(目標 15 分鐘,未達標)RPO0(無數據丟失)根因機房交換機故障 etcd 拓撲不合理5.2 經驗教訓etcd 拓撲必須跨故障域:3 節點不能有 2 個在同一交換機,這次是拓撲設計失誤。腦裂下 member remove 無效:必須--force-new-cluster單節點啟動,這是核心知識點。數據備份是底線:恢復前必須 snapshot,操作失敗還能回滾。清數據要徹底:/var/lib/etcd/member和 wal 都要清,殘留會導致加入失敗。apiserver 緩存要刷新:etcd 恢復后必須重啟 apiserver。告警必須收斂:故障時告警風暴嚴重影響判斷,聚合告警是剛需。應急流程要演練:我們這次操作有猶豫,因為沒演練過,后續每月演練一次。5.3 長期改進5.3.1 etcd 拓撲優化把 etcd 節點分散到不同交換機域,甚至跨機房:改造后拓撲: etcd-0 → 機房A-交換機1 etcd-1 → 機房A-交換機2 etcd-2 → 機房B-交換機1(異地容災)5.3.2 etcd 監控告警增強# Prometheus 告警規則groups:-name:etcdrules:-alert:EtcdClusterHasNoLeaderexpr:etcd_server_has_leader 0for:1mlabels:severity:criticalannotations:summary:etcd 集群無 leader,可能腦裂runbook:https://wiki.example.com/etcd-split-brain-alert:EtcdMembersUnhealthyexpr:count(etcd_server_health_failures) by (cluster)0for:2mlabels:severity:criticalannotations:summary:etcd 有成員不健康-alert:EtcdClusterNoQuorumexpr:|count(up{jobetcd} 1) by (cluster) 2for:1mlabels:severity:criticalannotations:summary:etcd 多數派丟失,集群不可寫-alert:EtcdFsyncDurationHighexpr:|histogram_quantile(0.99, rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m])) 0.1for:3mlabels:severity:warningannotations:summary:etcd WAL fsync 延遲高,磁盤可能瓶頸5.3.3 etcd 健康巡檢腳本#!/bin/bash# etcd_health_check.sh - 每日巡檢ENDPOINTShttps://10.0.1.10:2379,https://10.0.1.11:2379,https://10.0.1.12:2379CERTS--cacert/etc/etcd/ca.pem --cert/etc/etcd/etcd.pem --key/etc/etcd/etcd-key.pemecho etcd 集群健康巡檢$(date)# 1. 集群狀態echo--- 節點狀態 ---etcdctl--endpoints$ENDPOINTS$CERTSendpoint status --write-outtable# 2. 成員列表echo--- 成員列表 ---etcdctl--endpoints$ENDPOINTS$CERTSmember list --write-outtable# 3. 數據哈希一致性echo--- 數據哈希 ---etcdctl--endpoints$ENDPOINTS$CERTSendpoint hashkv--cluster# 4. 告警檢查echo--- 無 leader 檢查 ---LEADER$(etcdctl--endpoints$ENDPOINTS $CERTS endpoint status-wjson|jq-r.[0].Status.header.member_id)if[-z$LEADER];thenechoWARN: 無 leader!exit1fi# 5. 磁盤使用echo--- 數據目錄大小 ---forhostin10.0.1.1010.0.1.1110.0.1.12;doecho$host:$(ssh$hostdu-sh/var/lib/etcd|awk{print $1})done# 6. 備份驗證echo--- 最近備份 ---ls-lht/backup/etcd-snapshot-*.db|head-3echo 巡檢完成 5.3.4 定期容災演練每月一次 etcd 故障注入演練:模擬單節點宕機(驗證集群自愈)模擬雙節點宕機(驗證應急恢復流程)模擬網絡分區(驗證腦裂處置)模擬數據損壞(驗證備份恢復)六、可復用產出6.1 etcd 故障應急預案(分級響應)級別現象響應時間處置P0集群無 leader/腦裂1 分鐘內響應啟動應急流程,force-new-clusterP0多數派丟失1 分鐘內響應同上P1單節點宕機5 分鐘內響應集群自愈,觀察是否擴縮P1磁盤使用 80%15 分鐘內響應compact defrag 擴容P2fsync 延遲高30 分鐘內響應排查磁盤 IOP2leader 切換頻繁30 分鐘內響應排查網絡抖動6.2 腦裂檢測告警規則(見 5.3.2)6.3 etcd 健康巡檢腳本(見 5.3.3)6.4 復盤報告模板# etcd 故障復盤報告 ## 1. 故障概述 - 故障時間: - 影響時長: - 業務影響: - 根因: ## 2. 時間線(分鐘級) | 時間 | 事件 | 操作人 | ## 3. 根因分析 - 直接原因: - 深層原因: - 架構問題: ## 4. 處置過程 - 應急動作: - 踩坑記錄: ## 5. 數據一致性校驗 - 哈希對比: - 資源數量對比: - 業務側確認: ## 6. 改進項 | 改進項 | 負責人 | 截止時間 | 狀態 | ## 7. 經驗沉淀 - 可復用 SOP: - 監控告警優化: - 演練計劃:思考題如果 3 節點 etcd 中有 1 個數據損壞(非網絡問題),你會如何處置?和腦裂處置有什么區別?--force-new-cluster操作的風險點在哪?如何保證選用的節點數據是最新的?etcd 集群規模是 3 節點還是 5 節點更合理?在成本和可用性之間如何權衡?延伸閱讀etcd 官方災難恢復文檔:https://etcd.io/docs/v3.5/op-guide/recovery/Raft 論文:In Search of an Understandable Consensus Algorithmetcd 腦裂與多數派機制解析K8s 控制面高可用最佳實踐《分布式系統:概念與設計》第 5 章