K8S周期性备份etcd数据实战案例

在 Kubernetes(K8S)集群的世界里,etcd 扮演着 "大脑" 的角色,它存储着整个集群的所有状态数据,从 Pod 的配置、服务的注册到网络策略的定义,无一不依赖于 etcd 的稳定运行。一旦 etcd 数据发生丢失或损坏,整个 K8S 集群可能陷入瘫痪,业务运行将遭受严重影响。​

然而,在实际运维过程中,误操作、硬件故障、网络异常等突发状况时有发生,这些都可能对 etcd 数据的完整性构成威胁。因此,为 etcd 数据构建一套可靠的备份机制,尤其是实现周期性的自动备份,成为保障 K8S 集群稳定性和业务连续性的关键环节。​

本文将聚焦于 K8S 环境下 etcd 数据的周期性备份,通过实战案例的形式,为大家详细展示如何搭建一套高效、稳定的自动备份方案,助力运维人员轻松应对数据安全挑战。

一、备份方案思路

这里后端存储使用Ceph的RBD或者对象存储效果更佳,操作都类似,所以这里演示的为PV-NFS。

本次方案的核心思路是利用 K8S 原生的 CronJob 实现周期性任务调度,通过etcdctl工具执行备份操作,并依赖 PV 共享存储持久化保存备份文件和证书。具体包括以下关键点:

  1. 备份工具 :使用 etcd 官方提供的etcdctl工具,支持 etcd 数据的快照备份;
  2. 调度控制:通过 K8S 的 CronJob 控制器定义备份周期(如每天凌晨 2 点),自动触发备份任务;
  3. 证书管理 :etcd 默认启用 TLS 加密通信,备份时需要挂载 CA 证书、客户端证书和密钥,确保etcdctl能正常访问 etcd 集群;
  4. 存储方案: 证书和备份文件通过csi-nfs-storageclass动态创建的 PVC 挂载,利用 NFS 共享存储实现持久化。

二、实战步骤详解

2.1 准备工作:在 NFS 服务器创建存储路径

NFS 存储依赖服务器端的目录共享,需先在 NFS 服务器的共享目录下创建证书和备份专用路径,并配置权限。

1. 确认 NFS 服务器共享目录

从用户环境可知,NFS 服务器的共享目录为/data/nfs-server(通过exportfs命令验证),所有 K8S 节点可访问该目录。

2. 创建证书和备份目录

在 NFS 服务器的/data/nfs-server下创建证书目录(etcd-certs)和备份目录(etcd-backup),并赋予读写权限(确保 K8S Pod 可访问):

bash 复制代码
root@k8s-master:~# mkdir -p /data/nfs-server/etcd-certs
root@k8s-master:~# mkdir -p /data/nfs-server/etcd-backup
root@k8s-master:~# ll /data/nfs-server/
total 32
drwxr-xr-x 8 root root 4096 Jul 30 16:21 ./
drwxr-xr-x 3 root root 4096 Jul 30 14:54 ../
-rw-r--r-- 1 root root    0 Jul 30 14:55 1.txt
drwxr-xr-x 2 root root 4096 Jul 30 16:21 etcd-backup/
drwxr-xr-x 2 root root 4096 Jul 30 16:20 etcd-certs/
drwxrwxrwx 2 root root 4096 Jul 30 15:03 pv1/
drwxrwxrwx 2 root root 4096 Jul 30 15:03 pv2/
drwxrwxrwx 2 root root 4096 Jul 30 15:03 pv3/
drwxr-xr-x 3 root root 4096 Jul 30 15:22 sc/

root@k8s-master:~# chmod 755 /data/nfs-server/etcd-certs
root@k8s-master:~# chmod 755 /data/nfs-server/etcd-backup

node节点保证可以正常使用NFS

bash 复制代码
root@k8s-node1:~# install -d /data/nfs-server/
root@k8s-node1:~# mount -t nfs 10.0.0.6:/data/nfs-server /data/nfs-server/
root@k8s-node1:~# ll /data/nfs-server/
total 32
drwxr-xr-x 8 root root 4096 Jul 30 16:21 ./
drwxr-xr-x 3 root root 4096 Jul 30 17:02 ../
-rw-r--r-- 1 root root    0 Jul 30 14:55 1.txt
drwxr-xr-x 2 root root 4096 Jul 30 16:21 etcd-backup/
drwxr-xr-x 2 root root 4096 Jul 30 16:22 etcd-certs/
drwxrwxrwx 2 root root 4096 Jul 30 15:03 pv1/
drwxrwxrwx 2 root root 4096 Jul 30 15:03 pv2/
drwxrwxrwx 2 root root 4096 Jul 30 15:03 pv3/
drwxr-xr-x 5 root root 4096 Jul 30 16:26 sc/
3. 复制 etcd 证书到 NFS 目录

