背景
现有的2套kubernete环境是基于官方开源工具kubeadm部署的kubernete版本,容器运行时是docker,有3master+56worker节点规模。核心组件:etcd 集群(与 Master 节点共部署,3 节点高可用架构)、kube-apiserver、kube-controller-manager、kube-scheduler 等。
K8s集群突然崩了,etcd数据丢了一半,etcd是K8s的"数据大脑",所有集群资源(Pod、Service、ConfigMap)、状态信息都存在这里。etcd 是 Kubernetes 的核心数据库,负责存储集群的所有配置信息、状态数据(如 Pod 定义、Service 路由、Namespace、节点信息等),是集群的 "大脑"。一旦 etcd 出现故障,直接引发 Kubernetes 集群的功能异常甚至完全不可用。具体的故障影响和etcd的故障范围有关。
轻度故障:etcd 集群部分节点异常(单节点宕机 / 高延迟)
- 集群性能下降
- etcd 写入操作需要多数节点确认(Raft 共识机制),部分节点故障会导致写入延迟升高;
- Kubernetes API Server 与 etcd 交互变慢,表现为
kubectl命令响应卡顿、Pod 创建 / 删除耗时增加。
- 集群冗余能力丧失
- 剩余正常节点成为 "单点",若再出现节点故障,会直接触发集群不可用;
- etcd 数据备份任务可能失败,增加数据丢失风险。
- 日志频繁告警
- API Server、Controller Manager 等组件会在日志中报
etcdserver: request timed out或connection refused错误; - etcd 自身会输出
leader election(选主)相关日志,集群处于不稳定状态。
重度故障:etcd 集群失去多数节点(脑裂 / 全部宕机)
当 etcd 集群多数节点故障 (如 3 节点集群 2 个节点宕机)或发生脑裂,集群将丧失共识能力,此时 Kubernetes 会完全不可用,具体表现为:
- API Server 无法提供服务
kubectl命令执行失败,报the server is currently unable to handle the request (get pods)或connection to etcd failed;- 所有基于 Kubernetes API 的操作(如部署应用、扩容副本、删除资源)全部无法执行。
- 集群状态 "冻结"
- 已运行的 Pod 可能继续工作(若不依赖 API Server),但无法进行任何调度、重启、扩缩容操作;
- 节点心跳无法更新,Controller Manager 无法识别节点故障,不会触发 Pod 漂移;
- Service 无法更新 Endpoints,新 Pod 无法被负载均衡识别,导致服务访问失败。
- 数据一致性风险
- 脑裂场景下,etcd 可能出现多个 "伪 leader",不同节点数据不一致;
- 强制恢复后可能出现数据丢失或配置错乱,如 Pod 记录消失、Service 路由规则异常。
特殊故障:etcd 数据损坏 / 元数据丢失
如果 etcd 数据目录损坏、元数据被篡改(如之前提到的 LVM 误操作导致数据区覆盖),会引发更严重的不可逆问题:
- 集群无法启动
- API Server 启动时无法从 etcd 加载集群配置,直接崩溃退出;
- etcd 节点启动失败,报
database snapshot file corrupt或metadata not found错误。
- 数据无法恢复
- 若无有效备份,集群的所有配置、状态数据会完全丢失,相当于需要重建集群;
- 即使有备份,恢复后也可能存在数据不一致,需要手动修复 Pod、Service 等资源。
- 依赖组件全部异常
- kubelet 无法从 API Server 获取 Pod 定义,已运行的 Pod 会被强制停止;
- CoreDNS、Ingress Controller 等核心组件无法工作,整个集群的网络服务瘫痪。
备份措施
如果etcd故障会造成k8s集群的瘫痪,所以etcd的日常监控和数据备份至关重要,以下是生产环境使用的巡检和备份脚本。
1.etcd的巡检脚本
# 检查3节点Etcd集群健康状态
ETCDCTL_API=3 etcdctl --endpoints=https://10.10.19.61:2379,https://10.10.19.62:2379,https://10.10.19.63:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
endpoint health --write-out=table
# 检查leader分布(避免leader长期固定在某节点,或频繁切换)
ETCDCTL_API=3 etcdctl --endpoints=https://10.10.19.61:2379,https://10.10.19.62:2379,https://10.10.19.63:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
endpoint status --write-out=table
# 查看集群成员信息,包括客户端URL和Peer URL
ETCDCTL_API=3 etcdctl --endpoints=https://10.10.19.61:2379,https://10.10.19.62:2379,https://10.10.19.63:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
member list --write-out=table
2.etcd的备份脚本
#!/bin/bash
# 配置参数(根据自己集群修改)
ETCD_ENDPOINTS="https://10.10.19.61:2379" #etcd地址
ETCD_CERTS_DIR="/etc/kubernetes/pki/etcd"
BACKUP_DIR="/data/etcd-backup" # 本地备份目录
REMOTE_BACKUP="false" # 是否开启异地备份(true/false)
REMOTE_PATH="root@10.10.10.44:/data/etcd-backup/" # 异地存储地址(需配置免密登录)
RETENTION_DAYS=7 # 保留7天备份
# 1. 创建备份目录
mkdir -p ${BACKUP_DIR} || { echo "创建备份目录失败!"; exit 1; }
# 2. 定义备份文件名(含时间戳,避免覆盖)
BACKUP_FILE="${BACKUP_DIR}/etcd-backup-$(date +%Y%m%d%H%M%S).db"
# 3. 执行etcd备份(核心命令)
echo "开始备份etcd数据..."
ETCDCTL_API=3 /usr/local/bin/etcdctl --endpoints=${ETCD_ENDPOINTS} \
--cacert=${ETCD_CERTS_DIR}/ca.crt \
--cert=${ETCD_CERTS_DIR}/server.crt \
--key=${ETCD_CERTS_DIR}/server.key \
snapshot save ${BACKUP_FILE}
# 4. 验证备份是否成功
if [ $? -eq 0 ]; then
echo "备份成功!备份文件:${BACKUP_FILE}"
# 验证备份文件有效性
etcdctl --endpoints=${ETCD_ENDPOINTS} \
--cacert=${ETCD_CERTS_DIR}/ca.crt \
--cert=${ETCD_CERTS_DIR}/server.crt \
--key=${ETCD_CERTS_DIR}/server.key \
snapshot status ${BACKUP_FILE}
else
echo "备份失败!请检查etcd连接和权限"
rm -f ${BACKUP_FILE} # 删除失败的备份文件
exit 1
fi
# 5. 清理旧备份(保留7天)
echo "清理${RETENTION_DAYS}天前的旧备份..."
find ${BACKUP_DIR} -name "etcd-backup-*.db" -mtime +${RETENTION_DAYS} -delete
# 6. 异地备份(可选,需提前配置master到远程服务器的免密登录)
if [ "${REMOTE_BACKUP}" = "true" ]; then
scp ${BACKUP_FILE} ${REMOTE_PATH}
if [ $? -eq 0 ]; then
echo "异地备份成功!备份文件已同步至:${REMOTE_PATH}"
else
echo "异地备份失败!请检查免密登录配置"
fi
fi
echo "etcd备份任务完成!"
exit 0
保存脚本,做成定时任务
# 1. 保存脚本
vim /usr/local/bin/etcd-backup.sh
# 2. 赋予执行权限
chmod +x /usr/local/bin/etcd-backup.sh
# 3. 修改脚本中的配置参数(ETCD_ENDPOINTS、BACKUP_DIR等)
# 4. 执行备份
/usr/local/bin/etcd-backup.sh
# 5. 配置定时任务(每天凌晨2点自动备份)
crontab -e
# 添加以下内容:
0 2 * * * /usr/local/bin/etcd-backup.sh >> /var/log/etcd-backup.log 2>&1
如果出现定时任务不能执行,但是手工执行脚本能够正常备份,需要检查脚本etcdctl命令的路径,指定etcdctl全路径和ETCDCTL_API=3,本脚本etcdctl的路径是ETCDCTL_API=3 /usr/local/bin/
数据恢复
一般k8s的高可用集群都是多master节点,etcd集群同样也是3节点或者5节点集群,单节点一般是在测试环境。
在高可用环境下,如果出现etcd的单节点故障或者脑裂问题,在数据不存在损坏的情况下,恢复etcd的进程大部分都可恢复。如果出现etcd的数据文件损坏或者元数据异常,则需要执行以下的etcd数据恢复流程。
1.测试环境的单节点etcd的异常
恢复前必须停止K8s核心组件,否则会导致数据冲突!恢复后需重启组件。
#1.停止服务,备份etcd的现有目录
systemctl stop kubelet
docker stop $(docker ps -q --filter name=k8s_kube-*) #在docker环境下和下面的非docker环境根据环境玄策操作
# 高版本的k8s默认不使用docker,使用containerd版本使用crictl操作
crictl stop $(crictl pods -q) # 停止沙箱(pause容器)
crictl stop $(crictl ps -q) # 停止业务容器(apiserver/etcd等)
##生产环境组件多、元数据复杂,残留容器极易导致恢复后组件启动失败,删了更稳,具体视情况而定,不是非必须要删除
crictl rm $(crictl ps -aq) # 删除,可选
crictl rmp $(crictl pods -aq) # 删除,可选
#2.备份现有目录
mv /var/lib/etcd /var/lib/etcd.bak.$(date +%Y%m%d)
#3.根据备份文件恢复etcd数据
BACKUP_FILE="/data/etcd-backup/etcd-backup-20251220100000.db" # 替换为你的备份文件路径
ETCDCTL_API=3 /usr/local/bin/etcdctl --endpoints=https://10.10.19.61:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot restore ${BACKUP_FILE} \
--data-dir=/var/lib/etcd \
--name=k8s-master-01 \ # 节点名称,和/etc/kubernetes/manifests/etcd.yaml中的name一致
--initial-cluster=master-1=https://10.10.19.61:2380 \
--initial-cluster-token=etcd-cluster-token \
--initial-advertise-peer-urls=https://10.10.19.61:2380
# 4. 重启K8s组件
systemctl start kubelet
# 5. 验证恢复结果
sleep 30 # 等待组件启动
kubectl get nodes # 能正常显示节点则恢复成功
kubectl get pods -A # 查看所有Pod状态,确认核心组件正常运行
2.在高可用etcd环境的etcd的恢复步骤
高可用恢复需在所有master节点 执行,核心是"先恢复一个节点,再同步到其他节点"。恢复前必须停止K8s核心组件,否则会导致数据冲突!恢复后需重启组件,停止所有master节点的kubelet和K8s组件(先操作master-1,再操作其他master)。具体操作如下
停止服务,三台master节点都要操作
#Master1节点
systemctl stop kubelet
docker stop $(docker ps -q --filter name=k8s_kube-*)
mv /var/lib/etcd /var/lib/etcd.bak.$(date +%Y%m%d)
#Master2节点
systemctl stop kubelet
docker stop $(docker ps -q --filter name=k8s_kube-*)
mv /var/lib/etcd /var/lib/etcd.bak.$(date +%Y%m%d)
#Master3节点
systemctl stop kubelet
docker stop $(docker ps -q --filter name=k8s_kube-*)
mv /var/lib/etcd /var/lib/etcd.bak.$(date +%Y%m%d)
#以下是针对containerd环境的停止服务
crictl stop $(crictl pods -q) # 停止沙箱(pause容器)
crictl stop $(crictl ps -q) # 停止业务容器(apiserver/etcd等)
##生产环境组件多、元数据复杂,残留容器极易导致恢复后组件启动失败,删了更稳,具体视情况而定,不是非必须要删除
crictl rm $(crictl ps -aq) # 删除,可选
crictl rmp $(crictl pods -aq) # 删除,可选
恢复master1的etcd数据
#恢复数据
BACKUP_FILE="/data/etcd-backup/etcd-backup-20260107100000.db"
ETCDCTL_API=3 /usr/local/bin/etcdctl --endpoints=https://10.10.19.61:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot restore ${BACKUP_FILE} \
--data-dir=/var/lib/etcd \
--name=k8s-master-01 \ --initial-cluster=k8s-master-01=https://10.10.19.61:2380,k8s-master-02=https://10.10.19.62:2380,k8s-master-03=https://10.10.19.63:2380 \
--initial-cluster-token=etcd-cluster-token \
--initial-advertise-peer-urls=https://10.10.19.61:2380
#启动master-1的kubelet
systemctl start kubelet
恢复master2和master数据的数据,通过etcd集群加入,自动同步master1的数据
mkdir -p /var/lib/etcd
systemctl start kubelet
验证高可用恢复结果
ETCDCTL_API=3 /usr/local/bin/etcdctl --endpoints=https://10.10.19.61:2379,https://10.10.19.62:2379,https://10.10.19.63:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
endpoint health # 所有节点显示healthy则同步成功
kubectl get nodes # 验证集群资源正常