3.13. 备份与恢复:Velero
etcd 快照能救回一个集群,却救不回被误删的命名空间和写坏的 PVC。Velero 把「Kubernetes 对象」和「卷里的数据」拆成两条独立路径备份,再组合成一次可回放的恢复。本章从零装一套 Velero + MinIO,走完备份、删除、恢复、定时、快照五步。
- 层级:
L3 · 平台
- 难度:
进阶
- 预计:
60 分钟
- 前置:
存储与持久化、CSI 快照
3.13.1. 你将学到
区分「备份对象」与「备份数据」,知道什么时候该用 etcd 快照、什么时候该用 Velero
控制面与 node-agent 的分工,BackupStorageLocation 与 VolumeSnapshotLocation 各自存什么
用 MinIO 作为对象存储后端安装 Velero,完成
backup/restore/schedule日常操作资源过滤、标签选择、CSI 快照、Kopia 文件级备份与 pre/post hooks 的取舍
3.13.2. 为什么需要它
Kubernetes 的「备份」经常被简化成「定时 etcd 快照」。但 etcd 快照保护的是 API 对象的整体状态:它能救回整个集群,却无法只恢复一个命名空间,不含 PV 里的数据,回滚还会连带丢掉快照之后的所有变更。真实故障更多是局部且具体的——误删命名空间、升级把 ConfigMap 改坏、PVC 被应用写脏、迁移到另一个集群。
Velero 的定位就是这些局部场景:用 CRD 描述一次备份(Backup)或恢复(Restore),把对象序列化后存进对象存储,把卷数据交给快照或文件级上传器。它不替代 etcd 快照,而是补上「按命名空间、按标签、按资源类型」的恢复粒度。
手段 |
保护范围 |
恢复粒度 |
主要局限 |
|---|---|---|---|
etcd 快照 |
全集群 API 对象 |
整个集群 |
不含 PV 数据;回滚丢弃快照后的所有变更 |
Velero 对象备份 |
选定的资源与命名空间 |
命名空间 / 标签 / 资源类型 |
本身不含卷数据,需配合快照或 FSB |
CSI 卷快照 |
单个 PV 的数据 |
单个 PVC |
依赖存储驱动;快照通常不能跨集群、跨云 |
应用级导出( |
逻辑数据 |
库 / 表 / 行 |
每个应用都要单独写脚本与调度 |
3.13.3. 核心概念
3.13.3.1. 对象与数据是两条路径
一次 Velero 备份产出两部分:对象清单(API 对象序列化后打包上传到对象存储)和卷数据。后者有三条路径,选错就会出现「恢复成功但数据是空的」:
CSI 快照:调用
VolumeSnapshotClass让存储侧打时间点快照,一致性最好,但快照留在原存储上。快照 + 数据移动(
--snapshot-move-data):先打快照,再由 node-agent 把内容读出来上传到对象存储,兼顾一致性与可迁移性。文件级备份(FSB,Pod Volume Backup):node-agent 直接读节点上的卷目录打包上传,不需要存储支持快照,但写入频繁时有数据不一致风险;也正因如此,仓库
Velero/tests.md把它定位为「卷不支持快照时唯一可用的方法」。
3.13.3.2. 组件与数据流
图:server 只管对象与编排,真正搬数据的是每个节点上的 node-agent。
velero install 会创建整套 CRD(kubectl api-resources --api-group=velero.io 可查):Backup、Restore、Schedule、BackupStorageLocation(bsl)、VolumeSnapshotLocation(vsl)、PodVolumeBackup、DeleteBackupRequest。BSL 决定备份元数据存哪里,VSL 决定快照存在哪里——两者可以是完全不同的存储系统,前者必须是对象存储。安装后自动创建的 default BSL,只有 PHASE=Available 才代表凭据、网络、bucket 三者都对。
3.13.3.3. hooks:让备份发生在「一致的那一刻」
文件级备份是「边写边读」,数据库类应用需要先落盘。Velero 用 Pod 注解在备份前后注入命令,hook 失败会让备份变成 PartiallyFailed 而不是静默成功:
常用的是 pre.hook.backup.velero.io/command(备份前执行,如 redis-cli BGSAVE)、post.hook.backup.velero.io/command(备份后清理或解冻)、pre.hook.backup.velero.io/container 与 /timeout(默认第一个容器、30s),恢复侧对应 post.hook.restore.velero.io/command。
3.13.4. 动手实验
实验:备份一个命名空间,删掉它,再恢复回来
目标:用 MinIO 做 BSL 安装 Velero,完成对象级备份/恢复、定时备份,以及 NFS PVC 的 CSI 快照与恢复。
前置:集群已有 nfs-csi StorageClass;已安装 velero CLI(仓库使用 v1.13.0);一台能跑容器的机器放 MinIO。
3.13.4.1. 步骤 1 · 准备 MinIO 并安装 Velero
仓库 Velero/minio/docker-compose.yml 是可直接用的 MinIO(9000/9001,minioadmin / magedu.com)。起服务、建 bucket、写与 Velero/credentials-velero 一致的凭据文件,再安装;s3Url 要填 Velero 能访问到的 MinIO 地址(它在集群内运行,所以写宿主机 IP 或集群内 Service DNS):
cd source-repo/Velero/minio && docker compose up -d
docker run --rm --network host minio/mc \
sh -c 'mc alias set local http://127.0.0.1:9000 minioadmin magedu.com && mc mb local/velero'
cat > credentials-velero <<'EOF'
[default]
aws_access_key_id = minioadmin
aws_secret_access_key = magedu.com
region = minio
EOF
velero install --provider=aws --bucket=velero --use-node-agent --uploader-type=kopia \
--plugins=velero/velero-plugin-for-aws:v1.9.0 \
--backup-location-config region=minio,s3ForcePathStyle=true,s3Url=http://172.29.7.52:9000 \
--snapshot-location-config region=minio --secret-file=./credentials-velero
kubectl -n velero get pods,deploy
velero backup-location get
pod/node-agent-6r5wb 1/1 Running 0 6m
NAME PROVIDER BUCKET/PREFIX PHASE ACCESS MODE DEFAULT
default aws velero Available ReadWrite true
--use-node-agent 创建负责搬数据的 DaemonSet(v1.13 起取代 --use-restic);--uploader-type=kopia 是新部署的默认上传器,restic 已在 v1.14 弃用、v1.15 起移除。
3.13.4.2. 步骤 2 · 备份一个命名空间
先造数据:一个带 NFS PVC 的 Redis,并用 pre hook 保证备份前落盘。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
namespace: demo
spec:
storageClassName: nfs-csi
accessModes: ["ReadWriteMany"]
resources: {requests: {storage: 5Gi}}
---
apiVersion: v1
kind: Pod
metadata:
name: redis-with-nfs-pvc
namespace: demo
annotations:
pre.hook.backup.velero.io/command: '["/bin/sh", "-c", "redis-cli BGSAVE"]'
pre.hook.backup.velero.io/timeout: "60s"
spec:
containers:
- name: redis
image: redis:7-alpine
volumeMounts: [{name: data, mountPath: /data}]
volumes:
- name: data
persistentVolumeClaim: {claimName: nfs-pvc}
kubectl create namespace demo
kubectl apply -f demo-redis.yaml
kubectl -n demo exec redis-with-nfs-pvc -- redis-cli set course k8s
velero backup create demo-fsb --include-namespaces demo --default-volumes-to-fs-backup --wait
velero backup describe demo-fsb --details
Backup Volumes:
Pod Volume Backups:
demo/redis-with-nfs-pvc:
Uploader Type: kopia / Volume: data
3.13.4.3. 步骤 3 · 删掉再恢复
kubectl delete namespace demo && velero restore create --from-backup demo-fsb --wait
kubectl -n demo exec redis-with-nfs-pvc -- redis-cli get course
NAME BACKUP STATUS ERRORS
demo-fsb demo-fsb Completed 0
"k8s"
--namespace-mappings demo:demo-restored 可恢复到另一个命名空间;--include-resources、-l/--selector 只恢复备份集的一部分;目标对象已存在时用 --existing-resource-policy=update 覆盖。
3.13.4.4. 步骤 4 · 定时备份
velero schedule create demo-nightly --schedule="0 2 * * *" --include-namespaces demo \
--default-volumes-to-fs-backup --ttl 168h
velero schedule get
velero backup create --from-schedule demo-nightly --wait # 不等 cron,立即验证
NAME STATUS SCHEDULE BACKUP TTL LAST BACKUP
demo-nightly Enabled 0 2 * * * 168h0m0s 12s ago
cron 按 UTC 解释,0 2 * * * 在东八区是上午 10 点;--ttl 控制保留期(默认 720h)。velero backup delete demo-fsb --confirm 删单个,--all --confirm 清空。
3.13.4.5. 步骤 5 · NFS PVC 的 CSI 快照与恢复
CSI 快照需要三件套:snapshot.storage.k8s.io/v1 的 CRD、snapshot-controller、存储驱动的快照能力(csi-driver-nfs v4.6.0 的部署清单已含前两者)。
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: nfs-csi
labels: {velero.io/csi-volumesnapshot-class: "true"} # 全集群只能标一个
driver: nfs.csi.k8s.io
parameters:
server: 172.29.7.2
share: /data/nfs
deletionPolicy: Delete
仓库同名文件把 parameters: 写了两遍(第一处是空值),kubectl apply 会报 duplicate field;不同 YAML 解析器对重复键的行为并不一致,直接用上面这版。
kubectl apply -f nfs-csi-volumesnapshotclass.yaml
velero backup create demo-csi --include-namespaces demo --snapshot-volumes --wait
velero backup describe demo-csi --details
Backup Volumes:
CSI Snapshots:
demo/nfs-pvc:
Snapshot: included
恢复时 Velero 用快照内容重建 PVC,机理与手工写 dataSource: {name: nfs-pvc-snap, kind: VolumeSnapshot} 的 PVC 相同;执行 velero restore create demo-csi-restore --from-backup demo-csi --namespace-mappings demo:demo-restored --wait 后用 kubectl -n demo-restored get pvc,pod 验证。
若目标是另一个集群,快照内容并不跟着走,此时加 --snapshot-move-data,由 Kopia 把快照数据上传到对象存储,describe --details 会出现 Data Movement: Data Mover: velero。另一种指定快照类的方式是在 Backup 上加注解 velero.io/csi-volumesnapshot-class_nfs.csi.k8s.io: "nfs-csi",适合一个集群有多个 CSI 驱动的场景。
3.13.4.6. 验证清单
velero backup-location get显示Available,velero version能连上 serverkubectl -n velero get ds node-agent的DESIRED等于工作节点数备份
Phase=Completed、ERRORS 0,且--details中卷段非空快照恢复出的 PVC
Bound,kubectl get vs中READYTOUSE=true
3.13.5. 常见问题与排错
现象 |
可能原因 |
处理方式 |
|---|---|---|
BSL 一直 |
|
|
|
未挂载 |
检查 DaemonSet 的 hostPath 与 |
备份 |
PVC 后端不支持快照且没开 FSB |
加 |
恢复后 PVC |
目标集群缺 VolumeSnapshotClass 或快照内容已删 |
恢复前重建快照类; |
恢复成功但 Pod 里没数据 |
备份时只备了对象,未备卷数据 |
用 |
|
删除是异步的 |
|
3.13.6. 进阶挑战
用
--selector app=demoapp做一次「按标签」备份,恢复到新命名空间,验证只带走带标签的对象。配置两个 BSL(MinIO 两个 bucket),用
--storage-location做「日常 / 归档」分离,再用velero backup-location set切换默认位置。把
demo-nightly的 TTL 改成24h,连续触发两次,观察旧备份是否按 TTL 过期。
3.13.7. 小结
Velero 备份的是「对象 + 卷数据」,两条路径要分别确认,否则会得到一次「成功但空」的恢复。
server 负责编排与对象打包,node-agent 负责搬数据;BSL 存元数据,VSL /
VolumeSnapshotClass管快照。卷数据有快照、快照+数据移动、文件级备份三种方式,一致性依次降低、可迁移性依次升高。