
这几年做 Linux 运维我越来越觉得 etcd 属于那种“平时没人想起、出事就要命”的组件。Kubernetes 集群跑得好好的时候你几乎感受不到 etcd 的存在可一旦它出故障apiserver 连不上、节点状态全部异常、整个集群直接“失忆”那时候再想补备份就来不及了。这篇文章结合 etcd 3.5.15 集群的日常维护经验把 Linux 运维场景下 etcd 的数据备份与恢复完整梳理一遍包括备份原理、备份脚本、恢复流程、常见坑点希望能帮你把这条“保命技能”真正落地。文章内容不区分你是手动部署的 etcd 集群还是 Kubernetes 静态 Pod 方式部署的 etcd核心思路和命令都通用只是路径、证书、启动方式有些差异我会分别说明。1. 为什么备份 etcd 是集群运维的“保命技能”1.1 etcd 在集群里到底承担什么角色etcd 是一个分布式、强一致性的键值存储系统底层基于 Raft 协议专门用来保存关键元数据。在 Kubernetes 里它承担的是“集群大脑”的职责所有 API 对象包括 Node 状态、Pod 定义、Service、ConfigMap、Secret、RBAC 权限、命名空间等全部存在 etcd 里。它保存的数据量通常不大但精度要求极高丢一个 key 都可能引发连锁故障。很多小伙伴容易把 etcd 和业务数据库混为一谈。业务数据库挂了你可以切从库、做恢复影响是局部的但 etcd 是整个控制面的数据中枢它挂了意味着整个集群的“记忆”丢失计算节点还在上层调度、服务发现、配置下发全部停摆。除了 Kubernetes很多分布式系统也依赖 etcd比如 OpenStack、微服务注册中心、分布式锁服务等所以掌握 etcd 的备份与恢复是 Linux 运维的一项基础功。我习惯把 etcd 比作一个人的记忆身体计算节点都好好的但记忆没了人就没法正常工作和交流。而且 etcd 的数据恢复和 MySQL 不太一样它没有类似于 binlog 那样便利的增量归档机制至少官方常用工具链没有所以日常备份策略非常重要绝不能等到出事再想办法。1.2 常见的数据丢失场景我做运维这些年遇到过和听说过的 etcd 数据丢失/损坏场景基本都是下面这几类磁盘故障或底层存储损坏。本地盘坏道、SSD 寿命耗尽、云盘异常都能直接导致 etcd 数据目录里的 WAL 或 snapshot 文件损坏。数据目录被误删/误清。比如排查磁盘空间时顺手rm -rf了挂载目录或者容器被重建时数据卷没挂对把 etcd 数据卷覆盖了。误操作删除关键数据。有人通过etcdctl del删掉了一批前缀 key或者删掉了某个 namespace导致上层业务配置全部丢失。多数节点故障导致选主失败。三节点集群挂了两台Raft 无法达成多数派集群进入只读不可写状态甚至完全不可用。断电、重启后 WAL 文件不一致。非正常关机后部分日志还没落盘或者事务未提交完启动时 etcd 直接拒绝服务。磁盘空间写满。etcd 数据目录所在分区满了之后它会 panic 或持续刷错误日志严重时直接不可写。升级或回滚时操作失误。版本跳级、配置不兼容导致数据目录无法被新版本识别。这些场景里除了少数情况可以通过etcdctl在线修复大多数都依赖备份数据来做恢复。所以备份不是可选项而是集群上线第一天就要做好的基本操作。2. 备份方案设计快照备份和恢复的实现逻辑2.1 etcd 快照备份的原理etcd 的数据存储分为内存状态、WAL 日志文件和快照文件三部分。内存里保存着最新的多版本索引WAL 记录的是每一次写入预写日志snapshot 则是某个时间点的一致性状态持久化文件。由于 etcd 的数据一致性依赖于 Raft 日志WAL 和 snapshot 文件如果不一致恢复会非常麻烦。官方推荐的备份方式是使用etcdctl snapshot save命令。它会通过 etcd 的 API 从节点上导出一份“一致性视角”的数据快照而不是简单复制文件。直接cp -r运行中的 etcd 数据目录是极不推荐的因为 etcd 一直在写入复制到的 WAL 和 snapshot 文件可能不在同一个时间点上放在一起就是一堆不一致的垃圾数据。关于备份频率我的建议是生产环境至少每 4~6 小时做一次全量快照变更频繁的集群可以缩短到 1 小时一次同时做每日归档本地保留 7 天以上并且同步到其他机器或对象存储。因为 etcd 快照是全量快照没有方便的增量拼接方案频率太低损失的数据就越多。2.2 备份策略设计在设计备份策略时要考虑几个问题备份放到哪里、保留多久、如何校验、如何恢复。本地放一份是底线但千万别只放本机万一整台机器磁盘坏了备份也一起没了那就等于没备份。我通常的做法是脚本先在 etcd 节点本地生成快照文件压缩后同步到一台专门的备份服务器或对象存储本地只保留最近 N 天。备份文件最好以时间戳命名例如etcd-snapshot-20250616-040001.db.gz同时一定要在备份后做有效性校验光生成文件不算数要确保 revision 非 0、文件大小合理、能正常读取。这个校验步骤我强烈建议加在脚本里不然定时任务跑了半年实际产物全是坏文件你都不知道等到要恢复的那一天才会发现惨了。etcd 3.5.x 默认启用 v3 API3.4 之后连etcdctl默认也是 v3不需要再手动export ETCDCTL_API3但如果你机器上还保留了旧版本的 etcdctl或者脚本是从老项目里拷来的这行还是要带上避免执行成 v2 命令导致备份失败。3. 实操etcd 3.5.15 集群环境准备与备份3.1 环境准备与版本选择etcd 3.5.x 是目前生产环境里比较稳妥的版本线相比早期版本解决了不少稳定性问题。我们这边线上用的就是 3.5.15运行了几个月WAL 和 snapshot 表现都正常。选择版本时要注意etcdctl 的版本最好和 etcd server 一致大版本不一致有时候会出现兼容性问题。假设我有一个三节点的 etcd 集群节点信息如下节点IPclient 端口peer 端口etcd010.0.0.123792380etcd110.0.0.223792380etcd210.0.0.323792380在执行备份前先检查集群健康状态etcdctl --endpointshttps://10.0.0.1:2379,https://10.0.0.2:2379,https://10.0.0.3:2379 \ --cacert/etc/ssl/etcd/ca.pem \ --cert/etc/ssl/etcd/etcd-server.pem \ --key/etc/ssl/etcd/etcd-server-key.pem \ endpoint health --cluster如果输出每个节点都是healthy再检查一下成员列表etcdctl member list -w table这里-w table是 etcdctl 3.4 以上支持的输出格式列清晰适合人眼查看。每次备份前这步可以不做但至少要在脚本里加上节点健康检查避免在集群已经异常的情况下备份出“残废快照”。3.2 手动备份命令单次手动备份非常简单但要注意证书参数。下面是我常用的完整命令export ETCDCTL_API3 export ETCDCTL_CACERT/etc/ssl/etcd/ca.pem export ETCDCTL_CERT/etc/ssl/etcd/etcd-server.pem export ETCDCTL_KEY/etc/ssl/etcd/etcd-server-key.pem etcdctl --endpointshttps://127.0.0.1:2379 \ snapshot save /backup/etcd-snapshot-$(date %Y%m%d-%H%M%S).db我建议在执行备份时指定127.0.0.1:2379不要走远程 endpoint。一方面减少网络因素干扰另一方面即使集群里其他节点状态不对至少本机节点还能正常响应备份成功率更高。备份完成后立刻用snapshot status校验文件etcdctl snapshot status /backup/etcd-snapshot-20260616-040001.db -w table正常输出会包含Hash、Revision、TotalKeys、Size等信息Revision 不能为 0TotalKeys 也不会是 0。如果发现 Revision 是 0 或者文件大小异常小那就是备份失败了要马上排查。3.3 定时任务自动化备份手动备份只能救急定时自动化才是日常保障。下面这个脚本是我在用的简化版本可以直接抄作业#!/usr/bin/env bash set -euo pipefail # 配置区 ETCD_ENDPOINThttps://127.0.0.1:2379 ETCD_CA/etc/ssl/etcd/ca.pem ETCD_CERT/etc/ssl/etcd/etcd-server.pem ETCD_KEY/etc/ssl/etcd/etcd-server-key.pem BACKUP_DIR/data/etcd-backups KEEP_DAYS7 REMOTE_HOSTbackup01 REMOTE_DIR/data/backups/etcd # export ETCDCTL_API3 export ETCDCTL_CACERT$ETCD_CA export ETCDCTL_CERT$ETCD_CERT export ETCDCTL_KEY$ETCD_KEY TIMESTAMP$(date %Y%m%d-%H%M%S) SNAPSHOT_FILE${BACKUP_DIR}/etcd-snapshot-${TIMESTAMP}.db COMPRESSED_FILE${SNAPSHOT_FILE}.gz mkdir -p ${BACKUP_DIR} etcdctl --endpoints${ETCD_ENDPOINT} snapshot save ${SNAPSHOT_FILE} # 校验快照文件revision 为 0 直接失败退出 snapshot_status$(etcdctl snapshot status ${SNAPSHOT_FILE} -w json) revision$(echo $snapshot_status | jq -r .Revision) if [ -z $revision ] || [ $revision 0 ]; then echo [ERROR] snapshot revision is 0, backup invalid exit 1 fi gzip -f ${SNAPSHOT_FILE} # 清理本地 N 天前的备份 find ${BACKUP_DIR} -name etcd-snapshot-*.db.gz -mtime ${KEEP_DAYS} -delete # 同步到远程主机 if [ -n ${REMOTE_HOST} ]; then scp -q ${COMPRESSED_FILE} ${REMOTE_HOST}:${REMOTE_DIR}/ fi echo [$(date %Y-%m-%d %H:%M:%S)] backup ok: ${COMPRESSED_FILE}脚本里依赖jq解析 JSON如果机器上没装用 Python 也可以但要确保环境稳定。set -euo pipefail一定要加上尤其set -e能在命令失败时立刻退出不会让脚本带着错误继续往下跑。定时任务用 crontab0 */4 * * * /opt/scripts/etcd-backup.sh /var/log/etcd-backup.log 21这里我选择每 4 小时做一次全量备份每天 6 份保留 7 天本地最多 42 份左右占用空间不大。如果集群数据量很大比如已经超过 5GB可以适当降低频率但要评估数据丢失的风险。4. 实操etcd 集群数据恢复全流程4.1 恢复前的准备与判断先说清楚什么情况下需要走完整恢复流程。如果你发现 etcd 日志报错、容器反复重启、etcdctl endpoint health无法通过且确认是数据目录损坏或节点数据不一致那基本就要用快照来恢复了。恢复前最重要的原则是不要轻易删除旧数据目录。我通常先把旧目录mv改名比如/var/lib/etcd改成/var/lib/etcd.bak.日期这样就算恢复失败还能退回去分析原因。很多人一上来就rm -rf等发现恢复出来的数据有问题连最初现场都没了悔都悔不过来。另外要明确一个概念etcdctl snapshot restore不是简单地把快照文件“解压”到数据目录它会重新生成集群成员 ID、集群 ID并重建 WAL 和 snapshot 文件。所以恢复后的 etcd 集群对上层系统来说相当于一个“全新集群”只是数据内容来自快照。这也是为什么恢复后需要将节点配置里的--initial-cluster-token换成新值避免和旧的集群元数据冲突。4.2 数据恢复完整步骤我以三节点全部损坏的极端场景为例假设快照文件是/backup/etcd-snapshot-20260616-040001.db三个节点信息同上。第一步在三个节点上分别执行 restore。在 etcd0 上执行etcdctl snapshot restore /backup/etcd-snapshot-20260616-040001.db \ --data-dir /var/lib/etcd \ --name etcd0 \ --initial-cluster etcd0http://10.0.0.1:2380,etcd1http://10.0.0.2:2380,etcd2http://10.0.0.3:2380 \ --initial-cluster-token etcd-cluster-restore \ --initial-advertise-peer-urls http://10.0.0.1:2380在 etcd1 上执行同样的命令但要把--name改为etcd1、--initial-advertise-peer-urls改为http://10.0.0.2:2380。etcd2 同理。注意三个节点 restore 时用的--initial-cluster参数要完全一样包含全部三个节点--initial-cluster-token也要一致否则节点间互相认不出对方。第二步处理旧数据目录。先在每个节点上把旧数据目录移走mv /var/lib/etcd /var/lib/etcd.bak.$(date %Y%m%d%H%M%S)然后确认 restore 生成的新目录路径正确。如果刚才 restore 时指定的--data-dir /var/lib/etcd而这个路径已经被移走了那就没问题如果 restore 时用了临时目录需要手动把数据挪到 etcd 实际使用的路径。第三步启动 etcd 服务。如果是 systemd 管理模式systemctl start etcd systemctl status etcd如果是 Kubernetes 静态 Pod 模式处理方式略有不同下面单独说。第四步验证集群状态。etcdctl endpoint health --cluster etcdctl member list -w table如果所有节点都是 healthy成员正常整个集群就基本恢复成功了。4.3 Kubernetes 静态 Pod 场景下的恢复要点很多 Kubernetes 集群的 etcd 是由 kubelet 通过静态 Pod 方式拉起数据目录通常在宿主机/var/lib/etcdPod manifest 在/etc/kubernetes/manifests/etcd.yaml。这种情况下操作顺序要注意。先把 etcd 的静态 Pod 清单移走不让 kubelet 自动拉起mv /etc/kubernetes/manifests/etcd.yaml /root/等几秒钟确认 etcd 容器已经退出。然后按上面的 restore 流程恢复数据到/var/lib/etcd恢复后检查目录属主如果是 etcd 用户运行需要chown -R etcd:etcd /var/lib/etcd最后把 yaml 文件移回/etc/kubernetes/manifests/kubelet 会自动检测并创建 etcd Pod。如果你恢复后容器一直在 CrashLoopBackOff优先看日志crictl logs etcd-container-id大部分静态 Pod 恢复失败都和权限、证书、--initial-cluster配置不对有关。4.4 恢复后的集群验证etcd 恢复成功不代表整个集群就完全正常了还要验证上层应用和 Kubernetes 控制面是否恢复。用kubectl get nodes和kubectl get pods -A检查 K8s 集群状态。如果 apiserver 一直连接 etcd 失败看一下 apiserver 日志里有没有证书报错或连接拒绝。同时要提醒业务方恢复操作本质上是把数据回退到快照时刻快照之后的新增数据和变更会丢失这是无法避免的。所以恢复完成后最好和相关负责人确认一下数据变更范围有些数据可能需要重新录入或从其他系统同步回来。5. 常见问题排查与避坑实录5.1 常见问题与解决方案速查表我把实际操作中遇到的高频问题整理成了表格方便你排查时快速对照。问题现象可能原因解决方法snapshot save执行失败证书路径错误、ETCDCTL_API未设置、磁盘空间不足、endpoint 不可达先etcdctl endpoint health验证连通性检查证书权限确认备份目录剩余空间restore 后 etcd 启动报 permission denied数据目录属主不是 etcd 运行用户手动chown -R etcd:etcd /var/lib/etcd恢复后节点加入不了集群--initial-cluster参数不一致或--initial-cluster-token未更换三个节点的--initial-cluster必须完全一致token 要换新静态 Pod 场景 etcd 容器反复重启manifest 中启动参数是旧集群的配置修改 manifest 中的--initial-cluster、--initial-cluster-token或直接对照 restore 参数修正备份文件 revision 为 0备份时 etcd 处于异常状态、endpoint 选错检查集群健康状态备份前执行endpoint health确认 healthy恢复后 apiserver 连不上 etcd证书 SAN/IP 不匹配、endpoint 配置错误、TLS 校验失败检查 apiserver 的--etcd-servers地址确保证书里的 IP 和实际访问 IP 匹配集群间时钟偏差大节点反复超时节点时间不同步部署 NTP 服务确保所有节点时间同步恢复后 K8s 资源状态异常快照时间点和当前状态不一致控制器尚未 reconcile等待几分钟让控制器自动协调必要时手动删除异常 Pod 触发重建5.2 我踩过的坑和独家心得有一个坑我必须单独说备份脚本里忘了设置ETCDCTL_API3。那台机器上装了旧版本的 etcdctl默认走的 v2 API定时任务跑了三天全部报错但因为脚本没有加失败告警我一直没发现。后来加了 revision 校验和失败告警才把这个问题暴露出来。现在我的脚本里不仅校验 revision还会在失败时通过企业微信机器人发告警确保任何一次备份失败都能第一时间知道。还有一个恢复时的教训有次做恢复演练我脑子一热先把旧数据目录mv走了但 restore 后的新目录因为权限问题启动失败。还好旧目录没有删我赶紧把旧目录改回来才避免事故。从那以后我给自己定了个死规矩任何恢复操作前必须确认旧数据目录的备份已经完整保留不急着删。说到时间同步再多说一句。etcd 对节点间时钟偏差非常敏感恢复后的集群尤其要注意如果节点间时间差超过 Raft 心跳间隔极容易造成 leader 频繁切换。我遇到过恢复后 node 状态一直 NotReady最后排查发现是其中一台机器 NTP 服务没起来时间快了十几秒。所以恢复完成后第一件事除了检查 etcd也要确认时间同步。最后再分享一个小习惯每次要在集群上做危险操作比如删除 namespace、清理 etcd 大 key、升级控制面组件之前我都会手动打一个快照文件命名加上备注例如etcd-snapshot-20260616-100000-pre-namespace-cleanup.db。这样一旦操作失误恢复目标非常明确不用从一个模糊的时间点里去猜数据状态。这个习惯救过我很多次你也可以直接用起来。