K8s etcd 集群崩溃异常恢复

kubectl执行命令卡住,访问apiserver:6443 报504连接超时报错,k8s集群无法操作,根据经验感觉 etcd 集群是出问题了,查看 etcd 集群发现只剩一个节点的etcd还在运行,登陆异常 etcd 节点发现etcd容器一直在不断重启,拿到报错日志,备份3个master节点上etcd数据,将etcd报错信息截图和故障现象描述发给 qwen3.8-max,根据AI的分析参考恢复。

K8s etcd 集群崩溃异常恢复

K8s etcd 集群崩溃异常恢复

qwen3.8-max


这是一个非常典型的 etcd 故障场景。根据你提供的日志截图,我们可以清晰地定位到问题的根源,并制定恢复方案。

1. 故障原因分析

核心报错信息:
在第一张图的最后一行,可以看到致命的错误日志:
{"level":"fatal", ... "msg":"failed to read WAL, cannot be repaired", "error":"wal: slice bounds out of range" ...}

详细解读:

  1. WAL 损坏 (Write-Ahead Log Corruption): etcd 使用 WAL 来保证数据的一致性和持久化。报错 failed to read WAL, cannot be repairedwal: slice bounds out of range 明确指出,当前节点(chegva-s3)的 WAL 日志文件已经损坏,且 etcd 尝试自动修复失败。这通常是由于磁盘 I/O 错误、非正常关机(断电)、或者文件系统问题导致的。

  2. 快照文件异常: 日志中还有一行警告:found unexpected non-snap file; skipping ... .snap.broken。这说明 etcd 在启动时检测到了损坏的快照文件,并将其标记为 broken。虽然它尝试从另一个快照恢复 (recovered v3 backend from snapshot),但在重放 WAL 日志以追赶最新状态时失败了。

  3. 集群状态:

    • 当前节点 chegva-s3 (IP: 172.xx.xx.45) 无法启动。

    • 从启动参数 --initial-cluster 可以看到这是一个 3 节点集群:chegva-s3(45), chegva-s2(44), chegva-s1(43)。

    • 关键点: etcd 是强一致性集群,只要多数派(2/3)节点存活,集群理论上仍可工作。但如果 kubectl 卡住,说明 API Server 无法连接到健康的 etcd 节点,或者另外两个节点也出现了问题(例如网络隔离、或者也因为类似原因挂了)。

  4. 备份脚本卡死: 第二张图显示有一个 etcdctl snapshot save 的进程正在运行。因为本地 etcd 挂了,这个命令会一直等待连接,导致卡住。这证实了 etcd 服务不可用。

2. 恢复方案

由于这是一个 3 节点集群,恢复策略取决于另外两个节点 (chegva-s1, chegva-s2) 的状态

第一步:检查其他节点状态 (至关重要)

请登录到另外两台机器 (4344),执行以下命令检查 etcd 是否健康:

# 在另外两台机器上执行
crictl ps | grep etcd
# 或者查看日志
crictl logs <etcd_container_id> --tail 50

同时,尝试在任意一台健康的节点上使用 etcdctl 检查集群状态(需要配置证书环境变量,或者直接在容器内执行):

# 示例:在容器内执行 (假设容器ID已知)
crictl exec -it <healthy_etcd_container_id> /bin/sh
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

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 \
  member list
  
# 示例:在物理机上执行
ETCDCTL_API=3 etcdctl \
  --endpoints=172.xx.xx.43:2379,172.xx.xx.44:2379,172.xx.xx.45:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key \
  endpoint status --cluster -w table

场景 A:另外两个节点健康 (集群多数派存活)

如果 chegva-s1chegva-s2 是健康的,那么恢复 chegva-s3 最简单的方法是移除坏节点的数据,让它作为新成员重新加入集群同步数据

操作步骤 (在 chegva-s3 上执行):

1.停止 etcd 容器:
由于是 kubelet 管理的静态 Pod,直接删除容器可能会重启。最好的方式是临时移走 manifest 文件。

mv /etc/kubernetes/manifests/etcd.yaml /tmp/etcd.yaml.bak
# 等待几秒,确认容器已停止
crictl ps | grep etcd

2.备份并清理数据目录:
警告: 务必先备份,以防万一。

mv /etc/kubernetes/manifests/etcd.yaml /tmp/etcd.yaml.bak
# 等待几秒,确认容器已停止
crictl ps | grep etcd

3.从集群中移除该成员 (在健康的节点上执行):
你需要获取 chegva-s3 的 Member ID。
在健康节点执行 etcdctl member list,找到对应 172.xx.xx.45 的 ID (例如 8e9e05c52164694d)。

