跳转至

INCIDENT-20260916 — etcd 失去多数派导致控制面完全不可用

本文档是台账与交接记录,用于复盘与行动项跟踪。 变更是"关闭两台控制面虚拟机",属人为操作;故障是"etcd 成员数不足导致 quorum 丢失"。

1. 概要

项 内容
发生时间 2026-09-16,故障起始时刻无直接日志留存;处置动作在 12:24:34(有证据)
等级 P1(控制面完全不可用):集群 API 全停,无法创建/修改/删除任何资源
影响面 全部 6 台节点;已有 Pod 未受影响(kubelet 与业务容器继续运行),但集群失去管理能力
数据影响 无数据损坏、无数据丢失(etcd 数据完整,仅因成员数不足无法达成共识)
当前状态 已恢复。集群运行正常,且已按新架构缩容为单控制面(见 SOP 5.10)
直接原因 3 成员 etcd 中被关闭 2 台,仅剩 1 个成员 < 多数派 2,etcd 无法选举 leader

2. 影响范围

  • 受影响:kubectl 全部操作、控制面组件(apiserver/controller-manager/scheduler)、依赖 API 的所有自动化
  • 未受影响:已在运行的业务 Pod(不含被关闭节点上的 Pod)、CNI 数据面转发
  • 被关闭节点上的 Pod:随虚拟机一起停止(calico-node、kube-proxy 等 DaemonSet 容器)
  • 无影响:etcd 数据文件本身完整;无 etcd 数据目录损坏,无集群重建需求

3. 时间线

时间取自节点与工程的可核验记录;无法取证处已明确标注为推断。

时间(CST) 事件 证据来源
09-16 02:56:08 部署流程安装 containerd(运行时就绪) systemctl show containerd -p ActiveEnterTimestamp
09-16 03:07:48 keepalived 启动,VIP 就绪 systemctl show keepalived -p ActiveEnterTimestamp
09-16 03:34 部署工程最后一次执行(3 成员 etcd HA 集群建成) 工程目录 backups/20260916_033456/
03:34 至 12:24 之间 人为关闭 master-02、master-03 虚拟机,etcd 由 3 成员降至 1 成员,失去多数派 推断(无直接日志留存,依据 member 变更与后续现象)
同期 apiserver 反复退出与重启 crictl ps -a:apiserver 容器 ATTEMPT 18(当前 Running)、ATTEMPT 17(Exited)
同期 客户端侧 kubectl 报连接失败(TLS 握手超时 / context deadline exceeded) 现场实测
09-16 12:24:34 处置:启动 master-02 恢复多数派,并重启 master-01 的 kubelet 触发静态 Pod 重建 systemctl show kubelet -p ActiveEnterTimestamp
09-16 12:24 之后 apiserver 重建完成,控制面恢复 现场实测 curl -sk https://192.168.31.8:16443/healthz 返回 ok
09-16 16:00 起 进一步缩容为单控制面(受控操作,见 SOP 5.10) 工程 backups/20260916_163436/、170705/

4. 根因

etcd 集群失去多数派(quorum lost),导致无法选出 leader,apiserver 因存储层不可用而反复退出。

etcd 采用 Raft 共识:3 成员集群的多数派为 2,任何写操作与 leader 选举都需 ≥2 个成员达成一致。 当 2 台被关闭后只剩 1 个成员,永远无法满足多数派,集群进入无限选举循环。

证据

证据 1:apiserver 反复重启

ssh root@192.168.31.10 "crictl ps -a | grep kube-apiserver"
4f841ad00fbda  ...  3 days ago  Running  kube-apiserver  ATTEMPT 18
7dad052463f04  ...  3 days ago  Exited   kube-apiserver  ATTEMPT 17

ATTEMPT 18 表示该容器已被重新拉起 18 次,是控制面持续崩溃重启的直接记录。

证据 2:etcd 反复发起选举、客户端请求超时(故障现场实时抓取)

{"level":"warn","msg":"waiting for ReadIndex response took too long, retrying"}
{"level":"warn","msg":"23d6928b1e4bc362 is starting a new election at term 5"}
{"level":"warn","msg":"etcdserver: no leader"}
etcd-client: rpc error: code = DeadlineExceeded desc = context deadline exceeded

no leader + 反复 starting a new election 是 quorum 不足的典型特征——有候选者、无多数票。

证据 3:apiserver 无法读写 etcd,进程 Fatal 退出

kube-apiserver ... failed to contact etcd
PostStartHook "wait for APIServer" failed ... context deadline exceeded

