K8s etcd 数据库碎片化导致集群脑裂崩溃排查
2026-08-11 00:53:03 # Kubernetes

凌晨 API Server 无响应、所有节点状态异常、etcd 健康检查超时——这类 P0 级事故,背后常是 etcd 数据库碎片化。它不会立刻报错,而是渐进式变慢,直到某个时间点突然触发连锁故障。

问题现象与背景原因

典型现象:API Server 无响应,etcd 健康检查卡住或超时。

1
2
3
4
5
6
7
ETCDCTL_API=3 etcdctl \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
endpoint health
# 卡住或返回 timeout

核心原因:etcd 数据库碎片化(Fragmentation)严重

  • K8s 所有状态都存在 etcd。etcd 用 MVCC(多版本并发控制),每次写都保留历史版本,由后台 compaction 清理。
  • 若 compaction 不及时或写入量巨大,历史版本堆积,底层 boltdb 文件产生大量”空洞”,DB Size 膨胀到数 GB(健康通常几百 MB)。
  • 文件碎片化后,读写需扫描大量空洞,性能急剧下降,etcd 响应变慢 → 与 API Server 心跳超时 → 集群脑裂 / 控制面崩溃。

提示:etcd 慢是”沉默的杀手”。它不会立刻报错,而是渐进式变慢,直到某个时间点(常是凌晨批量任务 / 大量对象变更)突然触发连锁故障。

排查过程(思路与定位方法)

第一步:看 etcd 健康与 DB 大小。

1
2
3
4
5
6
export ETCDCTL_API=3
ectl() { etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key "$@"; }
ectl endpoint status --write-out=table

关注 DB SIZE 列,正常应 < 500MB;若到 GB 级说明已膨胀。

第二步:看是否有 alarm(如 NOSPACE 空间不足告警)。

1
2
ectl alarm list
# NOSPACE 表示已触顶配额

第三步:看碎片率(粗略判断)。endpoint statusDB SIZE 与磁盘上实际文件大小对比,差异越大碎片化越严重。

第四步:看 API Server / etcd 日志里的 slow 请求。

1
journalctl -u kube-apiserver -u etcd --since 1h | grep -iE 'slow|timeout|lag'

最终的解决方法

核心操作:etcdctl defrag 碎片整理(需在维护窗口内,会阻塞写)。

1
2
3
# 对每个 etcd 端点执行在线碎片整理
ectl defrag
# 输出 Finished defraging ... 即成功,DB SIZE 会明显回落

配套措施(黄金法则):

  1. 定期 compaction + defrag 脚本化,纳入定时维护。
  2. 定期快照备份(见下)。
  3. 监控 DB SIZE 与磁盘延迟,设阈值告警。
  4. 控制写入量:避免集群对象(Pod / Secret / CRD 实例)无限增长。

周期性维护示例:

1
2
3
4
5
6
# 先 compact 到当前版本
rev=$(ectl endpoint status --write-out=json | \
python3 -c 'import sys,json;print(json.load(sys.stdin)["header"]["revision"])')
ectl compact $rev
# 再 defrag
ectl defrag

快照备份(救命用):

1
2
ectl snapshot save /backup/etcd-$(date +%F).db
ectl snapshot status /backup/etcd-$(date +%F).db --write-out=table

操作命令(可直接复制执行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 设别名简化(控制面节点执行)
export ETCDCTL_API=3
alias ectl='etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key'

# 1. 看状态与 DB 大小
ectl endpoint status --write-out=table

# 2. 看告警
ectl alarm list

# 3. 碎片整理(维护窗口内)
ectl defrag

# 4. 快照备份
ectl snapshot save /backup/etcd-$(date +%F).db

# 5. 验证快照
ectl snapshot status /backup/etcd-$(date +%F).db --write-out=table