【K8S 运维实战】39-etcd脑裂故障复盘

案例: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                机房 B

3 个控制面节点分布在机房 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{job="etcd"} = 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 应急挑战

  1. etcd 不可用,所有 kubectl 命令失败:常规 K8s 运维手段全部失效,必须直接操作 etcd。
  2. 脑裂状态下数据可能不一致:恢复时选哪个节点的数据?选错会丢数据。
  3. 操作风险高:etcdctl member remove 操作不当,可能把健康成员也踢出去,雪上加霜。
  4. 业务压力:每分钟影响约 4000 笔交易,老板每 5 分钟问一次"好了没"。

二、方案设计(应急流程)

2.1 etcd 脑裂应急决策树

#mermaid-svg-w9h0yicLaxxpFBYP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-w9h0yicLaxxpFBYP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-w9h0yicLaxxpFBYP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-w9h0yicLaxxpFBYP .error-icon{fill:#552222;}#mermaid-svg-w9h0yicLaxxpFBYP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-w9h0yicLaxxpFBYP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-w9h0yicLaxxpFBYP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-w9h0yicLaxxpFBYP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-w9h0yicLaxxpFBYP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-w9h0yicLaxxpFBYP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-w9h0yicLaxxpFBYP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-w9h0yicLaxxpFBYP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-w9h0yicLaxxpFBYP .marker.cross{stroke:#333333;}#mermaid-svg-w9h0yicLaxxpFBYP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-w9h0yicLaxxpFBYP p{margin:0;}#mermaid-svg-w9h0yicLaxxpFBYP .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-w9h0yicLaxxpFBYP .cluster-label text{fill:#333;}#mermaid-svg-w9h0yicLaxxpFBYP .cluster-label span{color:#333;}#mermaid-svg-w9h0yicLaxxpFBYP .cluster-label span p{background-color:transparent;}#mermaid-svg-w9h0yicLaxxpFBYP .label text,#mermaid-svg-w9h0yicLaxxpFBYP span{fill:#333;color:#333;}#mermaid-svg-w9h0yicLaxxpFBYP .node rect,#mermaid-svg-w9h0yicLaxxpFBYP .node circle,#mermaid-svg-w9h0yicLaxxpFBYP .node ellipse,#mermaid-svg-w9h0yicLaxxpFBYP .node polygon,#mermaid-svg-w9h0yicLaxxpFBYP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-w9h0yicLaxxpFBYP .rough-node .label text,#mermaid-svg-w9h0yicLaxxpFBYP .node .label text,#mermaid-svg-w9h0yicLaxxpFBYP .image-shape .label,#mermaid-svg-w9h0yicLaxxpFBYP .icon-shape .label{text-anchor:middle;}#mermaid-svg-w9h0yicLaxxpFBYP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-w9h0yicLaxxpFBYP .rough-node .label,#mermaid-svg-w9h0yicLaxxpFBYP .node .label,#mermaid-svg-w9h0yicLaxxpFBYP .image-shape .label,#mermaid-svg-w9h0yicLaxxpFBYP .icon-shape .label{text-align:center;}#mermaid-svg-w9h0yicLaxxpFBYP .node.clickable{cursor:pointer;}#mermaid-svg-w9h0yicLaxxpFBYP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-w9h0yicLaxxpFBYP .arrowheadPath{fill:#333333;}#mermaid-svg-w9h0yicLaxxpFBYP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-w9h0yicLaxxpFBYP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-w9h0yicLaxxpFBYP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-w9h0yicLaxxpFBYP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-w9h0yicLaxxpFBYP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-w9h0yicLaxxpFBYP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-w9h0yicLaxxpFBYP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-w9h0yicLaxxpFBYP .cluster text{fill:#333;}#mermaid-svg-w9h0yicLaxxpFBYP .cluster span{color:#333;}#mermaid-svg-w9h0yicLaxxpFBYP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-w9h0yicLaxxpFBYP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-w9h0yicLaxxpFBYP rect.text{fill:none;stroke-width:0;}#mermaid-svg-w9h0yicLaxxpFBYP .icon-shape,#mermaid-svg-w9h0yicLaxxpFBYP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-w9h0yicLaxxpFBYP .icon-shape p,#mermaid-svg-w9h0yicLaxxpFBYP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-w9h0yicLaxxpFBYP .icon-shape .label rect,#mermaid-svg-w9h0yicLaxxpFBYP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-w9h0yicLaxxpFBYP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-w9h0yicLaxxpFBYP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-w9h0yicLaxxpFBYP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1 个
2 个


