817 字
4 分钟
K8s 集群故障恢复实录

事故现场#

起因是我在做 K8s 部署 CI/CD 实验时,只备份了 Master 节点的快照。当时的想法是:etcd 数据主要在 Master 上,只要 Master 能恢复,集群就能恢复到可用状态;工作节点和其他组件即使有问题也可以重新拉起来。之后进行其他实验时,为了快速回到“干净环境”,我在实验结束后直接回滚了 Master 的快照。

问题来了:我只恢复了 Master,Node1 和 Node2 只恢复了刚搭建k8s集群时的快照!

集群起来后直接懵了,所有 Pod 都是 Unknown 状态,完全不工作。

Terminal window
kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
jenkins jenkins-0 0/1 Unknown 0 3d
gitea gitea-0 0/1 Unknown 0 3d
argocd argocd-server-xxx 0/1 Unknown 0 3d
...

这下麻烦了,整个集群挂了。

排查思路#

冷静分析了一下:

  1. Master 恢复到了几天前的状态
  2. Node1 和 Node2 还是”最初始”的状态
  3. 时间不同步,节点证书可能过期
  4. Kubelet 和 containerd 状态不对
  5. 一堆 Pod 卡在 Unknown,需要强制重建

开始抢救#

第一步:时间同步#

时间不同步会导致各种奇怪的问题,先把三个节点的时间对齐。

在所有节点上执行:

Terminal window
systemctl start chronyd
chronyc makestep
hwclock -w
date # 确认时间已经是当前时间

第二步:安装 NFS 客户端#

因为 Node 恢复后没有 NFS 工具,但集群里的 PV 都是 NFS 的,会导致挂载失败。

在 Node1 和 Node2 上执行:

Terminal window
yum install -y nfs-utils
systemctl enable --now rpcbind

第三步:重启容器运行时#

很多 Pod 的状态是旧的,需要重启 containerd 和 kubelet 清理掉僵尸进程。

在 Node1 和 Node2 上执行:

Terminal window
# 停止旧的容器进程(防止僵尸进程占用端口)
systemctl restart containerd
# 重启 kubelet
systemctl restart kubelet

等了几分钟,看了下节点状态:

Terminal window
kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane 10d v1.28.0
k8s-node1 Ready <none> 10d v1.28.0
k8s-node2 Ready <none> 10d v1.28.0

好,节点恢复 Ready 了。但 Pod 还是 Unknown。

第四步:强制删除 Pod#

Unknown 状态的 Pod 已经没救了,只能强制删掉让它们重建。

恢复 Jenkins#

Terminal window
kubectl -n jenkins delete pod jenkins-0 --force --grace-period=0

等了一会,新的 Pod 起来了:

Terminal window
kubectl -n jenkins get pods
NAME READY STATUS RESTARTS AGE
jenkins-0 1/1 Running 0 2m

恢复 Gitea#

Terminal window
kubectl -n gitea get pods
kubectl -n gitea delete pod --all --force --grace-period=0

恢复 ArgoCD#

Terminal window
kubectl -n argocd delete pod --all --force --grace-period=0

恢复 NFS Provisioner#

这个很关键,没有它 PVC 就没法自动创建。

Terminal window
kubectl -n kube-system get pods | grep nfs
kubectl -n kube-system delete pod -l app=nfs-subdir-external-provisioner --force --grace-period=0

第五步:验证#

等所有 Pod 都重建完成后,检查一下:

Terminal window
kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
jenkins jenkins-0 1/1 Running 0 5m
gitea gitea-0 1/1 Running 0 4m
argocd argocd-server-xxx 1/1 Running 0 3m
argocd argocd-application-controller-xxx 1/1 Running 0 3m
...

全部 Running!

访问一下服务:

  • Jenkins: http://192.168.100.10:30080
  • Gitea: http://192.168.100.10:30030
  • ArgoCD: https://192.168.100.10:30090

数据也都在,因为用的是 NFS 存储,没丢。

教训#

  1. 备份要全:既然要存快照,Master 和 Node 都得存,不能只存一半
  2. 时间同步很重要:分布式系统对时间敏感,不同步会出各种奇怪问题
  3. PV 数据还在就好办:只要持久化存储没丢,Pod 重建就能恢复

折腾了一个多小时,总算把集群救回来了。以后做实验还是得小心点,多备份。

K8s 集群故障恢复实录
https://dev-null-sec.github.io/posts/k8s集群故障恢复实录/
作者
DevNull
发布于
2025-05-15
许可协议
CC BY-NC-SA 4.0