当前位置: 首页 > news >正文

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

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:- ReadOnlyManyresources:requests:storage: 1GivolumeName: static-pv-etcd-certs  # 手动绑定到上面的 PVstorageClassName: ""  # 与 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: 10GiaccessModes:- ReadWriteMany  # 读写,允许多节点访问persistentVolumeReclaimPolicy: Retainnfs:server: 10.0.0.6  # NFS 服务器 IPpath: /data/nfs-server/etcd-backup  # 手动创建的备份目录storageClassName: ""---apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: static-pvc-etcd-backup  # PVC 名称
spec:accessModes:- ReadWriteManyresources:requests:storage: 10GivolumeName: static-pv-etcd-backup  # 手动绑定到上面的 PVstorageClassName: ""
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>                          2m17sNAME                                                   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: certspersistentVolumeClaim:claimName: static-pvc-etcd-certs  # 引用静态证书 PVC- name: bakpersistentVolumeClaim:claimName: static-pvc-etcd-backup  # 引用静态备份 PVCcontainers:- name: etcd-backupimage: etcd-bak:v1imagePullPolicy: IfNotPresentvolumeMounts:- name: certsmountPath: /certsreadOnly: true- name: bakmountPath: /backupenv:- name: ETCD_HOSTvalue: "10.0.0.6"  # 你的 etcd 节点 IP- name: ETCD_PORTvalue: "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 集群数据,确保在数据异常时能够快速恢复业务。

http://www.dtcms.com/a/309034.html

相关文章:

  • 番茄项目3:完成了项目的数据库设计
  • npm报错:npm install 出现“npm WARN old lockfile”
  • ZED 2/2i 相机安装与调试完整指南 | Ubuntu 20.04 + CUDA 11.8
  • k8s云原生rook-ceph pvc快照与恢复(下)
  • 前端SWR策略:优化数据请求
  • STM32学习记录--Day5
  • AG-UI 协议全面解析--下一代 AI Agent 交互框架医疗应用分析(下)
  • 接口幂等性
  • 【Flutter】双路视频播放方案
  • 《R for Data Science (2e)》免费中文翻译 (第3章) --- Data transformation(1)
  • Jupyter Notebook 使用指南
  • Idea集成Jenkins Control插件,在IDEA中触发Jenkins中项目的构建
  • 【数据可视化-78】2025年上半年广东省各市GDP排名深度解析与可视化:Python + Pyecharts 深度洞察(含完整数据、代码)
  • 【Linux学习|黑马笔记|Day1】Linux初识、安装VMware Workstation、安装CentOS7、远程连接、虚拟机快照
  • Cradle:颠覆AI Agent 操作本地软件,AI驱动的通用计算机控制框架,如何让基础模型像人一样操作你的电脑?
  • 九、Maven入门学习记录
  • 嵌入式筑基之STM32启动流程
  • AG-UI 协议全面解析--下一代 AI Agent 交互框架医疗应用分析(上)
  • SQL注入SQLi-LABS 靶场less25a-28a详细通关攻略
  • LoRA低秩适配的原理
  • anaconda searchanaconda show | conda 检索包资源安装指定版本包指定源安装命令package
  • Mysql-视图,函数,存储过程,触发器
  • 日语学习-日语知识点小记-构建基础-JLPT-N3阶段(12):文法+单词
  • conda issue
  • C++-2025.7.31
  • LaTeX 表格制作全面指南
  • js防抖、节流和扁平化实现
  • 链特异性文库是什么?为什么它在转录组测序中越来越重要?
  • 【Kubernetes 指南】基础入门——Kubernetes 201(三)
  • 第13届蓝桥杯C++青少组中/高级组选拔赛2022年3月13日真题