Demo 1b: live migration and storage migration (bonus)¶
Version 1.1.3 · draft · 2026-10-17 · class/demos/01b-live-and-storage-migration.md
What you will learn¶
Two moves on a running VM, with no reboot: a live migration moves its memory and CPU to another node, and a storage migration moves one of its disks to another storage class (ODF → NFS) while Windows and PostgreSQL keep running. You write a row before and after each move and read them back.
Before you start: finish Demo 1. Your VM runs on site A, and every command runs
in the site A console's Web Terminal (>_).
⚠ The commands are an example for
student-01-0024-012, VMwin-pg-s01at10.101.23.101. Change them to your class ID, your VM and its address before you run them.
🎬 Demo 1b: live migration and storage migration: a walk-through of the whole demo in the console. Watch it, then do the same with the steps below.
1b.1 Inject, and check the VM can live-migrate¶
oc exec -n student-01-0024-012 deploy/sql-client -- sql -h 10.101.23.101 -c "SELECT * FROM ovedr_mark('student-01-0024-012', 'before-live-migration', 'running on one node');"
oc exec -n student-01-0024-012 deploy/sql-client -- sql -h 10.101.23.101 -c "SELECT id, ts, stage, payload FROM marks ORDER BY id;" # read back: your rows so far, oldest first
oc -n student-01-0024-012 get vmi win-pg-s01 -o jsonpath='{.status.nodeName} LiveMigratable={.status.conditions[?(@.type=="LiveMigratable")].status}{"\n"}'
✅ LiveMigratable=True. ✍️ Note the node it runs on now. A live migration needs the disks reachable from both
nodes at once (ReadWriteMany): ODF's ocs-storagecluster-ceph-rbd-virtualization is. If it says False, the
condition's message names the reason, usually a mounted CD.
1b.2 Live-migrate to another node¶
Console: Virtualization → VirtualMachines → win-pg-s01 → Actions → Migrate → Compute. Or:
virtctl -n student-01-0024-012 migrate win-pg-s01
oc -n student-01-0024-012 get vmim -w # … Succeeded (Ctrl-C)
oc -n student-01-0024-012 get vmi win-pg-s01 -o jsonpath='{.status.nodeName}{"\n"}'
✅ The VM runs on a different node, and it never stopped: the console's VM Overview shows the new node and no restart.
oc exec -n student-01-0024-012 deploy/sql-client -- sql -h 10.101.23.101 -c "SELECT * FROM ovedr_mark('student-01-0024-012', 'after-live-migration', 'running on another node');"
oc exec -n student-01-0024-012 deploy/sql-client -- sql -h 10.101.23.101 -c "SELECT id, ts, stage, payload FROM marks ORDER BY id;" # read back: your rows so far, oldest first
1b.3 Storage migration: move the data disk from ODF to NFS, while it runs¶
Your VM's disks are on ODF (ocs-storagecluster-ceph-rbd-virtualization). Move the 1 GB data disk (D:) to
nfs-csi, with the VM running. OpenShift Virtualization copies the disk in the background, then switches the VM to
the new copy with a live migration.
Proven on this cluster (0024-015, 2026-10-16): a 1 GiB disk moved from ODF (block) to nfs-csi (filesystem) in
46 seconds, the VM never restarted, and its data was intact. [verify: whether the console's Actions → Migrate →
Storage offers the same]
First check the VM allows it:
oc -n student-01-0024-012 get vm win-pg-s01 -o jsonpath='StorageLiveMigratable={.status.conditions[?(@.type=="StorageLiveMigratable")].status}{"\n"}'
✅ StorageLiveMigratable=True.
Find the data disk's volume (the 1 GiB one):
oc -n student-01-0024-012 get vm win-pg-s01 -o json | jq -r '.spec.template.spec.volumes[] | "\(.name) \(.persistentVolumeClaim.claimName // .dataVolume.name)"'
oc -n student-01-0024-012 get pvc -o custom-columns=NAME:.metadata.name,SIZE:.spec.resources.requests.storage,CLASS:.spec.storageClassName
Set the one value this step needs, from the list above:
# the data disk's current volume (the 1 GiB one on ODF): the disk you are moving
OLD='<the 1 GiB pvc from the list above>'
Create an empty 1Gi target volume on nfs-csi, the same size as the disk you move:
oc apply -f - <<EOF
apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata: { name: win-pg-s01-data-nfs, namespace: student-01-0024-012 }
spec:
source: { blank: {} }
storage:
storageClassName: nfs-csi
accessModes: [ ReadWriteMany ]
resources: { requests: { storage: 1Gi } }
EOF
oc -n student-01-0024-012 get dv win-pg-s01-data-nfs -w # Succeeded (Ctrl-C)
Point the VM's data disk at the new volume, and tell it to migrate the data (not just swap the disk):
oc -n student-01-0024-012 get vm win-pg-s01 -o json | jq --arg old "$OLD" '
.spec.updateVolumesStrategy = "Migration"
| .spec.template.spec.volumes |= map(
if ((.persistentVolumeClaim.claimName // .dataVolume.name) == $old)
then {name: .name, dataVolume: {name: "win-pg-s01-data-nfs"}} else . end)
| if .spec.dataVolumeTemplates then .spec.dataVolumeTemplates |= map(select(.metadata.name != $old)) else . end' \
| oc replace -f -
oc -n student-01-0024-012 get vmim -w # a migration runs and Succeeds (Ctrl-C)
oc -n student-01-0024-012 get vmi win-pg-s01 -o jsonpath='{range .status.volumeStatus[*]}{.name} {.persistentVolumeClaimInfo.claimName}{"\n"}{end}'
✅ The data disk now reads win-pg-s01-data-nfs (nfs-csi), and the VM never restarted. In the console: VM →
Configuration → Storage shows the data disk on nfs-csi.
1b.4 Inject, and read back¶
oc exec -n student-01-0024-012 deploy/sql-client -- sql -h 10.101.23.101 -c "SELECT * FROM ovedr_mark('student-01-0024-012', 'after-storage-migration', 'data disk on NFS');"
oc exec -n student-01-0024-012 deploy/sql-client -- sql -h 10.101.23.101 -c "SELECT id, ts, stage, payload FROM marks ORDER BY id;"
oc exec -n student-01-0024-012 deploy/sql-client -- sql -h 10.101.23.101 -c "SELECT * FROM ovedr_verify('student-01-0024-012');"
✅ After Demo 1's rows: before-live-migration, after-live-migration and after-storage-migration, in order, with
chain_ok = t. PostgreSQL kept running through both moves, so nothing between them is missing.
Back to: Demo 1 · Next: Demo 2: warm migration with storage copy offload (🚧 coming soon: read only).