etcd 备份 + 恢复:K8s 灾难前的最后一道防线

起因

K8s 整个 cluster 状态存在 etcd:

  • 所有 deployment / service / configmap / secret
  • node 状态
  • 所有 namespace

etcd 挂 → cluster 整体瘫。
更恐怖:etcd 数据损坏(disk 错 / 误操作)→ 数据丢失。

定期 etcd backup + 演练 restore 是 cluster 运维基础功。
没做过 restore 演练等于没 backup。

看 etcd 状态

# kubeadm 部署的 cluster
sudo 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 status --write-out=table
+------------------------+------------+---------+---------+--------+
| ENDPOINT               | DB SIZE    | LEADER  | RAFT IDX| ALARMS |
+------------------------+------------+---------+---------+--------+
| 10.0.1.10:2379         | 250 MB     | true    | 123456  |        |
| 10.0.1.11:2379         | 250 MB     | false   | 123456  |        |
| 10.0.1.12:2379         | 250 MB     | false   | 123456  |        |
+------------------------+------------+---------+---------+--------+

3 个 endpoint 应一致 + 1 个 leader + 无 alarms。

创建 snapshot

sudo ETCDCTL_API=3 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 \
    snapshot save /backup/etcd-$(date +%Y%m%d-%H%M%S).db

Snapshot saved at /backup/etcd-...db

snapshot 是单 binary 文件,几百 MB 量级(小 cluster)。

自动化备份 (cronjob)

# /etc/cron.d/etcd-backup
0 */6 * * * root /usr/local/bin/etcd-backup.sh
#!/bin/bash
# /usr/local/bin/etcd-backup.sh
set -euo pipefail

BACKUP_DIR=/backup
RETENTION_DAYS=7
TIMESTAMP=$(date +%Y%m%d-%H%M%S)

ETCDCTL_API=3 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 \
    snapshot save "$BACKUP_DIR/etcd-$TIMESTAMP.db"

# 上传到 S3
aws s3 cp "$BACKUP_DIR/etcd-$TIMESTAMP.db" s3://my-backups/etcd/

# 删本地老备份
find $BACKUP_DIR -name 'etcd-*.db' -mtime +$RETENTION_DAYS -delete

# 删 S3 老备份(lifecycle policy 也行)
aws s3 ls s3://my-backups/etcd/ | awk '{print $4}' | while read f; do
    days_old=$((($(date +%s) - $(date -d $(echo $f | sed 's/etcd-\(....\)\(..\)\(..\)-.*/\1-\2-\3/') +%s)) / 86400))
    if [ $days_old -gt 30 ]; then
        aws s3 rm s3://my-backups/etcd/$f
    fi
done

每 6 小时备份 + 7 天本地 + 30 天 S3。

restore (灾难恢复)

演练步骤(请在测试环境跑过几次):

# 1. 停所有 etcd 节点的 etcd(kubeadm 用 static pod 控制)
sudo mv /etc/kubernetes/manifests/etcd.yaml /tmp/

# 等几秒确认 etcd 停了
sudo crictl ps | grep etcd

# 2. 备份当前 data dir
sudo mv /var/lib/etcd /var/lib/etcd.bak

# 3. restore snapshot
sudo ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-xxx.db \
    --data-dir /var/lib/etcd \
    --name etcd-node-1 \
    --initial-cluster etcd-node-1=https://10.0.1.10:2380,etcd-node-2=https://10.0.1.11:2380,etcd-node-3=https://10.0.1.12:2380 \
    --initial-cluster-token etcd-cluster-1 \
    --initial-advertise-peer-urls https://10.0.1.10:2380

# 4. 在每节点上做 3 / restore

# 5. 启动 etcd
sudo mv /tmp/etcd.yaml /etc/kubernetes/manifests/

# 6. 验证
kubectl get nodes
kubectl get pods -A

cluster 回到 snapshot 时点状态。
snapshot 之后创建的资源(pod / deployment 等)丢失(除非应用 IaC / GitOps
能重 apply)。