将 etcd 证书复制到 NFS 的etcd-certs目录(证书路径仍为/etc/kubernetes/pki/etcd/):

bash 复制代码
root@k8s-master:~# cp /etc/kubernetes/pki/etcd/{ca.crt,peer.crt,peer.key} /data/nfs-server/etcd-certs/
root@k8s-master:~# ll /data/nfs-server/etcd-certs/
total 20
drwxr-xr-x 2 root root 4096 Jul 30 16:22 ./
drwxr-xr-x 8 root root 4096 Jul 30 16:21 ../
-rw-r--r-- 1 root root 1094 Jul 30 16:22 ca.crt
-rw-r--r-- 1 root root 1204 Jul 30 16:22 peer.crt
-rw------- 1 root root 1675 Jul 30 16:22 peer.key

2.2 编写 PVC 清单:基于 NFS 存储类创建持久卷

创建两个 PVC:一个用于挂载证书(只读),一个用于挂载备份目录(读写)。

1. 创建证书 PVC(etcd-certs-pvc)

新建static-pv-pvc-etcd-certs.yaml,用于挂载 NFS 的etcd-certs目录(证书需只读访问):

bash 复制代码
apiVersion: v1
kind: PersistentVolume
metadata:
  name: static-pv-etcd-certs  # PV 名称
spec:
  capacity:
    storage: 1Gi  # 容量仅为标识,不影响实际存储
  accessModes:
    - ReadOnlyMany  # 只读,允许多节点访问
  persistentVolumeReclaimPolicy: Retain  # 保留数据,删除 PVC 后不清理
  nfs:
    server: 10.0.0.6  # NFS 服务器 IP(从 PV 描述中获取)
    path: /data/nfs-server/etcd-certs  # 手动创建的证书目录
  storageClassName: ""  # 不指定存储类,避免动态供给

---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: static-pvc-etcd-certs  # PVC 名称
spec:
  accessModes:
    - ReadOnlyMany
  resources:
    requests:
      storage: 1Gi
  volumeName: static-pv-etcd-certs  # 手动绑定到上面的 PV
  storageClassName: ""  # 与 PV 保持一致
2. 创建备份 PVC(etcd-backup-pvc)

新建etcd-backup-pv-pvc.yaml,用于挂载 NFS 的etcd-backup目录(备份文件需读写):

bash 复制代码
apiVersion: v1
kind: PersistentVolume
metadata:
  name: static-pv-etcd-backup  # PV 名称
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteMany  # 读写,允许多节点访问
  persistentVolumeReclaimPolicy: Retain
  nfs:
    server: 10.0.0.6  # NFS 服务器 IP
    path: /data/nfs-server/etcd-backup  # 手动创建的备份目录
  storageClassName: ""

---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: static-pvc-etcd-backup  # PVC 名称
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi
  volumeName: static-pv-etcd-backup  # 手动绑定到上面的 PV
  storageClassName: ""
3. 创建 PVC 并验证状态

执行以下命令创建 PVC,并确认状态为Bound

bash 复制代码
# 创建静态 PV
kubectl apply -f static-pv-etcd-certs.yaml
kubectl apply -f static-pv-etcd-backup.yaml

# 创建静态 PVC(会自动绑定到同名 PV)
kubectl apply -f static-pvc-etcd-certs.yaml
kubectl apply -f static-pvc-etcd-backup.yaml

# 验证状态(确保 STATUS 为 Bound)
root@k8s-master:~# kubectl get pv,pvc
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS        CLAIM                                    STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
persistentvolume/static-pv-etcd-backup                      10Gi       RWX            Retain           Bound         default/static-pvc-etcd-backup                          <unset>                          2m17s
persistentvolume/static-pv-etcd-certs                       1Gi        ROX            Retain           Bound         default/static-pvc-etcd-certs                           <unset>                          2m17s