# 在健康节点执行
ETCDCTL_API=3 etcdctl \
  --endpoints=172.xx.xx.43:2379,172.xx.xx.44:2379,172.xx.xx.45:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key \ 
  member remove <chegva-s3_Member_ID>

4.将该节点重新加入集群 (在健康的节点上执行):

# 在健康节点执行
etcdctl member add chegva-s3 --peer-urls=https://172.xx.xx.45:2380

执行后,终端会输出一段环境变量配置,请记录下来(虽然静态 Pod 通常不需要手动配这些,但确认一下 peer urls 是对的)。

5.恢复 etcd 静态 Pod:
回到 chegva-s3 节点。

mv /tmp/etcd.yaml.bak /etc/kubernetes/manifests/etcd.yaml

6.观察恢复情况:
kubelet 会自动拉起 etcd 容器。查看日志:

crictl logs -f <new_etcd_container_id>

你应该能看到它开始从 Leader 节点同步数据 (streaming snapshot, applying entries)。同步完成后,集群恢复正常。


场景 B:所有节点都挂了,或者无法形成多数派 (灾难恢复)

如果另外两个节点也挂了,或者数据也损坏了,你需要使用快照恢复

前提: 你必须有之前备份的 etcd snapshot 文件 (.db 文件)。
看第二张图,你的系统里似乎有个定时任务在跑 etcdctl snapshot save /data/snapshot-latest.db。如果这个文件存在且是最新的,可以用它恢复。

操作步骤 (全集群重建):

1.停止所有节点的 etcd:
在所有 3 台机器上执行:

mv /etc/kubernetes/manifests/etcd.yaml /tmp/etcd.yaml.bak

2.清理所有节点的数据目录:
在所有 3 台机器上执行:

rm -rf /var/lib/etcd/*

3.在一个节点上恢复快照 (例如 chegva-s1):
假设快照文件在 /data/snapshot-latest.db

# 注意:这里的 --name, --initial-cluster 等参数必须与原集群配置完全一致
etcdctl snapshot restore /data/snapshot-latest.db \
  --name chegva-s1 \
  --initial-cluster chegva-s1=https://172.xx.xx.43:2380,chegva-s2=https://172.xx.xx.44:2380,chegva-s3=https://172.xx.xx.45:2380 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-advertise-peer-urls https://172.xx.xx.43:2380 \
  --data-dir /var/lib/etcd

注意:--initial-cluster-token 最好查看原 etcd.yaml 里的配置,如果没有显式配置,etcd 有默认值,但为了保险起见,尽量保持一致。如果原配置没写,可以去掉这个参数或保持默认。

4.将恢复后的数据分发到其他节点 (或者分别恢复):
方法一(推荐): 分别在每个节点上用同一个快照文件执行 snapshot restore,但修改 --name--initial-advertise-peer-urls 为对应节点的 IP。

确保所有节点的 /var/lib/etcd 目录权限正确 (通常是 root:root)。

    • chegva-s2: --name chegva-s2 ... --initial-advertise-peer-urls https://172.xx.xx.44:2380

    • chegva-s3: --name chegva-s3 ... --initial-advertise-peer-urls https://172.xx.xx.45:2380

5.启动所有节点的 etcd:
在所有 3 台机器上执行:

mv /tmp/etcd.yaml.bak /etc/kubernetes/manifests/etcd.yaml

6.验证:
等待几分钟,检查日志,确认集群建立成功。

3. 针对你当前情况的建议操作顺序

  1. 不要急着删数据。 先保留现场 /var/lib/etcd 的备份。

  2. 立刻去检查 43 和 44 节点。

    • 如果 43/44 活着 -> 采用 场景 A (移除 s3 成员,清空 s3 数据,重新加入)。这是最快且风险最小的方法。

    • 如果 43/44 也死了 -> 寻找 /data/snapshot-latest.db (看图二似乎有这个文件),采用 场景 B 进行全量恢复。

  3. 关于卡住的 kubectl: 一旦 etcd 恢复,API Server 会自动重连 etcd,kubectl 命令会自动恢复响应,无需重启 API Server (除非 API Server 也挂了)。

补充提示:
图二中显示的 etcdctl snapshot save 进程卡住是因为它连不上本地 etcd。在修复过程中,你可以直接 kill 掉这个进程 (PID 3560716),以免影响后续操作或占用资源。


参考:

anzhihe 安志合个人博客,版权所有 丨 如未注明,均为原创 丨 转载请注明转自:https://chegva.com/6797.html | ☆★★每天进步一点点,加油!★★☆ | 

您可能还感兴趣的文章!

发表评论

电子邮件地址不会被公开。 必填项已用*标注