velero (更高层备份)

etcd snapshot 是 raw K8s API state。
Velero:备份 + restore K8s 资源 + PV 数据:

helm install velero vmware-tanzu/velero \
    --namespace velero --create-namespace \
    --set configuration.provider=aws \
    --set configuration.backupStorageLocation.bucket=my-backups \
    ...
# 备份 namespace
velero backup create myapp-backup --include-namespaces myapp

# 备份全集群(除 system)
velero backup create cluster-backup --exclude-namespaces kube-system,kube-public

# 列出
velero backup get

# restore
velero restore create --from-backup myapp-backup --namespace-mappings myapp:myapp-restore

Velero 优势 vs etcd snapshot:

  • 选择性备份(按 namespace / label)
  • restore 到不同 namespace / cluster
  • PV 数据快照(CSI snapshot 支持)
  • 跨 cluster migration

etcd snapshot 是最后兜底(整 cluster 灾难);
Velero 是日常运维(误删 namespace / 迁移 / 测试 restore)。

两者都做。

测试 restore 至关重要

经验:没演练过的 backup = 没 backup

每季度跑一次:

  1. spin up 临时 cluster
  2. 从 backup restore
  3. 验证应用能跑
  4. 文档化每步

第一次跑会发现:

  • 漏备份某 resource
  • restore script 步骤错
  • 某 PV 数据没快照

修完 → real disaster 时不慌。

etcd 调优

defrag (DB 碎片整理):

etcdctl defrag --endpoints=...

DB size 大但 key 少 → 碎片多。定期 defrag。
每 leader change / 大批量改后跑。

quota:

etcd --quota-backend-bytes=8589934592   # 8 GB

默认 2 GB → 大 cluster 不够。

monitor:

etcd_mvcc_db_total_size_in_bytes
etcd_disk_wal_fsync_duration_seconds
etcd_server_leader_changes_seen_total

leader changes 频繁 → 网络 / disk 问题。

多 cluster: 跨 region backup

# velero schedule
velero schedule create daily-backup --schedule="0 2 * * *" --ttl 720h \
    --include-namespaces myapp \
    --storage-location aws-prod-east

# 跨 region replication(备份不放同 region 防 region-wide 灾)
aws s3 cp s3://my-backups-east/... s3://my-backups-west/...

与 cloud managed (EKS / GKE / AKS)

cloud managed K8s 的 etcd 是 cloud provider 维护。
- 自动备份 / failover
- 但你不能直接 etcdctl snapshot

仍要用 Velero 备份 resource 层(防 namespace 误删 / restore 到其它
cluster)。

真实 incident case

某客户误执行 kubectl delete namespace prod

恢复:

  1. 看最近 velero backup(4 小时前)
  2. velero restore create --from-backup ... --include-namespaces prod
  3. 30 分钟后 deployment / service / configmap / secret 全回
  4. PV 数据完整(CSI snapshot)

无 backup → cluster 完蛋。有 backup + 演练 → 30 分钟恢复。

踩过的坑

  1. backup 没测:以为有 backup → 真灾难时发现 backup file 损坏 /
    不完整。定期 restore 测试。

  2. etcd snapshot 不包括 secret in etcd-encryption-config 外
    K8s 1.13+ 支持 etcd 加密。restore 后 encryption config 不存在
    → 解密失败。备份时同时 cp encryption config。

  3. velero 不备 PV 数据 by default:要装 CSI plugin 或者 restic
    integration。

  4. restore 后 IP 变:service 重新创建 ClusterIP 可能变 → DNS 改
    传播延迟。

  5. etcd full:DB size 超 quota → cluster read-only。defrag +
    alarm disarm

精确评价 共 0 人评价
可复现性
可复现 · 0 不可复现 · 0
文风
文风流畅 · 0 文风晦涩 · 0
立场
支持 · 0 反对 · 0

登录后即可对本帖作出评价。

评论区 0 条 · 所有人可在此交流

登录后参与评论。

还没有评论,来说两句。