Featured image of post K8s PVC 备份恢复:CSI Snapshot / Velero / rsync 三方案

K8s PVC 备份恢复:CSI Snapshot / Velero / rsync 三方案

PVC 备份 5 种方式对比、GlusterFS 卷备份与恢复、远程 rsync 同步、sshd 注入 pod

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 备份的"最佳实践"是组合拳:

  1. 日常:CSI Snapshot(最快,秒级)
  2. 跨集群:Velero(标准方案)
  3. 冷备/迁移:rsync + tar(最灵活)
  4. 不要忘:应用层备份(mysqldump、etcd snapshot)

下一步:K8s 集群运维命令大全:kubectl 速查 + 节点维护

使用 Hugo 构建
主题 StackJimmy 设计