antony@notes:~/rancher$ cat "Harvester-使用-Velero-外部-CSI-儲存備份-如何確保-VM-資料一致性.md"
Harvester 使用 Velero 外部 CSI 儲存備份:如何確保 VM 資料一致性。
Harvester 使用 Velero 外部 CSI 儲存備份,並確保 VM 資料一致性
1. Preface
Harvester 1.5 導入了對使用外部容器儲存介面(CSI)驅動程式來配置虛擬機器根磁碟區(root volumes)與資料磁碟區(data volumes)的支援。
本文將示範如何使用 Velero 1.17.0 在 Harvester 中執行虛擬機器的備份與還原。文章將透過具體的指令與資源清單(manifests)來完成以下操作:
- 備份特 namespace 內的虛擬機器、其 NFS CSI 磁碟區,以及相關的命名空間層級設定檔
- 將備份產物匯出至 AWS S3 儲存體(bucket)
- 還原至同一個叢集(cluster)上的不同 namespace
- 還原至不同的叢集
Velero 是一款 Kubernetes 原生的備份與還原工具,讓使用者能夠對虛擬機器執行排程與隨選(on-demand)備份,並將資料儲存至外部的物件儲存供應商(例如 S3、Azure Blob 或 GCS),完美契合企業級的備份與災難復原實務標準。
2. 安裝與部署 Garage(輕量級 S3 相容的物件儲存系統)
建立永存目錄
sudo mkdir -p /garage/{data,meta,config}撰寫
garage.toml設定檔[s3_api]和[s3_web]區塊底下的root_domain的值需要根據環境調整cat <<EOF | sudo tee /garage/config/garage.toml > /dev/null metadata_dir = "/var/lib/garage/meta" data_dir = "/var/lib/garage/data" db_engine = "sqlite" replication_factor = 1 rpc_bind_addr = "[::]:3901" rpc_public_addr = "127.0.0.1:3901" rpc_secret = "ba7861f27fccfe06a99245b49e33113ca846cecdc9956169a47d7eecd74aa7d8" [s3_api] s3_region = "garage" api_bind_addr = "[::]:3900" root_domain = "api.garage.kubeantony.com" [s3_web] bind_addr = "[::]:3902" root_domain = "web.garage.kubeantony.com" index = "index.html" [k2v_api] api_bind_addr = "[::]:3904" [admin] api_bind_addr = "[::]:3903" admin_token = "KV0EuVoDxze0Ut7tQdkvNiU8TKcD8LejZQglH/JGK00=" metrics_token = "RQxx1DjKcnhpEVnPmjxWn+CzLY5wNBNT60GMvHQ+LXo=" EOF
透過 Podman 部署 Garage
sudo podman run \ -d \ --name garaged \ --network host \ -v /garage/config/garage.toml:/etc/garage.toml \ -v /garage/meta:/var/lib/garage/meta \ -v /garage/data:/var/lib/garage/data \ dxflrs/garage:v2.2.0確認 Garage Container 運作狀態
sudo podman ps -a --filter name=garage執行結果:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 499fff9ed2db docker.io/dxflrs/garage:v2.2.0 /garage server 31 minutes ago Up 31 minutes garaged查詢 Garage 節點狀態
sudo podman exec -it garaged /garage status執行結果:
2026-02-26T06:41:45.072105Z INFO garage_net::netapp: Connected to 127.0.0.1:3901, negotiating handshake... 2026-02-26T06:41:45.113726Z INFO garage_net::netapp: Connection established to 368849c86cba2382 ==== HEALTHY NODES ==== ID Hostname Address Tags Zone Capacity DataAvail Version 368849c86cba2382 garage.kubeantony.com 127.0.0.1:3901 NO ROLE ASSIGNED v2.2.0將指定的 Garage 節點設定至
dc1可用區(Zone)和50GB的儲存容量。sudo podman exec -it garaged /garage layout assign -z dc1 -c 50G 368849c86cba2382執行結果:
2026-02-26T06:42:30.841741Z INFO garage_net::netapp: Connected to 127.0.0.1:3901, negotiating handshake... 2026-02-26T06:42:30.882712Z INFO garage_net::netapp: Connection established to 368849c86cba2382 Role changes are staged but not yet committed. Use `garage layout show` to view staged role changes, and `garage layout apply` to enact staged changes.執行結果顯示此變更目前僅處於「暫存
staged」狀態正式套用先前暫存的叢集配置(版本 1),使節點的角色與容量設定正式生效。
sudo podman exec garaged /garage layout apply --version 1執行結果:
2026-02-26T06:42:50.085913Z INFO garage_net::netapp: Connected to 127.0.0.1:3901, negotiating handshake... 2026-02-26T06:42:50.126651Z INFO garage_net::netapp: Connection established to 368849c86cba2382 ==== COMPUTATION OF A NEW PARTITION ASSIGNATION ==== Partitions are replicated 1 times on at least 1 distinct zones. Optimal partition size: 195.3 MB Usable capacity / total cluster capacity: 50.0 GB / 50.0 GB (100.0 %) Effective capacity (replication factor 1): 50.0 GB dc1 Tags Partitions Capacity Usable capacity 368849c86cba2382 [] 256 (256 new) 50.0 GB 50.0 GB (100.0%) TOTAL 256 (256 unique) 50.0 GB 50.0 GB (100.0%) New cluster layout with updated role assignment has been applied in cluster. Data will now be moved around between nodes accordingly.再次確認 Garage 節點狀態
sudo podman exec garaged /garage status執行結果:
2026-02-26T07:49:07.462651Z INFO garage_net::netapp: Connected to 127.0.0.1:3901, negotiating handshake... 2026-02-26T07:49:07.503706Z INFO garage_net::netapp: Connection established to 368849c86cba2382 ==== HEALTHY NODES ==== ID Hostname Address Tags Zone Capacity DataAvail Version 368849c86cba2382 garage.kubeantony.com 127.0.0.1:3901 [] dc1 50.0 GB 58.5 GB (82.0%) v2.2.0建立 Bucket
sudo podman exec garaged /garage bucket create vm-backup執行結果:
2026-02-26T07:54:42.568429Z INFO garage_net::netapp: Connected to 127.0.0.1:3901, negotiating handshake... 2026-02-26T07:54:42.609720Z INFO garage_net::netapp: Connection established to 368849c86cba2382 ==== BUCKET INFORMATION ==== Bucket: be30df62bea032acdc9ba3b992b32a43794b9f950599870e21ee706340c39c96 Created: 2026-02-26 07:54:42.610 +00:00 Size: 0 B (0 B) Objects: 0 Website access: false Global alias: vm-backup ==== KEYS FOR THIS BUCKET ==== Permissions Access key Local aliases建立 API Key
sudo podman exec garaged /garage key create vm-backup-access-key執行結果:
2026-02-26T07:56:11.307951Z INFO garage_net::netapp: Connected to 127.0.0.1:3901, negotiating handshake... 2026-02-26T07:56:11.348732Z INFO garage_net::netapp: Connection established to 368849c86cba2382 ==== ACCESS KEY INFORMATION ==== Key ID: GK6e4243deca7bc09fdffcb405 Key name: vm-backup-access-key Secret key: 9280dbc88cb656a8589783f0762652fdb93362b2df0c7a47cfa7f2ae9b15899f Created: 2026-02-26 07:56:11.348 +00:00 Validity: valid Expiration: never Can create buckets: false ==== BUCKETS FOR THIS KEY ==== Permissions ID Global aliases Local aliases賦予權限
sudo podman exec garaged /garage bucket allow --read --write --owner vm-backup --key vm-backup-access-key執行結果:
2026-02-26T07:57:44.077279Z INFO garage_net::netapp: Connected to 127.0.0.1:3901, negotiating handshake... 2026-02-26T07:57:44.118727Z INFO garage_net::netapp: Connection established to 368849c86cba2382 ==== BUCKET INFORMATION ==== Bucket: be30df62bea032acdc9ba3b992b32a43794b9f950599870e21ee706340c39c96 Created: 2026-02-26 07:54:42.610 +00:00 Size: 0 B (0 B) Objects: 0 Website access: false Global alias: vm-backup ==== KEYS FOR THIS BUCKET ==== Permissions Access key Local aliases RWO GK6e4243deca7bc09fdffcb405 vm-backup-access-key測試掛載,安裝 s3fs
sudo apt-get install s3fs -y設定 s3fs
echo "GK6e4243deca7bc09fdffcb405:9280dbc88cb656a8589783f0762652fdb93362b2df0c7a47cfa7f2ae9b15899f" > ${HOME}/.passwd-s3fs chmod 600 ${HOME}/.passwd-s3fs sudo sed -i 's/#user_allow_other/user_allow_other/' /etc/fuse.conf建立掛載點
sudo mkdir -p /mnt/garage-s3 sudo chown $USER:$USER /mnt/garage-s3將自建 Garage S3 上的
vm-backupbucket 儲存體掛載至本地/mnt/garage-s3s3fs vm-backup /mnt/garage-s3 \ -o passwd_file=${HOME}/.passwd-s3fs \ -o url=http://garage.kubeantony.com:3900 \ -o use_path_request_style \ -o allow_other \ -o umask=000 \ -o endpoint=garage檢視
/mnt/garage-s3目錄已掛載到 s3findmnt -t fuse.s3fs執行結果:
TARGET SOURCE FSTYPE OPTIONS /mnt/garage-s3 s3fs fuse.s3fs rw,nosuid,nodev,relatime,user_id=1000,group_id=1000,allow_other寫入資料
echo "test connection" > /mnt/garage-s3/test.txt查詢
vm-backupBucket 詳細狀態sudo podman exec garaged /garage bucket info vm-backup執行結果:
2026-02-26T08:17:07.919852Z INFO garage_net::netapp: Connected to 127.0.0.1:3901, negotiating handshake... 2026-02-26T08:17:07.960680Z INFO garage_net::netapp: Connection established to 368849c86cba2382 ==== BUCKET INFORMATION ==== Bucket: be30df62bea032acdc9ba3b992b32a43794b9f950599870e21ee706340c39c96 Created: 2026-02-26 07:54:42.610 +00:00 Size: 16 B (16 B) Objects: 1 Website access: false Global alias: vm-backup ==== KEYS FOR THIS BUCKET ==== Permissions Access key Local aliases RWO GK6e4243deca7bc09fdffcb405 vm-backup-access-key清除測試資料與取消掛載
rm /mnt/garage-s3/test.txt umount /mnt/garage-s3
3. Install and Configure Velero
ssh 連線到 Harvester Control plane 節點執行以下操作
安裝 Velero CLI
# 設定版本 VERSION=v1.17.0 # 使用 curl 下載並重新命名檔案 curl -L https://github.com/vmware-tanzu/velero/releases/download/$VERSION/velero-$VERSION-linux-amd64.tar.gz -o velero.tar.gz # 解壓縮 tar -xvf velero.tar.gz # 移動到系統路徑 mv velero-$VERSION-linux-amd64/velero /root/bin/ # 測試 velero version --client-only執行結果:
Client: Version: v1.17.0 Git commit: 3172d9f99c3d501aad9ddfac8176d783f7692dce設置以下 shell 變數
BUCKET_NAME=vm-backup BUCKET_REGION=garage echo "[default] aws_access_key_id=GK6e4243deca7bc09fdffcb405 aws_secret_access_key=9280dbc88cb656a8589783f0762652fdb93362b2df0c7a47cfa7f2ae9b15899f" > ${HOME}/.passwd AWS_CREDENTIALS_FILE=${HOME}/.passwd在 Harvester 集群上安裝 Velero
velero install \ --provider aws \ --features=EnableCSI \ --plugins "velero/velero-plugin-for-aws:v1.12.0,quay.io/kubevirt/kubevirt-velero-plugin:v0.7.1" \ --bucket "${BUCKET_NAME}" \ --secret-file "${AWS_CREDENTIALS_FILE}" \ --backup-location-config region="${BUCKET_REGION}" \ --snapshot-location-config region="${BUCKET_REGION}" \ --use-node-agent在這個設定中,Velero 被設定為:
- 跑在
veleronamespace - 啟用 CSI 儲存卷快照 (volume snapshot) API
- 啟用內建的節點代理程式 (node agent) 資料移動控制器與 Pod
- 使用
velero-plugin-for-aws外掛程式來管理與 S3 物件儲存庫的互動 - 使用
kubevirt-velero-plugin外掛程式來備份與還原 KubeVirt 資源
- 跑在
確認 Velero 已安裝並正在運行
kubectl -n velero get pods執行結果:
NAME READY STATUS RESTARTS AGE node-agent-49f56 1/1 Running 0 4m30s node-agent-dhtmj 1/1 Running 0 4m30s node-agent-n4wh9 1/1 Running 0 4m30s velero-6c57f94f49-bxcjx 1/1 Running 0 4m30s設定 velero CLI 以輸出 CSI 物件的備份和還原狀態
velero client config set features=EnableCSIpatch backupstoragelocation
kubectl patch backupstoragelocation default -n velero --type merge --patch '{"spec":{"config":{"s3Url":"http://garage.kubeantony.com:3900","s3ForcePathStyle":"true"}}}'Check Backup-location status
velero backup-location get執行結果:
NAME PROVIDER BUCKET/PREFIX PHASE LAST VALIDATED ACCESS MODE DEFAULT default aws vm-backup Available 2026-02-28 11:56:20 +0000 UTC ReadWrite true
4. Deploy the NFS CSI and Example Server
在 Kubernetes 叢集上設置 NFS 伺服器
curl -s -O https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/deploy/example/nfs-provisioner/nfs-server.yaml vim nfs-server.yaml檔案內容:
volumes: - name: nfs-vol hostPath: path: /root/nfs-data # modify this to specify another path to store nfs share data type: DirectoryOrCreate部署 NFS 伺服器
kubectl apply -f nfs-server.yaml執行結果:
service/nfs-server created deployment.apps/nfs-server created檢查 NFS 伺服器是否正常運作
kubectl get pods執行結果:
NAME READY STATUS RESTARTS AGE nfs-server-69ff4786bd-4pf6v 1/1 Running 0 3m22s部署 NFS CSI 驅動程式
curl -skSL https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/v4.13.1/deploy/install-driver.sh | bash -s v4.13.1 --執行結果:
Installing NFS CSI driver, version: v4.13.1 ... serviceaccount/csi-nfs-controller-sa created serviceaccount/csi-nfs-node-sa created clusterrole.rbac.authorization.k8s.io/nfs-external-provisioner-role created clusterrolebinding.rbac.authorization.k8s.io/nfs-csi-provisioner-binding created clusterrole.rbac.authorization.k8s.io/nfs-external-resizer-role created clusterrolebinding.rbac.authorization.k8s.io/nfs-csi-resizer-role created csidriver.storage.k8s.io/nfs.csi.k8s.io created deployment.apps/csi-nfs-controller created daemonset.apps/csi-nfs-node created NFS CSI driver installed successfully.確認 pods 狀態
kubectl -n kube-system get pod -o wide -l 'app in (csi-nfs-controller, csi-nfs-node)'執行結果:
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES csi-nfs-controller-68599bd8b6-v68cr 5/5 Running 0 8m36s 192.168.11.183 hvx-03 <none> <none> csi-nfs-node-2s57l 3/3 Running 0 8m35s 192.168.11.181 hvx-01 <none> <none> csi-nfs-node-79zpf 3/3 Running 0 8m35s 192.168.11.182 hvx-02 <none> <none> csi-nfs-node-smcq8 3/3 Running 0 8m35s 192.168.11.183 hvx-03 <none> <none>建立 Client pod 掛載 NFS Volume 檢查 NFS 伺服器是否正常運作
kubectl create -f https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/deploy/example/nfs-provisioner/nginx-pod.yaml確認 client pod 運作正常
kubectl get pod nginx-nfs-example執行結果:
NAME READY STATUS RESTARTS AGE nginx-nfs-example 1/1 Running 0 43m從範例 pod 檢查掛載點
kubectl exec nginx-nfs-example -- bash -c "findmnt /var/www -o TARGET,SOURCE,FSTYPE"執行結果:
TARGET SOURCE FSTYPE /var/www nfs-server.default.svc.cluster.local:/ nfs4清除 client pod
kubectl delete -f https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/deploy/example/nfs-provisioner/nginx-pod.yaml撰寫 NFS Storage Class YAML 檔
vi nfs-sc.yaml檔案內容如下:
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs-csi provisioner: nfs.csi.k8s.io parameters: server: nfs-server.default.svc.cluster.local share: / # csi.storage.k8s.io/provisioner-secret is only needed for providing mountOptions in DeleteVolume # csi.storage.k8s.io/provisioner-secret-name: "mount-options" # csi.storage.k8s.io/provisioner-secret-namespace: "default" reclaimPolicy: Delete volumeBindingMode: Immediate allowVolumeExpansion: true mountOptions: - nfsvers=4.1建立 NFS Storage Class
kubectl apply -f nfs-sc.yamlCreate source PVC and an example pod to write data
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/deploy/example/pvc-nfs-csi-dynamic.yaml kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/deploy/example/nginx-pod-nfs.yamlCheck the Source PVC
kubectl exec nginx-nfs -- ls /mnt/nfs執行結果:
outfileCreate a snapshot on source PVC
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/deploy/example/snapshot/snapshotclass-nfs.yaml kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/deploy/example/snapshot/snapshot-nfs-dynamic.yamlCheck snapshot Status
kubectl describe volumesnapshot test-nfs-snapshot執行結果:
Name: test-nfs-snapshot Namespace: default Labels: <none> Annotations: <none> API Version: snapshot.storage.k8s.io/v1 Kind: VolumeSnapshot Metadata: Creation Timestamp: 2026-02-28T09:37:06Z Finalizers: snapshot.storage.kubernetes.io/volumesnapshot-as-source-protection snapshot.storage.kubernetes.io/volumesnapshot-bound-protection Generation: 1 Resource Version: 15038120 UID: 59a4ddb9-a123-4dea-b220-e0fcac477f59 Spec: Source: Persistent Volume Claim Name: pvc-nfs-dynamic Volume Snapshot Class Name: csi-nfs-snapclass Status: Bound Volume Snapshot Content Name: snapcontent-59a4ddb9-a123-4dea-b220-e0fcac477f59 Creation Time: 2026-02-28T09:37:06Z Ready To Use: true Restore Size: 1026 Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal CreatingSnapshot 46s snapshot-controller Waiting for a snapshot default/test-nfs-snapshot to be created by the CSI driver. Normal SnapshotCreated 46s snapshot-controller Snapshot default/test-nfs-snapshot was successfully created by the CSI driver. Normal SnapshotReady 46s snapshot-controller Snapshot default/test-nfs-snapshot is ready to use.In above example,
snapcontent-59a4ddb9-a123-4dea-b220-e0fcac477f59is the snapshot name.Create a new PVC based on snapshot
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/deploy/example/snapshot/pvc-nfs-snapshot-restored.yaml kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/deploy/example/snapshot/nginx-pod-restored-snapshot.yamlCheck data
kubectl exec nginx-nfs-restored-snapshot -- ls /mnt/nfs執行結果:
outfile清除範例 pod
kubectl delete -f https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/deploy/example/nginx-pod-nfs.yaml kubectl delete -f https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/deploy/example/pvc-nfs-csi-dynamic.yaml kubectl delete -f https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/deploy/example/snapshot/nginx-pod-restored-snapshot.yaml kubectl delete -f https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/deploy/example/snapshot/pvc-nfs-snapshot-restored.yaml
5. Preparing the Virtual Machine and Image
Create a custom namespace named
demo-srckubectl create ns demo-src上傳 ubuntu images
https://cloud-images.ubuntu.com/minimal/releases/noble/release-20260218/ubuntu-24.04-minimal-cloudimg-amd64.img點選左側選單 images -> 在 URL 欄位輸入以上網址

