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

发布于:2025-08-02 ⋅ 阅读:(15) ⋅ 点赞:(0)

在 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 可访问):

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 

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/):

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目录(证书需只读访问):

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目录(备份文件需读写):

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

# 创建静态 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工具和备份命令,与存储类型无关,直接复用之前的内容:

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"]
# 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*

构建镜像

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
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,内容如下:

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:

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

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

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

查看 CronJob 和 Pod 状态

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
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工具验证:

# 假设备份文件在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 节点备份原有数据

mv /var/lib/etcd /var/lib/etcd.bak
  1. 在第一个节点执行恢复

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为对应节点信息):

# 第二个节点示例
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 数据目录权限正确(否则可能启动失败):

chown -R 1000:1000 /var/lib/etcd  # etcd默认使用1000用户运行
  1. 启动控制平面组件

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. 验证集群状态
    确认恢复成功:

# 查看节点状态
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 集群数据,确保在数据异常时能够快速恢复业务。


网站公告

今日签到

点亮在社区的每一天
去签到