k8s的etcd的一键备份和故障恢复

背景

现有的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 集群部分节点异常(单节点宕机 / 高延迟)

  1. 集群性能下降
  • etcd 写入操作需要多数节点确认(Raft 共识机制),部分节点故障会导致写入延迟升高;
  • Kubernetes API Server 与 etcd 交互变慢,表现为 kubectl 命令响应卡顿、Pod 创建 / 删除耗时增加。
  1. 集群冗余能力丧失
  • 剩余正常节点成为 "单点",若再出现节点故障,会直接触发集群不可用;
  • etcd 数据备份任务可能失败,增加数据丢失风险。
  1. 日志频繁告警
  • API Server、Controller Manager 等组件会在日志中报 etcdserver: request timed outconnection refused 错误;
  • etcd 自身会输出 leader election(选主)相关日志,集群处于不稳定状态。

重度故障:etcd 集群失去多数节点(脑裂 / 全部宕机)

当 etcd 集群多数节点故障 (如 3 节点集群 2 个节点宕机)或发生脑裂,集群将丧失共识能力,此时 Kubernetes 会完全不可用,具体表现为:

  1. API Server 无法提供服务
  • kubectl 命令执行失败,报 the server is currently unable to handle the request (get pods)connection to etcd failed
  • 所有基于 Kubernetes API 的操作(如部署应用、扩容副本、删除资源)全部无法执行。
  1. 集群状态 "冻结"
  • 已运行的 Pod 可能继续工作(若不依赖 API Server),但无法进行任何调度、重启、扩缩容操作;
  • 节点心跳无法更新,Controller Manager 无法识别节点故障,不会触发 Pod 漂移;
  • Service 无法更新 Endpoints,新 Pod 无法被负载均衡识别,导致服务访问失败。
  1. 数据一致性风险
  • 脑裂场景下,etcd 可能出现多个 "伪 leader",不同节点数据不一致;
  • 强制恢复后可能出现数据丢失或配置错乱,如 Pod 记录消失、Service 路由规则异常。

特殊故障:etcd 数据损坏 / 元数据丢失

如果 etcd 数据目录损坏、元数据被篡改(如之前提到的 LVM 误操作导致数据区覆盖),会引发更严重的不可逆问题

  1. 集群无法启动
  • API Server 启动时无法从 etcd 加载集群配置,直接崩溃退出;
  • etcd 节点启动失败,报 database snapshot file corruptmetadata not found 错误。
  1. 数据无法恢复
  • 若无有效备份,集群的所有配置、状态数据会完全丢失,相当于需要重建集群;
  • 即使有备份,恢复后也可能存在数据不一致,需要手动修复 Pod、Service 等资源。
  1. 依赖组件全部异常
  • 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  # 验证集群资源正常
相关推荐
伞啊伞17 小时前
k8s(四) Rancher 管理集群
容器·kubernetes·rancher
QWsin17 小时前
【k8s】为什么statefulSet初始化pod需要service name
云原生·容器·kubernetes
lin张17 小时前
k8s(三)pod详解(精简版)
云原生·容器·kubernetes
小二·17 小时前
前端 DevOps 完全指南:从 Docker 容器化到 GitHub Actions 自动化部署(Vue 3 + Vite)
前端·docker·devops
戴西软件18 小时前
戴西发布 DLM许可证加密防护软件V4.2让工业软件授权迈入并发调度与精细治理时代
运维·服务器·网络·数据库·人工智能·安全·云计算
youxiao_9018 小时前
kubernetes 插件、操作管理(二)
云原生·容器·kubernetes
NineData18 小时前
NineData云原生智能数据管理平台新功能发布|2025年12月版
数据库·云原生·数据库管理工具·ninedata·数据库迁移·数据库迁移工具·智能数据管理平台
Mr_sun.20 小时前
Day03——微服务网关与配置中心
微服务·云原生·架构
huaweichenai1 天前
docker部署kkFileView实现文件预览功能
运维·docker·容器