Stoage 選擇
nfs-csi-> Create
確認虛擬機映像檔已成功上傳至 Harvester:

建立 VM
輸入 VM 名稱:
test
點選 Volumes -> 選擇剛剛上傳的 Image
demo-src/ubuntu-24.04-minimal-cloudimg-amd64.img (nfs-csi / 3.5 Gi)
點選 Add Volume -> StorageClass
nfs-csi-> Volume Mode:Filesystem
Cloud Config user-data
ssh_pwauth: true users: - default - name: rancher gecos: rancher groups: users lock_passwd: false sudo: ALL=(ALL) NOPASSWD:ALL plain_text_passwd: 'rancher' shell: /bin/bash點選 Console -> Open in Serial Console

add some files to both the root and data volumes.
sudo touch /test sudo mkdir /data sudo mkfs.ext4 /dev/vdb echo '/dev/vdb /data ext4 defaults 0 2' | sudo tee -a /etc/fstab sudo mount -a echo "haha" | sudo tee /data/test
6. 驗證檔案系統凍結相容性
Access the virtual machine’s virt-launcher
computecontainerPOD=$(kubectl get pods -n demo-src \ -l vm.kubevirt.io/name=test \ -o jsonpath='{.items[0].metadata.name}') kubectl exec -it $POD -n demo-src -c compute -- bashTest filesystem freeze using the virt-freezer application available in the compute container:
virt-freezer --freeze --namespace demo-src --name test執行結果:
{"component":"freezer","level":"info","msg":"Guest agent version is 8.2.2","pos":"virt-freezer.go:114","timestamp":"2026-02-28T14:00:43.873324Z"} {"component":"freezer","level":"info","msg":"Operation completed successfully","pos":"virt-freezer.go:152","timestamp":"2026-02-28T14:00:43.940305Z"}解凍
virt-freezer --unfreeze --namespace demo-src --name test執行結果:
{"component":"freezer","level":"info","msg":"Guest agent version is 8.2.2","pos":"virt-freezer.go:114","timestamp":"2026-02-28T14:02:06.328210Z"} {"component":"freezer","level":"info","msg":"Operation completed successfully","pos":"virt-freezer.go:152","timestamp":"2026-02-28T14:02:06.338162Z"}設定在開始建立備份前凍結 VM 檔案系統,備份完成後解凍虛擬機檔案系統
kubectl patch virtualmachine test -n demo-src --type merge --patch '{ "spec": { "template": { "metadata": { "annotations": { "pre.hook.backup.velero.io/command": "[\"/usr/bin/virt-freezer\", \"--freeze\", \"--namespace\", \"demo-src\", \"--name\", \"ub-test\"]", "pre.hook.backup.velero.io/container": "compute", "pre.hook.backup.velero.io/on-error": "Fail", "pre.hook.backup.velero.io/timeout": "30s", "post.hook.backup.velero.io/command": "[\"/usr/bin/virt-freezer\", \"--thaw\", \"--namespace\", \"demo-src\", \"--name\", \"ub-test\"]", "post.hook.backup.velero.io/container": "compute", "post.hook.backup.velero.io/on-error": "Continue", "post.hook.backup.velero.io/timeout": "30s" } } } } }'到 Harvester UI 將目標 vm 重新開機,套用最新的設定檔
7. Backup the Source Namespace
使用 velero CLI 利用 Velero 內建的資料移動工具,建立 demo-src 命名空間的備份
BACKUP_NAME=backup-demo-src-`date "+%s"` velero backup create "${BACKUP_NAME}" \ --include-namespaces demo-src \ --snapshot-move-data執行結果:
Backup request "backup-demo-src-1772286287" submitted successfully. Run `velero backup describe backup-demo-src-1772286287` or `velero backup logs backup-demo-src-1772286287` for more details.DataUpload的自訂資源提供了備份進度的見解velero backup get "${BACKUP_NAME}"執行結果:
NAME STATUS ERRORS WARNINGS CREATED EXPIRES STORAGE LOCATION SELECTOR backup-demo-src-1772278089 InProgress 0 0 2026-02-28 11:56:33 +0000 UTC 29d default <none>…過幾分鐘後
NAME STATUS ERRORS WARNINGS CREATED EXPIRES STORAGE LOCATION SELECTOR backup-demo-src-1772278089 Completed 0 0 2026-02-28 11:56:33 +0000 UTC 29d default <none>
8. Restore To A Different Namespace
Save the following restore modifier to a local file named
modifier-data-volumes.yamlcat <<EOF > modifier-data-volumes.yaml version: v1 resourceModifierRules: - conditions: groupResource: persistentvolumeclaims matches: - path: /metadata/annotations/harvesterhci.io~1volumeForVirtualMachine value: "\"true\"" patches: - operation: remove path: /metadata/annotations/harvesterhci.io~1volumeForVirtualMachine EOFCreate the restore modifier
kubectl -n velero create cm modifier-data-volumes --from-file=modifier-data-volumes.yamlAssign the backup name to a shell variable
velero restore create \ --from-backup "${BACKUP_NAME}" \ --namespace-mappings "demo-src:demo-dst" \ --exclude-resources "virtualmachineimages.harvesterhci.io" \ --resource-modifier-configmap "modifier-data-volumes" \ --labels "velero.kubevirt.io/clear-mac-address=true,velero.kubevirt.io/generate-new-firmware-uuid=true"執行結果:
Restore request "backup-demo-src-1772286287-20260228141734" submitted successfully. Run `velero restore describe backup-demo-src-1772286287-20260228141734` or `velero restore logs backup-demo-src-1772286287-20260228141734` for more details.修復期間: 虛擬機的 MAC 位址與韌體 UUID 會被重置,以避免與現有虛擬機發生潛在衝突。 虛擬機映像清單被排除,因為 Velero 會從備份中恢復整個虛擬機狀態。 會呼叫 modifier-data-volumes 的還原修飾符來修改虛擬機器的資料卷的元資料,以避免與 CDI 卷匯入填充器發生衝突。
當還原操作仍在進行中時,
DataDownload的自訂資源可用來檢視作業進度:RESTORE_NAME=backup-demo-src-1772286287-20260228141734 kubectl -n velero get datadownload -l velero.io/restore-name="${RESTORE_NAME}"執行結果:
NAME STATUS STARTED BYTES DONE TOTAL BYTES STORAGE LOCATION AGE NODE backup-demo-src-1772286287-20260228135229-9fnqg Completed 59s 3758096384 3758096384 default 75s hvx-02 backup-demo-src-1772286287-20260228135229-fqrhx Completed 72s 192040960 192040960 default 74s hvx-02 backup-demo-src-1772286287-20260228135229-kp2jw Completed 71s 10737418240 10737418240 default 75s hvx-01 backup-demo-src-1772286287-20260228135229-n7bcp Completed 70s 10146021376 10146021376 default 74s hvx-03確認還原成功完成:
velero restore get執行結果:
NAME BACKUP STATUS STARTED COMPLETED ERRORS WARNINGS CREATED SELECTOR backup-demo-src-1772286287-20260228135229 backup-demo-src-1772286287 Completed 2026-02-28 13:52:29 +0000 UTC 2026-02-28 13:54:00 +0000 UTC 0 6 2026-02-28 13:52:29 +0000 UTC <none>確認虛擬機及其設定是否已恢復到新的
demo-dstnamespace: