KubeVirt VM Live Migration with Experimental RWX Block Volumes
Overview
Replicated PV Mayastor provides experimental support for native ReadWriteMany (RWX) block volumes to enable KubeVirt Virtual Machine (VM) live migration without requiring an intermediary NFS layer.
This capability is intended exclusively for KubeVirt live migration workloads, where KubeVirt performs the required migration orchestration.
During a migration operation, both the source and destination nodes may maintain connectivity to the volume. However, only one VM instance accesses the block volume at a given time.
This document helps you test the workflow and provide feedback to the OpenEBS team.
Refer OEP: RWX Block Volume Support in Replicated PV Mayastor for KubeVirt for design/implementation details.
This feature is experimental and should be evaluated in non-production environments only. Feedback is actively requested so the team can harden the implementation before it becomes generally available.
Scope and Limitations
This feature is currently intended for evaluating KubeVirt VM live migration using native RWX block volumes.
The following limitations apply:
- Intended for KubeVirt VM live migration use cases only.
- Only block mode volumes are supported (
volumeMode: Block). ReadWriteManyfor filesystem mode volumes is not supported by this mechanism.- Complex network-fault scenarios during migration have known gaps (see Known limitation).
Validated Environment
The procedures in this document were validated using the following software versions:
| Component | Version |
|---|---|
| KubeVirt | v1.8.3 |
| Kubernetes | v1.33.12 |
| OpenEBS | v4.5.0 |
| CDI | v1.65.0 |
Prerequisites
Before proceeding, ensure that:
- OpenEBS Replicated PV Mayastor is installed and healthy.
- At least three worker nodes are available for migration testing.
- A healthy Replicated PV Mayastor storage cluster is configured.
Refer to the OpenEBS Installation Documentation for installation instructions.
Install KubeVirt
- Install the KubeVirt Operator.
Command
export VERSION=$(curl -s https://storage.googleapis.com/kubevirt-prow/release/kubevirt/kubevirt/stable.txt)
echo $VERSION
kubectl create -f "https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/kubevirt-operator.yaml"
Sample Output
namespace/kubevirt created
customresourcedefinition.apiextensions.k8s.io/kubevirts.kubevirt.io created
priorityclass.scheduling.k8s.io/kubevirt-cluster-critical created
clusterrole.rbac.authorization.k8s.io/kubevirt.io:operator created
serviceaccount/kubevirt-operator created
role.rbac.authorization.k8s.io/kubevirt-operator created
rolebinding.rbac.authorization.k8s.io/kubevirt-operator-rolebinding created
clusterrole.rbac.authorization.k8s.io/kubevirt-operator created
clusterrolebinding.rbac.authorization.k8s.io/kubevirt-operator created
deployment.apps/virt-operator created
-
Create the KubeVirt Custom Resource (CR).
Command
kubectl create -f "https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/kubevirt-cr.yaml"Sample Output
kubevirt.kubevirt.io/kubevirt created -
(Optional) Enable emulation if hardware virtualisation is unavailable on the nodes.
Command
kubectl -n kubevirt patch kubevirt kubevirt --type=merge \--patch '{"spec":{"configuration":{"developerConfiguration":{"useEmulation":true}}}}' -
Wait for KubeVirt to become available.
Command
kubectl -n kubevirt wait kv kubevirt --for condition=Available --timeout=5m -
Verify the installation.
Command
kubectl get all -n kubevirtSample Output
NAME READY STATUS RESTARTS AGEpod/virt-api-595d49d6fd-474xq 1/1 Running 1 (25h ago) 26hpod/virt-api-595d49d6fd-xg6w4 1/1 Running 1 (25h ago) 26hpod/virt-controller-6bbb8667fd-587nh 1/1 Running 1 (25h ago) 26hpod/virt-controller-6bbb8667fd-kzp2h 1/1 Running 1 (25h ago) 26hpod/virt-handler-6g4tr 1/1 Running 0 26hpod/virt-handler-982bg 1/1 Running 0 26hpod/virt-handler-dpwcf 1/1 Running 0 26hpod/virt-operator-64b9cfbdcc-hjg75 1/1 Running 0 26hpod/virt-operator-64b9cfbdcc-st7rj 1/1 Running 0 26hNAME AGE PHASEkubevirt.kubevirt.io/kubevirt 26h Deployed
Install Containerized Data Importer
Containerized Data Importer (CDI) manages the import and cloning of VM disk images into PersistentVolumes.
-
Install the CDI Operator and CR.
Command
export VERSION=$(basename $(curl -s -w %{redirect_url} https://github.com/kubevirt/containerized-data-importer/releases/latest))kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$VERSION/cdi-operator.yamlkubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$VERSION/cdi-cr.yamlSample Output
namespace/cdi createdcustomresourcedefinition.apiextensions.k8s.io/cdis.cdi.kubevirt.io createdclusterrole.rbac.authorization.k8s.io/cdi-operator-cluster createdclusterrolebinding.rbac.authorization.k8s.io/cdi-operator createdserviceaccount/cdi-operator createdrole.rbac.authorization.k8s.io/cdi-operator createdrolebinding.rbac.authorization.k8s.io/cdi-operator createddeployment.apps/cdi-operator created -
Configure containerd for CDI block-device imports.
On some kubeadm/containerd environments, CDI importer pods can fail block-mode DataVolume imports with:
exit status 1, blockdev: cannot open /dev/cdi-block-volume: Permission deniedSet
device_ownership_from_security_context = truein containerd on each Kubernetes node:sudo sed -i 's/device_ownership_from_security_context = false/device_ownership_from_security_context = true/' /etc/containerd/config.tomltipIf the key does not already exist in your
config.toml, open the file manually and add the following section:[plugins."io.containerd.cri.v1.runtime"]device_ownership_from_security_context = trueRestart containerd after the config change:
Command
sudo systemctl restart containerd -
Wait for CDI to become available.
Command
kubectl -n cdi wait cdi cdi --for condition=Available --timeout=5m -
Verify the installation.
Command
kubectl get all -n cdiSample Output
NAME READY STATUS RESTARTS AGEpod/cdi-apiserver-5bbd7b4df5-28gm8 1/1 Running 1 (2m55s ago) 3mpod/cdi-deployment-84d584dbdd-g8mfn 1/1 Running 0 3mpod/cdi-operator-7cfb4db845-fg6vt 1/1 Running 0 3m46spod/cdi-uploadproxy-856554cb9c-m7kll 1/1 Running 1 (2m54s ago) 3m
Install virtctl
virtctl is the CLI companion to kubectl for KubeVirt operations such as accessing the VM console and triggering live migration.
Download the binary that matches your installed KubeVirt version:
Command
VERSION=$(kubectl get kubevirt.kubevirt.io/kubevirt -n kubevirt -o=jsonpath="{.status.observedKubeVirtVersion}")
ARCH=$(uname -s | tr A-Z a-z)-$(uname -m | sed 's/x86_64/amd64/') || windows-amd64.exe
echo ${ARCH}
curl -L -o virtctl https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/virtctl-${VERSION}-${ARCH}
sudo install -m 0755 virtctl /usr/local/bin
Verify:
virtctl version
Alternative - Install virtctl via krew
If you already use kubectl plugins through krew, you can install the virt plugin:
Command
kubectl krew install virt
kubectl virt version
Refer to the Official virtctl Documentation for installation on other operating systems.
Create a StorageClass
-
Create a StorageClass backed by Replicated PV Mayastor using the NVMe-oF protocol. Two replicas are sufficient for migration testing.
storageclass-rwx-block.yaml
apiVersion: storage.k8s.io/v1kind: StorageClassmetadata:name: mayastor-rwx-blockparameters:protocol: nvmfrepl: "2"thin: "true"rwxBlock: "true"provisioner: io.openebs.csi-mayastorreclaimPolicy: DeletevolumeBindingMode: ImmediateallowVolumeExpansion: true -
Apply it:
Command
kubectl apply -f storageclass-rwx-block.yaml
Create a block-mode DataVolume
-
Create a DataVolume using
accessModes: ReadWriteManyandvolumeMode: Block. This provisions a native RWX block PVC directly via Mayastor - no NFS pod is required.dv-rwx-block.yaml
apiVersion: cdi.kubevirt.io/v1beta1kind: DataVolumemetadata:name: ubuntu-rwx-blockspec:storage:accessModes:- ReadWriteManyresources:requests:storage: 8GistorageClassName: mayastor-rwx-blockvolumeMode: Blocksource:http:url: "https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img" -
Apply it:
Command
kubectl apply -f dv-rwx-block.yaml -
Wait until the DataVolume reports
Succeeded:Command
kubectl get datavolume ubuntu-rwx-block -w
Create a VM
-
Create a VM manifest that:
- References the
ubuntu-rwx-blockPVC as a block disk - Sets
evictionStrategy: LiveMigrate
vm-rwx-block.yaml
apiVersion: kubevirt.io/v1kind: VirtualMachinemetadata:name: vm-rwxspec:runStrategy: Alwaystemplate:metadata:labels:kubevirt.io/domain: vm-rwxspec:evictionStrategy: LiveMigrateterminationGracePeriodSeconds: 30networks:- name: defaultpod: {}domain:cpu:cores: 1devices:interfaces:- name: defaultmasquerade: {}disks:- disk:bus: virtioname: disk0machine:type: q35resources:requests:memory: 1024Mvolumes:- name: disk0persistentVolumeClaim:claimName: ubuntu-rwx-block - References the
-
Apply it:
Command
kubectl apply -f vm-rwx-block.yaml -
Verify the VM reaches
Runningstate:Command
kubectl get vm vm-rwxkubectl get vmi vm-rwx -o wide
Trigger and Validate Live Migration
Trigger migration:
Command
virtctl migrate vm-rwx
Monitor migration progress:
Command
kubectl get vmi vm-rwx -o yaml | grep -A4 migrationState
After migration completes, verify:
- VM is running on a different node
- Data is intact inside the VM
Known Limitations
During migration, if a network partition causes this asymmetry:
- Source node can still reach the NVMe-oF target, but
- Target node cannot reach the NVMe-oF target,
then the current high-availability behavior may repeatedly trigger failover/republish attempts (connection "ping-pong") until migration recovery fails.
This is a known gap in the current HA logic, which currently detects path failures on a per node-path basis independently. A more robust fix would require the Mayastor HA node agent to become volume-aware for multi-attach migration scenarios, so that a republish result for one path is shared with all other connected nodes rather than triggering a chain of independent failover requests.
Provide Feedback
As this feature is experimental, OpenEBS welcomes feedback from users evaluating its functionality and behavior in test environments.
When sharing feedback, please focus on the following areas:
- Migration stability under normal conditions
- Failover and reconnection behavior
- Behavior under network faults
Submit your feedback through one of the following channels:
To assist with troubleshooting and analysis, include the following information where applicable:
- OpenEBS, Kubernetes, and KubeVirt versions
- StorageClass and VM specs (sanitized)
- Migration timeline and relevant events or logs