(现场抓取的 kubelet 侧记录;此后 apiserver 容器被反复重建,形成证据 1 的 18 次重启。)

证据 4:etcd 进程本身始终健康

ssh root@192.168.31.10 "crictl ps -a | grep etcd"
38758502c2508  ...  3 days ago  Running  etcd  ATTEMPT 0

ATTEMPT 0 说明 etcd 容器自创建起从未崩溃重启——故障不在 etcd 进程,而在成员数量。

排除项:不是磁盘故障、不是证书过期、不是网络分区(节点间二层可达)、不是 etcd 数据损坏。

5. 处置过程

步骤 动作 说明
1 确认 etcd 成员数与健康度 etcdctl member list 显示 3 成员但仅 1 个可达
2 确认控制面容器状态 apiserver 反复重启、etcd 未重启(定位为 quorum 问题)
3 止血:启动 k8s-master-02 virsh start k8s-master-02(宿主机上执行),成员数回到 2,满足多数派
4 等待 etcd 选出 leader 观察选举结束后 endpoint health 转为健康
5 重启 master-01 的 kubelet systemctl restart kubelet,触发已退出的 apiserver 静态 Pod 重建
6 验证控制面恢复 healthz 返回 ok、kubectl get nodes 正常

未做的事(有意为之):未执行 kubeadm reset、未重建集群、未从快照恢复——数据完好, 仅需恢复成员数即可,任何重建动作都会带来不必要的数据风险。

6. 恢复验证

curl -sk https://192.168.31.8:16443/healthz                  # 预期:ok(实测通过)
kubectl get --raw='/readyz'                                  # 预期:ok(实测通过)
kubectl get nodes                                            # 预期:全部 Ready(实测通过)
kubectl get pods -A | grep -Ev 'Running|Completed'           # 预期:无输出(实测通过)

以上四项在现场处置后均实测通过;随后集群稳定运行,直至 09-16 16:00 起按计划缩容为单控制面。

7. 复盘

有效的做法

  1. 从"apiserver 反复重启 + etcd 从未重启 + member 列表完整"三点迅速锁定是 quorum 问题, 没有误判为 etcd 数据损坏或证书问题,避免了走重建集群的弯路
  2. 只在必要时动作(启动一台虚机 + 重启 kubelet),未做多余操作
  3. 恢复后立即做了幂等复跑与成员数核对

拖慢恢复的地方

  1. 没有第一时间怀疑"节点被关闭":起初按"网络/证书"方向排查,多绕了几步; 应先核对 etcd 成员可达性(endpoint health 而非 member list——后者对关机成员仍显示 started)
  2. 缺乏外部监控:故障从发生到被发现依赖人工操作时才暴露,期间无人知晓
  3. member list 的 STATUS 列有误导性:它表示"是否曾启动过",不反映实时健康
  4. 无人值守期间关闭多数派节点,本身缺少前置约束(没有"最多只能关一台控制面"的护栏)

根因归类:配置/操作失误(人为关闭多数派节点),叠加"无告警、无护栏"的运维缺口。

8. 行动项

# 事项 责任人 期限 状态
1 缩容为单控制面,从根本上消除"误关多数派"场景(etcd 单成员 quorum=1) 用户 2026-09-16 ✅ 已完成(SOP 5.10)
2 master-02/03 关闭 libvirt autostart,避免宿主机重启后自动拉起"残缺控制面" 用户 2026-09-16 ✅ 已完成
3 在部署工程前置校验中加入"etcd 多数派"检查,成员不足时拒绝缩容 运维 2026-09-16 ✅ 已完成(control_plane_scale.py pre_check)
4 部署工程加入 hostname 与时间同步托管,减少人工准备项 运维 2026-09-16 ✅ 已完成
5 为集群接入外部监控与告警(控制面可用性、节点 Ready、etcd 健康) 用户 待定 ⬜ 未开始(可复用 monitoring_upgrade/ 工程)
6 实施 etcd 定时快照并做一次恢复演练 用户 待定 ⬜ 未开始(当前仅有手工快照)
7 若日后扩回 3 控制面,需建立"任何时刻只操作一台控制面"的书面约束 用户 待定 ⬜ 未开始(已在 RUNBOOK 第 1 章写明铁律)

责任人一栏中"用户"指环境所有者(本环境为单人学习环境,无团队分工); 期限"待定"表示尚未排期,不是已放弃。


来源:/home/rain/Documents/k8s_ins/k8s_136_cluster_deploy/docs/INCIDENT-20260916-etcd-quorum-lost.md | 最后更新:2026-09-19 16:19(由 build/sync.py 同步,请勿在站点侧修改)