NAME                                                   STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
persistentvolumeclaim/static-pvc-etcd-backup           Bound     static-pv-etcd-backup                      10Gi       RWX                           <unset>                 2m16s
persistentvolumeclaim/static-pvc-etcd-certs            Bound     static-pv-etcd-certs                       1Gi        ROX                           <unset>                 2m17s
关键说明:
  • AccessModes
    • 证书目录用ReadOnlyMany(ROX):允许多个节点的 Pod 只读挂载(避免证书被意外修改);
    • 备份目录用ReadWriteMany(RWX):允许任意节点的 Pod 读写(CronJob 的 Pod 可能调度到不同节点)。
  • StorageClass :必须指定为用户已有的nfs-csi,确保 PVC 能动态绑定 NFS 卷。

2.3 编写 Dockerfile

Dockerfile 仅定义容器内的etcdctl工具和备份命令,与存储类型无关,直接复用之前的内容:

bash 复制代码
root@k8s-master:~/bak-etcd# cat Dockerfile 
FROM alpine:latest 
LABEL matainer="NovaCaoFc" \
      role="bak" \
      project="etcd"
COPY etcdctl /usr/local/bin/ 
CMD ["/bin/sh","-c","etcdctl --endpoints=${ETCD_HOST}:${ETCD_PORT} --cacert=/certs/ca.crt --cert=/certs/peer.crt --key=/certs/peer.key snapshot save /backup/etcd-`date +%F-%T`.backup"]
bash 复制代码
# Etcdctl可以去github下载他的二进制包,然后保留一下这个二进制命令即可。
# 官网地址: https://github.com/etcd-io/etcd/

root@k8s-master:~/bak-etcd# ll
total 16096
drwxr-xr-x  2 root root     4096 Jul 30 16:37 ./
drwx------ 13 root root     4096 Jul 30 16:37 ../
-rw-r--r--  1 root root      302 Jul 30 16:31 Dockerfile
-rwxr-xr-x  1 cao  cao  16466072 Jul 26 02:17 etcdctl*

构建镜像

bash 复制代码
root@k8s-master:~/bak-etcd# docker build -t etcd-bak:v1 .
[+] Building 0.1s (7/7) FINISHED                                                                                                              docker:default
 => [internal] load build definition from Dockerfile                                                                                                    0.0s
 => => transferring dockerfile: 353B                                                                                                                    0.0s
 => [internal] load metadata for docker.io/library/alpine:latest                                                                                        0.0s
 => [internal] load .dockerignore                                                                                                                       0.0s
 => => transferring context: 2B                                                                                                                         0.0s
 => [internal] load build context                                                                                                                       0.0s
 => => transferring context: 31B                                                                                                                        0.0s
 => [1/2] FROM docker.io/library/alpine:latest                                                                                                          0.0s
 => CACHED [2/2] COPY etcdctl /usr/local/bin/                                                                                                           0.0s
 => exporting to image                                                                                                                                  0.0s
 => => exporting layers                                                                                                                                 0.0s
 => => writing image sha256:8a29a144172a91e01eb81d8e540fb785e9749058be1d6336871036e9fb781adb                                                            0.0s
 => => naming to docker.io/library/etcd-bak:v1          

root@k8s-master:~/bak-etcd# docker images |grep bak
etcd-bak                                                                                         v1                                          8a29a144172a   7 minutes ago   24.8MB
bash 复制代码
root@k8s-master:~# docker save -o etcd-bak.tar etcd-bak:v1 
root@k8s-master:~# scp etcd-bak.tar 10.0.0.7:/root/
etcd-bak.tar                                                                                                               100%   24MB  73.5MB/s   00:00    
root@k8s-master:~# scp etcd-bak.tar 10.0.0.8:/root/
etcd-bak.tar                          

# 其他节点导入镜像
root@k8s-node1:~# docker load -i etcd-bak.tar 
418dccb7d85a: Loading layer [==================================================>]  8.596MB/8.596MB
39e2b60cb098: Loading layer [==================================================>]  16.47MB/16.47MB
Loaded image: etcd-bak:v1
             

在生产环境中,最好将构建好的镜像上传至我们的harbor仓库中,这样在下面的pod控制器中我们直接写入harbor地址就行了,不需要将镜像分配到其他K8S节点咯。

