K8s 数据备份为什么这么难
K8s 部署的应用大部分是无状态的,但总有些"有状态"必须保:
- 数据库(MySQL/PostgreSQL):跑在 StatefulSet 上
- 配置中心(Nacos/Consul):配置不能丢
- 用户上传(图片/文件):存在 PVC 上
K8s 本身的 controller 只管"无状态"应用,PVC 数据完全靠用户自己备份。
1. 5 种 PVC 备份方式
| 方法 | 说明 | 支持所有卷类型? |
|---|
| Volume 快照(CSI Snapshot) | CSI 插件实现卷快照 | ✅ CSI 卷 |
| Velero | 用 Velero 备份 PVC(需配合对象存储) | ✅ 大多数 |
| Rsync / tar + 挂载 PVC | 手动挂载 PVC 拷贝 | ✅ 几乎所有 |
| GlusterFS 专用方法 | gluster snapshot / rsync | ⚠️ 仅 GlusterFS |
| StatefulSet + PVC 模板 | 应用层备份 | ⚠️ 依赖应用设计 |
1.1 选型决策
- 用了 Rook-Ceph / Portworx / Longhorn:CSI Snapshot(最快)
- 用了 NFS / GlusterFS 这种"无快照能力"的:Velero(标准方案)
- 临时备份/迁移:rsync + 挂载 PVC(最灵活)
- 应用本身有备份能力(mysqldump、etcd snapshot):让应用自己备份
2. GlusterFS 备份与恢复
2.1 挂载 GlusterFS 客户端
1
2
3
4
5
| # Ubuntu
sudo apt install -y glusterfs-client
sudo mkdir -p /mnt/glusterfs_old
sudo mount -t glusterfs <gluster-node-ip>:<volume-name> /mnt/glusterfs_old
|
2.2 备份数据
1
2
3
4
5
6
7
8
| mkdir -p /backup/zendao_pvc
# 找到 PVC 对应目录
cd /mnt/glusterfs_dv
cd "$(find . -type d -name 'pvc-<uuid>' -print -quit)"
# 同步
rsync -av /mnt/glusterfs_dv/subvol/<path>/pvc-<uuid>/ /backup/zendao_pvc/
|
2.3 还原
1
2
3
| # 创建新 PVC(kadalu / nfs-subdir),拿到新 PVC 路径
# 把数据写回
rsync -av /backup/zendao_pvc/ /mnt/glusterfs_dv/subvol/<新路径>/pvc-<新uuid>/
|
3. 远程 rsync 备份
把数据同步到远程备份机(避免单点):
1
2
3
4
5
6
7
8
9
| # 远程机器先建目录
mkdir -p /home/bak/dv/zendao_pvc
# 同步
rsync \
-avz \
-e ssh \
/backup/zendao_pvc/ \
root@<worker4-ip>:/home/bak/dv/zendao_pvc/
|
增量同步(带删除):
1
2
3
4
5
6
7
| rsync \
-avz \
--delete \
--dry-run \
-e ssh \
/mnt/glusterfs_dv/subvol/<path>/pvc-<uuid>/ \
root@<worker4-ip>:/home/bak/dv/zendao_pvc/
|
--dry-run 看到底要同步什么;去掉它就是真的同步。
4. 借助"中转 Pod"做 PVC 备份
K8s 上直接访问 PVC 路径比较麻烦(要 exec 进 pod,或者用 driver 内置工具)。一个"中转 pod"模式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # pvc-tester.yaml
apiVersion: v1
kind: Pod
metadata:
name: pvc-tester
namespace: kube-ops
spec:
containers:
- name: tester
image: alpine
command: ["sleep", "3600"]
volumeMounts:
- name: data
mountPath: /mnt/pvc
volumes:
- name: data
persistentVolumeClaim:
claimName: jenkins-agent-pvc
|
1
| kubectl apply -f pvc-tester.yaml
|
4.1 远程到 pod(用 rsync over SSH)
Pod 内装 openssh-server,挂载 pod 上 .ssh 目录:
1
2
3
4
5
6
7
8
9
10
| # 主机上生成 SSH key
ssh-keygen
# 公钥写到 pod 内
kubectl -n kube-ops exec -it pvc-tester -- sh -c "mkdir -p /root/.ssh && chmod 700 /root/.ssh"
cat ~/.ssh/id_rsa.pub | kubectl -n kube-ops exec -i pvc-tester -- sh -c "cat >> /root/.ssh/authorized_keys && chmod 600 /root/.ssh/authorized_keys"
# 远程 rsync 到 pod
POD_IP=$(kubectl -n kube-ops get pod pvc-tester -o jsonpath='{.status.podIP}')
rsync -avz --progress -e ssh /backup/jenkins_pvc/ root@$POD_IP:/mnt/pvc/
|
注意:Pod 内需要有 sshd(基础 alpine 镜像没有);可以换 openssh-server 镜像,或用 kubectl cp 简化。
4.2 kubectl cp 简化
1
2
3
4
5
| # 不需要 sshd
kubectl -n kube-ops cp /backup/jenkins_pvc/. pvc-tester:/mnt/jenkins-pvc/
# 反向:把 pod 内数据拷出来
kubectl -n kube-ops cp pvc-tester:/mnt/zentao-pvc/ /backup/zendao_pvc/
|
局限:kubectl cp 走 tar 流,对超大文件慢(GB 级以上)。
5. CSI Snapshot(生产推荐)
CSI 1.0+(K8s 1.17+)提供 VolumeSnapshot / VolumeSnapshotContent / VolumeSnapshotClass 三个 CRD。
5.1 前置
存储驱动必须实现 VolumeSnapshot 接口(Rook-Ceph 1.0+、Portworx、Longhorn 1.2+ 都支持)。
5.2 VolumeSnapshotClass
1
2
3
4
5
6
7
8
9
10
11
12
| apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: csi-rbd-snapclass
provisioner: rook-ceph.rbd.csi.ceph.com
parameters:
clusterID: rook-ceph
csi.storage.k8s.io/snapshotter-secret-name: rook-csi-rbd-provisioner
csi.storage.k8s.io/snapshotter-secret-namespace: rook-ceph
csi.storage.k8s.io/snapshotter-list-secret-name: rook-csi-rbd-provisioner
csi.storage.k8s.io/snapshotter-list-secret-namespace: rook-ceph
deletionPolicy: Retain
|
5.3 创建快照
1
2
3
4
5
6
7
8
9
| apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: mysql-snapshot-2024-12-15
namespace: default
spec:
volumeSnapshotClassName: csi-rbd-snapclass
source:
persistentVolumeClaimName: mysql-data
|
1
2
| kubectl get volumesnapshot
# STATUS 必须是 Ready
|
5.4 从快照恢复
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-restored
namespace: default
spec:
storageClassName: rook-ceph-block
dataSource:
name: mysql-snapshot-2024-12-15
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
|
新 Pod 挂载 mysql-restored PVC 就拿到快照时的数据。
6. Velero(集群级备份)
Velero 备份整个 K8s 资源 + PV 数据到对象存储。
6.1 安装
1
2
3
4
5
| velero install \
--provider aws \
--bucket <bucket-name> \
--secret-file ./credentials-velero \
--use-restic
|
如果用 MinIO 当对象存储:
1
2
3
4
5
6
| velero install \
--provider aws \
--bucket velero-backups \
--secret-file ./credentials-velero \
--backup-location-config region=minio,s3ForcePathStyle="true",s3Url=http://<minio-host>:9000 \
--use-restic
|
6.2 备份
1
2
3
4
5
6
7
8
| # 备份整个集群
velero backup create full-backup-2024-12-15
# 备份指定 namespace
velero backup create mysql-backup --include-namespaces default
# 备份指定资源
velero backup create jenkins-backup --include-resources statefulsets,pvc
|
6.3 恢复
1
| velero restore create --from-backup full-backup-2024-12-15
|
6.4 定时备份
1
| velero schedule create daily-backup --schedule="0 2 * * *"
|
每天凌晨 2 点全量备份。
7. 实战:禅道 + Jenkins PVC 备份
以 kube-ops 命名空间下的禅道和 Jenkins 为例:
1
2
3
4
5
6
7
8
9
10
11
12
| # 1. 准备中转 pod
kubectl apply -f pvc-tester.yaml
# 2. 备份禅道
kubectl -n kube-ops cp pvc-tester:/mnt/zentao-pvc/ /backup/zendao_pvc/
kubectl -n kube-ops cp pvc-tester:/mnt/jenkins-pvc/ /backup/jenkins_pvc/
# 3. 远程同步
rsync -avz -e ssh /backup/zendao_pvc/ root@<remote-backup-host>:/backup/
# 4. 清理
kubectl delete -f pvc-tester.yaml
|
恢复时反向操作:
1
2
3
4
5
6
7
8
9
10
11
| # 1. 启动中转 pod
kubectl apply -f pvc-re.yaml
# 2. 恢复数据
kubectl -n kube-ops cp /backup/jenkins_pvc/. pvc-tester:/mnt/jenkins-pvc/
kubectl -n kube-ops cp /backup/zendao_pvc/. pvc-tester:/mnt/zentao-pvc/
# 3. 恢复原服务
kubectl apply -f xxxx.yaml
kubectl exec -it zentao-5dd779bcb8-ngl5p -n kube-ops -- bash
chmod 777 -R /apps/zentao/tmp/
|
8. 常见问题
8.1 PVC 一直 Pending
1
2
3
| kubectl describe pvc jenkins-agent-pvc -n kube-ops
# 找 "no persistent volumes available for this claim" → storage class 不对
# 找 "FailedBinding" → 节点不满足
|
如果是 SSH 问题(OpenEBS 节点访问),pvc-tester 上检查:
1
2
| ssh root@<gluster-host>
# 确认 ssh key 配好
|
8.2 VolumeSnapshot 失败
1
2
| kubectl describe volumesnapshot mysql-snapshot
# 查 Events:通常是 RBAC 或 Secret 缺
|
8.3 Velero 备份后恢复失败
Velero 默认不备份所有 CRD。恢复前确认:
1
2
| velero backup describe full-backup --details
# 看 Resources 列表
|
8.4 kubectl cp 大文件超时
1
2
| # 加 timeout
kubectl --request-timeout=600s cp ...
|
或换 rsync over SSH(pod 内装 openssh-server)。
9. 小结
PVC 备份的"最佳实践"是组合拳:
- 日常:CSI Snapshot(最快,秒级)
- 跨集群:Velero(标准方案)
- 冷备/迁移:rsync + tar(最灵活)
- 不要忘:应用层备份(mysqldump、etcd snapshot)
下一步:K8s 集群运维命令大全:kubectl 速查 + 节点维护。