告警:etcd 无 leader
确认网络分区范围
几个节点失联
多数派还在,集群自愈
多数派丢失,集群不可写
定位健康节点
健康节点数据是否最新
用健康节点重建集群
选数据最新的节点
member remove 失联节点
健康节点单点启动
apiserver 恢复
失联节点修复后逐个加入
数据一致性校验

2.2 关键决策点

决策 选项 风险 选择
用哪个节点恢复 etcd-0(健康) 数据可能不是最新 选(网络分区前是 leader)
是否直接删除失联节点 member remove 删错会永久丢成员 谨慎,先 backup
单节点能否撑业务 单点 etcd 再挂就全完 临时撑,立即扩回 3 节点

三、实施过程

3.1 第一步:确认故障范围(14:32 - 14:35)

bash 复制代码
# 1. 直接连 etcd(绕过 apiserver)
export ETCDCTL_API=3
ETCD_ENDPOINTS="https://10.0.1.10:2379,https://10.0.1.11:2379,https://10.0.1.12:2379"
etcdctl --endpoints=$ETCD_ENDPOINTS \
  --cacert=/etc/etcd/ca.pem \
  --cert=/etc/etcd/etcd.pem \
  --key=/etc/etcd/etcd-key.pem \
  endpoint status --write-out=table

# 输出:
# +------------------------+------------------+---------+---------+-----------+
# |       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 -c 3 10.0.1.11   #不通
ping -c 3 10.0.1.12   #不通
ping -c 3 10.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 单节点成集群":

bash 复制代码
# ⚠️ 这个操作后来证明是错的
etcdctl --endpoints=https://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 正确做法:单节点强制启动
bash 复制代码
# 1. 先备份 etcd-0 数据(救命稻草)
etcdctl --endpoints=https://10.0.1.10:2379 \
  --cacert=... --cert=... --key=... \
  snapshot save /backup/etcd-snapshot-$(date +%s).db

# 2. 停止 etcd-0
systemctl stop etcd

# 3. 修改 etcd 配置:从集群模式改为单节点模式
# 关键:--force-new-cluster,用现有数据启动新集群
cat > /etc/etcd/etcd.conf.yml << 'EOF'
name: 'etcd-0'
data-dir: /var/lib/etcd
force-new-cluster: true              # ← 关键:用现有数据强制启动单节点集群
listen-peer-urls: https://10.0.1.10:2380
listen-client-urls: https://10.0.1.10:2379
initial-advertise-peer-urls: https://10.0.1.10:2380
advertise-client-urls: https://10.0.1.10:2379
initial-cluster: etcd-0=https://10.0.1.10:2380
initial-cluster-state: new
initial-cluster-token: 'etcd-cluster-prod'
client-transport-security:
  cert-file: /etc/etcd/etcd.pem
  key-file: /etc/etcd/etcd-key.pem
  trusted-ca-file: /etc/etcd/ca.pem
peer-client-transport-security:
  cert-file: /etc/etcd/etcd.pem
  key-file: /etc/etcd/etcd-key.pem
  trusted-ca-file: /etc/etcd/ca.pem
EOF

# 4. 启动 etcd-0(单节点)
systemctl start etcd
sleep 5
etcdctl --endpoints=https://10.0.1.10:2379 \
  --cacert=... --cert=... --key=... \
  endpoint status --write-out=table

# 输出:IS LEADER = true,集群恢复(单节点)