2.4 编写 CronJob 资源清单(适配 NFS 挂载)

操作步骤:

新建cj-backup-etcd-nfs.yaml,内容如下:

bash 复制代码
root@k8s-master:~# cat cj-backup-etcd-nfs.yaml 
apiVersion: batch/v1
kind: CronJob
metadata:
  name: backup-etcd
spec:
  schedule: "* * * * *" #先用每分钟测试是否可用
  jobTemplate:
    spec:
      template:
        spec:
          volumes:
          - name: certs
            persistentVolumeClaim:
              claimName: static-pvc-etcd-certs  # 引用静态证书 PVC
          - name: bak
            persistentVolumeClaim:
              claimName: static-pvc-etcd-backup  # 引用静态备份 PVC
          containers:
          - name: etcd-backup
            image: etcd-bak:v1
            imagePullPolicy: IfNotPresent
            volumeMounts:
            - name: certs
              mountPath: /certs
              readOnly: true
            - name: bak
              mountPath: /backup
            env:
            - name: ETCD_HOST
              value: "10.0.0.6"  # 你的 etcd 节点 IP
            - name: ETCD_PORT
              value: "2379"
          restartPolicy: OnFailure

部署 CronJob:

bash 复制代码
root@k8s-master:~# kubectl apply -f cj-backup-etcd-nfs.yaml
cronjob.batch/backup-etcd created

2.5 测试验证:确认 NFS 备份生效

核心确认备份文件是否生成在 NFS 的etcd-backup目录。

查看 CronJob 和 Pod 状态

bash 复制代码
root@k8s-master:~# kubectl get -f  cj-backup-etcd-nfs.yaml 
NAME          SCHEDULE    TIMEZONE   SUSPEND   ACTIVE   LAST SCHEDULE   AGE
backup-etcd   * * * * *   <none>     False     0        <none>          17s
bash 复制代码
root@k8s-master:~# kubectl get po
NAME                         READY   STATUS             RESTARTS      AGE
backup-etcd-29231120-9s9dm   0/1     Completed          0             3m4s
backup-etcd-29231121-nfjl5   0/1     CrashLoopBackOff   1 (11s ago)   2m4s
backup-etcd-29231122-gl4df   0/1     Completed          1             64s
backup-etcd-29231123-tfw5f   0/1     Completed          0             4s
root@k8s-master:~# ll /data/nfs-server/etcd-backup/
total 50824
drwxr-xr-x 2 root root     4096 Jul 30 17:23 ./
drwxr-xr-x 8 root root     4096 Jul 30 16:21 ../
-rw------- 1 root root 13004832 Jul 30 17:22 etcd-2025-07-30-09:22:50.backup
-rw------- 1 root root 13004832 Jul 30 17:22 etcd-2025-07-30-09:22:53.backup
-rw------- 1 root root 13004832 Jul 30 17:23 etcd-2025-07-30-09:23:01.backup

三、数据恢复步骤

1. 恢复前的准备工作

确认备份文件有效性

首先检查备份文件是否完整可用,使用etcdctl工具验证:

bash 复制代码
# 假设备份文件在NFS目录中,先复制到本地(如/master节点)
cp /data/nfs-server/etcd-backup/etcd-2025-07-30-09:22:50.backup /tmp/etcd-backup.db

# 验证备份文件(需使用与集群版本匹配的etcdctl)
etcdctl --write-out=table snapshot status /tmp/etcd-backup.db

停止 Kubernetes 控制平面组件

恢复 etcd 数据时,需停止所有依赖 etcd 的控制平面组件(避免数据写入冲突):

复制代码
# 在master节点执行(根据实际组件调整)
systemctl stop kubelet
docker stop $(docker ps -q --filter name=k8s_kube-apiserver*)
docker stop $(docker ps -q --filter name=k8s_kube-controller-manager*)
docker stop $(docker ps -q --filter name=k8s_kube-scheduler*)
docker stop $(docker ps -q --filter name=k8s_etcd*)

2. 执行恢复操作

多节点 etcd 集群恢复(适用于生产环境)

如果 etcd 是集群部署(3 节点),需要在所有节点执行恢复,确保集群信息一致:

  1. 在所有 etcd 节点备份原有数据
bash 复制代码
mv /var/lib/etcd /var/lib/etcd.bak
  1. 在第一个节点执行恢复
