
Backing up stateless microservices in Kubernetes is relatively straightforward: export your GitOps manifests, and redeploy.
Virtual Machines, however, are massive stateful engines.
If you take a naive snapshot of a running virtual machine while an active database is processing write transactions in RAM, your restored VM will likely boot into a corrupted filesystem, broken journal logs, or lost transactions (the dreaded "Crash-Consistent" state).
To achieve true enterprise-grade Application Consistency, we must coordinate three distinct layers:
- The guest operating system's internal filesystem memory buffers.
- The hypervisor virtualization engine (KubeVirt).
- The underlying storage array CSI snapshot controller.
In this fifth part of our series, we construct an end-to-end Enterprise Disaster Recovery strategy using OADP (OpenShift API for Data Protection / Velero) and local NooBaa (S3 Object Storage).
🏗️ Architecture: The Application-Consistent Backup Workflow
The coordinated backup flow operates in five discrete phases:
-
Pre-Backup Hook: OADP communicates through KubeVirt to the
qemu-guest-agentinside the VM, executingfsfreezeto flush all cached data from RAM to disk and freeze write operations. - CSI Volume Snapshots: TopoLVM or ODF instantly captures point-in-time storage snapshots of all associated disks (PVCs).
-
Post-Backup Hook: OADP signals KubeVirt to issue
fsthaw, immediately unfreezing guest OS writes with zero perceptible application disruption. - Metadata & Archive Streaming: VM definitions, cloud-init configurations, secrets, and metadata are bundled and encrypted into an S3-compatible object bucket.
- Cross-Namespace Disaster Recovery: The VM can be restored seamlessly to another namespace or an entirely separate recovery cluster.
🛠️ Step 1: Provision Local S3 Storage via NooBaa
Instead of routing gigabytes of backup data across public cloud networks, we can use NooBaa (Multicloud Object Gateway) to create a high-speed, local S3 bucket directly inside the cluster:
cat << 'EOF' | oc apply -f -
apiVersion: objectbucket.io/v1alpha1
kind: ObjectBucketClaim
metadata:
name: oadp-backup-bucket
namespace: openshift-adp
spec:
generateBucketName: oadp-backup
storageClassName: openshift-storage.noobaa.io
EOF
Extract Bucket Credentials
BUCKET_NAME=$(oc get obc oadp-backup-bucket -n openshift-adp -o jsonpath='{.spec.bucketName}')
ACCESS_KEY=$(oc get secret oadp-backup-bucket -n openshift-adp -o jsonpath='{.data.AWS_ACCESS_KEY_ID}' | base64 -d)
SECRET_KEY=$(oc get secret oadp-backup-bucket -n openshift-adp -o jsonpath='{.data.AWS_SECRET_ACCESS_KEY}' | base64 -d)
cat << EOF > /tmp/credentials-velero
[default]
aws_access_key_id = ${ACCESS_KEY}
aws_secret_access_key = ${SECRET_KEY}
EOF
oc create secret generic cloud-credentials -n openshift-adp \
--from-file=cloud=/tmp/credentials-velero \
--dry-run=client -o yaml | oc apply -f -
rm -f /tmp/credentials-velero
⚙️ Step 2: Configure the DataProtectionApplication (DPA)
To support OpenShift Virtualization, your OADP deployment must include 4 mandatory plugins:
-
openshift: Handles OpenShift-specific resources (Routes, ImageStreams). -
aws: Interfaces with NooBaa's S3-compatible API. -
kubevirt: Orchestrates VM states and QEMU guest agent hooks. -
csi: Interacts with storage drivers for instantaneous volume snapshots.
cat << EOF | oc apply -f -
apiVersion: oadp.openshift.io/v1alpha1
kind: DataProtectionApplication
metadata:
name: oadp-dpa
namespace: openshift-adp
spec:
configuration:
velero:
defaultPlugins:
- openshift
- aws
- kubevirt
- csi
nodeAgent:
enable: false
uploaderType: kopia
backupLocations:
- velero:
provider: aws
default: true
objectStorage:
bucket: ${BUCKET_NAME}
prefix: velero
config:
profile: default
region: noobaa
s3Url: https://s3.openshift-storage.svc:443
s3ForcePathStyle: "true"
insecureSkipTLSVerify: "true"
credential:
key: cloud
name: cloud-credentials
EOF
Wait until the backup location is ready:
oc get backupstoragelocation -n openshift-adp
# Output must show: PHASE: Available
🧊 Step 3: Enable Application-Consistent Hooks
Inside your virtual machines (RHEL, CentOS, Ubuntu, Windows), ensure the QEMU Guest Agent is installed and running:
# On RHEL/CentOS guest:
sudo dnf install -y qemu-guest-agent
sudo systemctl enable --now qemu-guest-agent
With the kubevirt plugin installed in your DPA, KubeVirt automatically communicates with the QEMU Guest Agent over an internal virtio-serial socket to trigger fsfreeze before snapshots and fsthaw immediately after!
📦 Step 4: Execute the VM Namespace Backup
The most reliable strategy is backing up the entire namespace hosting your VM workloads. This guarantees all accompanying secrets, configmaps, data volumes, and network definitions are preserved:
cat << 'EOF' | oc apply -f -
apiVersion: velero.io/v1
kind: Backup
metadata:
name: prod-vm-backup
namespace: openshift-adp
spec:
includedNamespaces:
- vm-workloads
snapshotVolumes: true
storageLocation: oadp-dpa-1
EOF
Monitor progress until completion:
oc get backup prod-vm-backup -n openshift-adp -w
# Wait for Phase: Completed
🔄 Step 5: Disaster Recovery & The KubeMacPool Bypass
When restoring a VM into a new namespace (e.g., during disaster recovery drills or staging clone tests), KubeMacPool will reject the restore because the original VM's MAC address already exists in the cluster!
1. Create the Recovery Namespace and Bypass KubeMacPool
oc new-project vm-workloads-restored --skip-config-write
# Instruct KubeMacPool to ignore this namespace during restoration:
oc label namespace vm-workloads-restored \
mutatevirtualmachines.kubemacpool.io=ignore --overwrite
2. Execute the Restore CR
cat << 'EOF' | oc apply -f -
apiVersion: velero.io/v1
kind: Restore
metadata:
name: prod-vm-restore
namespace: openshift-adp
spec:
backupName: prod-vm-backup
restorePVs: true
namespaceMapping:
vm-workloads: vm-workloads-restored
EOF
3. Power On the Restored VM
Once the restore reaches Completed, boot up the recovered machine:
virtctl start production-db-vm -n vm-workloads-restored
As soon as the VM boots, any pending PVCs will bind immediately, bringing your application back online with zero data corruption!
🎯 Key Takeaways
-
Never rely on crash-consistent backups for databases or production VMs. Always run the
qemu-guest-agent. - Ensure your DPA includes the
kubevirtandcsiplugins to automate pre/post snapshot hooks. - Use the
mutatevirtualmachines.kubemacpool.io=ignorenamespace label to prevent webhook rejections during cross-namespace DR restores.
⏭️ Next Up: Day-2 Operations, Live Migration & Troubleshooting
Now that we have covered architecture, storage, golden images, networking, and enterprise backups, it's time to master day-to-day cluster operations!
In Part 6 (The Grand Finale), we explore:
- Performing zero-downtime Live Migrations during node kernel patching.
- Draining worker nodes (
oc adm drain) without dropping VM user sessions. - The top production debugging commands every OpenShift Virtualization administrator must know.
💬 What is your current RPO and RTO target for virtual machines? Let us know in the comments below!
Top comments (0)