关键点 :--force-new-cluster 用本地数据创建新集群,member list 里只有自己。这一步必须确认数据是正确的,因为后续都基于这份数据。

3.3 第三步:恢复 apiserver(14:48)

etcd-0 单节点起来后,apiserver 自动恢复:

bash 复制代码
# 验证 apiserver
kubectl get nodes
# 全部 Ready,apiserver 恢复

kubectl get pods -n prod | 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 不一致(分区期间各自有写入尝试),不能直接加入,必须清空数据重新同步。

bash 复制代码
# 在 etcd-1 上操作
systemctl stop etcd

# 清空旧数据(⚠️ 确认 etcd-0 数据正确后才做)
rm -rf /var/lib/etcd/member

# 修改配置:作为成员加入 etcd-0
cat > /etc/etcd/etcd.conf.yml << 'EOF'
name: 'etcd-1'
data-dir: /var/lib/etcd
listen-peer-urls: https://10.0.1.11:2380
listen-client-urls: https://10.0.1.11:2379
initial-advertise-peer-urls: https://10.0.1.11:2380
advertise-client-urls: https://10.0.1.11:2379
initial-cluster: etcd-0=https://10.0.1.10:2380,etcd-1=https://10.0.1.11:2380
initial-cluster-state: existing          # ← existing,不是 new
initial-cluster-token: 'etcd-cluster-prod'
# ... TLS 配置同上
EOF

# 在 etcd-0 上把 etcd-1 加为成员(先加 member 再启动)
etcdctl --endpoints=https://10.0.1.10:2379 \
  --cacert=... --cert=... --key=... \
  member add etcd-1 \
  --peer-urls=https://10.0.1.11:2380

# 启动 etcd-1
systemctl start etcd

# 验证:etcd-1 自动从 etcd-0 同步数据
etcdctl --endpoints=https://10.0.1.10:2379 \
  --cacert=... --cert=... --key=... \
  endpoint status --write-out=table
# etcd-0 = leader,etcd-1 = follower,数据同步中
3.4.2 修复 etcd-2(同样流程)
bash 复制代码
# etcd-2 上
systemctl stop etcd
rm -rf /var/lib/etcd/member

# etcd-0 上加成员
etcdctl --endpoints=https://10.0.1.10:2379 \
  --cacert=... --cert=... --key=... \
  member add etcd-2 \
  --peer-urls=https://10.0.1.12:2380

# etcd-2 上改配置并启动(同 etcd-1 流程)
systemctl start etcd

14:55:17,3 节点全部 online,leader 选举完成,集群恢复。

3.5 第五步:数据一致性校验(15:00 - 15:30)

脑裂期间,etcd-1/etcd-2 可能接受过少量写请求(虽然无法 commit,但 WAL 日志可能有脏数据)。必须校验数据一致性。

3.5.1 集群哈希对比
bash 复制代码
# etcd 提供了 hash 命令,对比各节点数据哈希
etcdctl --endpoints=https://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 关键资源完整性校验
bash 复制代码
# 对比关键资源数量
etcdctl --endpoints=https://10.0.1.10:2379 \
  --cacert=... --cert=... --key=... \
  get /registry/pods --prefix --keys-only | wc -l
# 28456 条(与故障前监控记录一致)

etcdctl --endpoints=https://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 文件。

修复:

bash 复制代码
# 彻底清空,包括 wal
systemctl stop etcd
rm -rf /var/lib/etcd/*
systemctl start etcd

4.3 踩坑3:apiserver 缓存导致业务偶发异常

现象:etcd 恢复后,部分 apiserver 请求返回旧数据。

定位:apiserver 本地有缓存,etcd 恢复后缓存没刷新。

修复:

bash 复制代码
# 重启所有 apiserver,强制刷新缓存
kubectl -n kube-system rollout restart deploy kube-apiserver
# 或直接 ssh 到控制面节点
systemctl restart kube-apiserver

4.4 踩坑4:监控告警风暴影响判断

现象:故障期间收到 200+ 条告警,真正的根因告警被淹没。

改进:后续做了告警收敛,etcd/apiserver 故障时只推一条聚合告警,其他依赖告警静默。

五、复盘与改进

5.1 故障影响总结

指标 数据
故障时长 23 分钟
业务影响 约 8 万笔交易延迟,无数据丢失
RTO 23 分钟(目标 < 15 分钟,未达标)
RPO 0(无数据丢失)
根因 机房交换机故障 + etcd 拓扑不合理

5.2 经验教训

  1. etcd 拓扑必须跨故障域:3 节点不能有 2 个在同一交换机,这次是拓扑设计失误。
  2. 脑裂下 member remove 无效 :必须 --force-new-cluster 单节点启动,这是核心知识点。
  3. 数据备份是底线:恢复前必须 snapshot,操作失败还能回滚。
  4. 清数据要彻底 :/var/lib/etcd/member 和 wal 都要清,残留会导致加入失败。
  5. apiserver 缓存要刷新:etcd 恢复后必须重启 apiserver。
  6. 告警必须收敛:故障时告警风暴严重影响判断,聚合告警是刚需。
  7. 应急流程要演练:我们这次操作有犹豫,因为没演练过,后续每月演练一次。

5.3 长期改进

5.3.1 etcd 拓扑优化

把 etcd 节点分散到不同交换机域,甚至跨机房:

复制代码
改造后拓扑:
etcd-0 → 机房A-交换机1
etcd-1 → 机房A-交换机2
etcd-2 → 机房B-交换机1(异地容灾)
5.3.2 etcd 监控告警增强
yaml 复制代码
# Prometheus 告警规则
groups:
  - name: etcd
    rules:
      - alert: EtcdClusterHasNoLeader
        expr: etcd_server_has_leader == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "etcd 集群无 leader,可能脑裂"
          runbook: "https://wiki.example.com/etcd-split-brain"

      - alert: EtcdMembersUnhealthy
        expr: count(etcd_server_health_failures) by (cluster) > 0
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "etcd 有成员不健康"

      - alert: EtcdClusterNoQuorum
        expr: |
          count(up{job="etcd"} == 1) by (cluster) < 2
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "etcd 多数派丢失,集群不可写"

      - alert: EtcdFsyncDurationHigh
        expr: |
          histogram_quantile(0.99, rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m])) > 0.1
        for: 3m
        labels:
          severity: warning
        annotations:
          summary: "etcd WAL fsync 延迟高,磁盘可能瓶颈"
5.3.3 etcd 健康巡检脚本
bash 复制代码
#!/bin/bash
# etcd_health_check.sh - 每日巡检
ENDPOINTS="https://10.0.1.10:2379,https://10.0.1.11:2379,https://10.0.1.12:2379"
CERTS="--cacert=/etc/etcd/ca.pem --cert=/etc/etcd/etcd.pem --key=/etc/etcd/etcd-key.pem"

echo "===== etcd 集群健康巡检 $(date) ====="

# 1. 集群状态
echo "--- 节点状态 ---"
etcdctl --endpoints=$ENDPOINTS $CERTS endpoint status --write-out=table

# 2. 成员列表
echo "--- 成员列表 ---"
etcdctl --endpoints=$ENDPOINTS $CERTS member list --write-out=table

# 3. 数据哈希一致性
echo "--- 数据哈希 ---"
etcdctl --endpoints=$ENDPOINTS $CERTS endpoint hashkv --cluster

# 4. 告警检查
echo "--- 无 leader 检查 ---"
LEADER=$(etcdctl --endpoints=$ENDPOINTS $CERTS endpoint status -w json | jq -r '.[0].Status.header.member_id')
if [ -z "$LEADER" ]; then
  echo "WARN: 无 leader!"
  exit 1
fi

# 5. 磁盘使用
echo "--- 数据目录大小 ---"
for host in 10.0.1.10 10.0.1.11 10.0.1.12; do
  echo "$host: $(ssh $host du -sh /var/lib/etcd | awk '{print $1}')"
done

# 6. 备份验证
echo "--- 最近备份 ---"
ls -lht /backup/etcd-snapshot-*.db | head -3

echo "===== 巡检完成 ====="
5.3.4 定期容灾演练

每月一次 etcd 故障注入演练:

  • 模拟单节点宕机(验证集群自愈)
  • 模拟双节点宕机(验证应急恢复流程)
  • 模拟网络分区(验证脑裂处置)
  • 模拟数据损坏(验证备份恢复)

六、可复用产出

6.1 etcd 故障应急预案(分级响应)

级别 现象 响应时间 处置
P0 集群无 leader/脑裂 1 分钟内响应 启动应急流程,force-new-cluster
P0 多数派丢失 1 分钟内响应 同上
P1 单节点宕机 5 分钟内响应 集群自愈,观察是否扩缩
P1 磁盘使用 > 80% 15 分钟内响应 compact + defrag + 扩容
P2 fsync 延迟高 30 分钟内响应 排查磁盘 IO
P2 leader 切换频繁 30 分钟内响应 排查网络抖动

6.2 脑裂检测告警规则

(见 5.3.2)

6.3 etcd 健康巡检脚本

(见 5.3.3)

6.4 复盘报告模板

markdown 复制代码
# etcd 故障复盘报告
## 1. 故障概述
- 故障时间:
- 影响时长:
- 业务影响:
- 根因:
## 2. 时间线(分钟级)
| 时间 | 事件 | 操作人 |
## 3. 根因分析
- 直接原因:
- 深层原因:
- 架构问题:
## 4. 处置过程
- 应急动作:
- 踩坑记录:
## 5. 数据一致性校验
- 哈希对比:
- 资源数量对比:
- 业务侧确认:
## 6. 改进项
| 改进项 | 负责人 | 截止时间 | 状态 |
## 7. 经验沉淀
- 可复用 SOP:
- 监控告警优化:
- 演练计划:

思考题

  1. 如果 3 节点 etcd 中有 1 个数据损坏(非网络问题),你会如何处置?和脑裂处置有什么区别?
  2. --force-new-cluster 操作的风险点在哪?如何保证选用的节点数据是最新的?
  3. etcd 集群规模是 3 节点还是 5 节点更合理?在成本和可用性之间如何权衡?

延伸阅读

  • etcd 官方灾难恢复文档:https://etcd.io/docs/v3.5/op-guide/recovery/
  • Raft 论文:In Search of an Understandable Consensus Algorithm
  • etcd 脑裂与多数派机制解析
  • K8s 控制面高可用最佳实践
  • 《分布式系统:概念与设计》第 5 章
相关推荐
Acrellea1 小时前
告别粗放式能耗管理!安科瑞EIOT平台助力产业园区智慧低碳运营
运维·安全·能源
Databuff1 小时前
五款主流 SSH 免费工具介绍
运维·ssh·运维开发
菜鸟学编程o1 小时前
Linux常见指令
linux·运维·服务器
pt10432 小时前
AIOps机器学习——当警报阈值被调高之后
运维·人工智能·自动化
迪康coolmu3 小时前
企业文件外发管控落地实践 —— 基于迪康端点安全一体化管理系统
运维·网络·经验分享·安全
见闻小天地3 小时前
四方电气DL300变频器面向雕铣机多工况的技术参数与适配验证
运维·人工智能·业界资讯
千舟软件4 小时前
【宿舍管理小程序】新生入住不靠纸质表
大数据·运维·服务器·科技·业界资讯
j7~5 小时前
【Linux网络加餐课】(篇八)网络版计算器(终篇):守护进程、标准 IO 重定向与部署打包
linux·运维·网络编程·tcp·守护进程·io重定向·部署打包
海宇服务6 小时前
零信任架构实战:基于海宇公安二要素认证即时版构建自动化号码发卡网关
运维·人工智能·架构·自动化
蓝速科技6 小时前
数字人一体机芯片选型:RK3576 与 X86 场景化对比指南丨蓝速科技
大数据·运维·数据库·人工智能·科技