bash 复制代码
etcdctl snapshot restore /tmp/etcd-backup.db \
  --data-dir=/var/lib/etcd \
  --name=etcd-1 \  # 节点名称,如etcd-1/etcd-2/etcd-3
  --initial-cluster=etcd-1=https://10.0.0.6:2380,etcd-2=https://10.0.0.7:2380,etcd-3=https://10.0.0.8:2380 \
  --initial-cluster-token=etcd-cluster-token \
  --initial-advertise-peer-urls=https://10.0.0.6:2380  # 当前节点的peer地址
  1. 在其他节点执行恢复 (修改--name--initial-advertise-peer-urls为对应节点信息):
bash 复制代码
# 第二个节点示例
etcdctl snapshot restore /tmp/etcd-backup.db \
  --data-dir=/var/lib/etcd \
  --name=etcd-2 \
  --initial-cluster=etcd-1=https://10.0.0.6:2380,etcd-2=https://10.0.0.7:2380,etcd-3=https://10.0.0.8:2380 \
  --initial-cluster-token=etcd-cluster-token \
  --initial-advertise-peer-urls=https://10.0.0.7:2380

3 . 恢复后验证与启动服务

  1. 修复目录权限

    恢复后需确保 etcd 数据目录权限正确(否则可能启动失败):

bash 复制代码
chown -R 1000:1000 /var/lib/etcd  # etcd默认使用1000用户运行
  1. 启动控制平面组件
bash 复制代码
systemctl start kubelet
# 等待容器自动重启,或手动启动
docker start $(docker ps -aq --filter name=k8s_etcd*)
docker start $(docker ps -aq --filter name=k8s_kube-apiserver*)
docker start $(docker ps -aq --filter name=k8s_kube-controller-manager*)
docker start $(docker ps -aq --filter name=k8s_kube-scheduler*)
  1. 验证集群状态

    确认恢复成功:

bash 复制代码
# 查看节点状态
kubectl get nodes

# 查看Pod状态
kubectl get pods --all-namespaces

# 检查etcd集群健康状态
etcdctl --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/peer.crt \
  --key=/etc/kubernetes/pki/etcd/peer.key \
  endpoint health

4. 注意事项

  1. 恢复会覆盖现有数据:恢复操作会清空当前 etcd 数据,替换为备份文件中的内容,执行前务必确认备份文件是最新且正确的。
  2. 版本兼容性etcdctl版本必须与 etcd 集群版本完全一致(如 v3.5.0 的 etcd 需用 v3.5.0 的 etcdctl),否则可能导致恢复失败。
  3. 生产环境建议
    • 恢复前备份当前 etcd 数据(etcdctl snapshot save),避免操作失误无法回滚;
    • 恢复过程会导致集群短暂不可用,建议在业务低峰期执行;
    • 多节点 etcd 集群恢复后,需确认所有节点数据同步正常(etcdctl member list)。

通过以上步骤,即可利用备份的 etcd 快照文件恢复 Kubernetes 集群数据,确保在数据异常时能够快速恢复业务。

相关推荐
涛声依旧3931630 分钟前
构建部署kubernetes所需主机
linux·运维·云原生·容器·kubernetes
槐序深巷里打雨伞的人1 小时前
k8s中部署prometheus并监控k8s集群以及nginx案例
nginx·kubernetes·prometheus
阿里云云原生1 小时前
模型调用总闸门再次被投毒
云原生
阿里云云原生3 小时前
Harness 驾驭工程是 AI 平权的必经之路?
云原生
IT一氪3 小时前
K8s Admin:一个轻量级的多集群 Kubernetes 管理平台
云原生·容器·kubernetes
斯普信专业组3 小时前
Kubeasz快速部署k8s混合架构集群
java·架构·kubernetes
cool32004 小时前
二进制基于kubeasz部署 K8s 1.34.x 高可用集群实战指南-第四章:kubeasz部署集群k8s系统(4-4)
云原生·容器·kubernetes
cool32004 小时前
ETCD每天凌晨2点自动备份 + 手动完整恢复
云原生·容器·kubernetes
cool32005 小时前
Kubernetes集群节点扩容实战-kubeasz
java·开发语言·kubernetes
BPM_宏天低代码5 小时前
【宏天架构】CRM系统的API网关:基于Spring Cloud Gateway
微服务·云原生·架构