hyper2kvm

Hyper2KVM + KubeVirt Integration

Version: v2.1.0 Date: 2026-01-31 Status: Production Ready


Overview

This integration enables seamless migration of VMware VMs to KubeVirt-managed virtual machines running on Kubernetes.

Flow:

VMware VMDK → Hyper2KVM Conversion → KubeVirt VM → Running on K8s

Benefits:


Architecture

Component Stack

┌─────────────────────────────────────────────────────────┐
│ KubeVirt VirtualMachine (VM running in pod)             │
├─────────────────────────────────────────────────────────┤
│ DataVolume (QCOW2 image in PVC)                         │
├─────────────────────────────────────────────────────────┤
│ Hyper2KVM Conversion (VMDK → QCOW2 + offline fixes)     │
├─────────────────────────────────────────────────────────┤
│ Source VMDK (VMware export)                             │
└─────────────────────────────────────────────────────────┘

Integration Points

  1. Hyper2KVM MigrationJob → Converts VMDK to KVM-ready QCOW2
  2. Kubernetes PVC → Stores converted image
  3. KubeVirt DataVolume → Imports QCOW2 into VM disk
  4. KubeVirt VirtualMachine → Runs the converted VM

Prerequisites

Cluster Requirements

Hardware:

Software:

Install KubeVirt

# Install KubeVirt operator
export VERSION=$(curl -s https://api.github.com/repos/kubevirt/kubevirt/releases/latest | grep tag_name | cut -d '"' -f 4)

kubectl create -f https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/kubevirt-operator.yaml
kubectl create -f https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/kubevirt-cr.yaml

# Wait for deployment
kubectl wait --for=condition=Available kubevirt kubevirt -n kubevirt --timeout=5m

# Install virtctl CLI
curl -L -o virtctl https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/virtctl-${VERSION}-linux-amd64
chmod +x virtctl
sudo mv virtctl /usr/local/bin/

Install CDI (for DataVolumes)

export CDI_VERSION=$(curl -s https://github.com/kubevirt/containerized-data-importer/releases/latest | grep -o "v[0-9]\+\.[0-9]\+\.[0-9]\+")

kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$CDI_VERSION/cdi-operator.yaml
kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$CDI_VERSION/cdi-cr.yaml

Install Hyper2KVM

# Install CRDs
kubectl apply -f https://raw.githubusercontent.com/ssahani/hyper2kvm/main/k8s/operator/crds/

# Deploy operator (see DEPLOYMENT.md)
kubectl apply -f k8s/operator/manifests/

Integration Workflow

Step 1: Convert VMDK with Hyper2KVM

Create a MigrationJob to convert the VMDK:

apiVersion: hyper2kvm.io/v1alpha1
kind: MigrationJob
metadata:
  name: centos9-conversion
  namespace: vms
spec:
  operation: offline_fix  # Full conversion with KVM preparation

  image:
    path: /data/input/centos9.vmdk
    format: vmdk

  artifacts:
    output_path: /data/output
    output_format: qcow2
    compress: true

  priority: 80
  timeout: 90m

  # Offline fix parameters
  parameters:
    fstab_mode: stabilize-all
    regen_initramfs: true
    regenerate_grub: true
    network_fixes: moderate
    disable_vmware_services: true

Apply:

kubectl apply -f centos9-migration.yaml

# Watch progress
kubectl get migrationjob centos9-conversion -n vms -w

# Check output
kubectl exec <worker-pod> -n vms -- ls -lh /data/output/

Step 2: Create DataVolume from Converted Image

Import the QCOW2 into a DataVolume:

apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
  name: centos9-disk
  namespace: vms
spec:
  source:
    pvc:
      name: centos9-output-pvc  # PVC from MigrationJob
      namespace: vms
  storage:
    accessModes:
      - ReadWriteOnce
    resources:
      requests:
        storage: 20Gi  # Adjust based on VM size
    storageClassName: standard

Alternative: HTTP source (if QCOW2 is hosted):

spec:
  source:
    http:
      url: "https://storage.example.com/centos9-fixed.qcow2"
  storage:
    # ... same as above

Step 3: Create VirtualMachine

Define the VM using the converted disk:

apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
  name: centos9-vm
  namespace: vms
  labels:
    app: centos9
    migrated-by: hyper2kvm
spec:
  running: false  # Set to true to auto-start
  template:
    metadata:
      labels:
        kubevirt.io/vm: centos9-vm
    spec:
      domain:
        devices:
          disks:
            - name: rootdisk
              disk:
                bus: virtio  # Hyper2KVM injected virtio drivers
          interfaces:
            - name: default
              masquerade: {}
              model: virtio
        resources:
          requests:
            memory: 2Gi
            cpu: 2
      networks:
        - name: default
          pod: {}
      volumes:
        - name: rootdisk
          dataVolume:
            name: centos9-disk

Apply:

kubectl apply -f centos9-vm.yaml

# Start the VM
virtctl start centos9-vm -n vms

# Check status
kubectl get vmi -n vms
virtctl console centos9-vm -n vms

Complete End-to-End Example

Automated Pipeline

---
# Step 1: Namespace
apiVersion: v1
kind: Namespace
metadata:
  name: vm-migrations

---
# Step 2: PVC for VMDK input
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: centos9-vmdk-input
  namespace: vm-migrations
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi

---
# Step 3: PVC for QCOW2 output
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: centos9-qcow2-output
  namespace: vm-migrations
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi

---
# Step 4: MigrationJob (converts VMDK → QCOW2)
apiVersion: hyper2kvm.io/v1alpha1
kind: MigrationJob
metadata:
  name: centos9-migration
  namespace: vm-migrations
spec:
  operation: offline_fix

  image:
    path: /data/input/centos9.vmdk
    format: vmdk

  artifacts:
    output_path: /data/output
    output_format: qcow2
    compress: true

  priority: 90
  timeout: 120m

  parameters:
    fstab_mode: stabilize-all
    regen_initramfs: true
    regenerate_grub: true
    network_fixes: moderate

---
# Step 5: DataVolume (imports QCOW2 for KubeVirt)
apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
  name: centos9-rootdisk
  namespace: vm-migrations
spec:
  source:
    pvc:
      name: centos9-qcow2-output
      namespace: vm-migrations
  storage:
    accessModes:
      - ReadWriteOnce
    resources:
      requests:
        storage: 20Gi
    storageClassName: standard

---
# Step 6: VirtualMachine (runs converted image)
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
  name: centos9
  namespace: vm-migrations
  labels:
    app: centos9
    os: centos-stream-9
    migrated-from: vmware
    migrated-by: hyper2kvm
    migration-date: "2026-01-31"
  annotations:
    description: "CentOS Stream 9 migrated from VMware"
    hyper2kvm/source-vmdk: "centos9.vmdk"
    hyper2kvm/conversion-job: "centos9-migration"
spec:
  running: true
  template:
    metadata:
      labels:
        kubevirt.io/vm: centos9
    spec:
      domain:
        cpu:
          cores: 2
        devices:
          disks:
            - name: rootdisk
              disk:
                bus: virtio
            - name: cloudinit
              disk:
                bus: virtio
          interfaces:
            - name: default
              masquerade: {}
              model: virtio
        machine:
          type: q35
        resources:
          requests:
            memory: 2Gi
      networks:
        - name: default
          pod: {}
      volumes:
        - name: rootdisk
          dataVolume:
            name: centos9-rootdisk
        - name: cloudinit
          cloudInitNoCloud:
            userData: |
              #cloud-config
              hostname: centos9
              users:
                - name: centos
                  sudo: ALL=(ALL) NOPASSWD:ALL
                  shell: /bin/bash
              ssh_authorized_keys:
                - ssh-rsa AAAA... # Add your SSH key

Deployment Instructions

1. Upload VMDK to Kubernetes

# Create namespace
kubectl create namespace vm-migrations

# Upload VMDK to PVC
kubectl run vmdk-uploader -n vm-migrations \
  --image=busybox --restart=Never \
  --overrides='{"spec":{"containers":[{"name":"uploader","image":"busybox","command":["sleep","3600"],"volumeMounts":[{"name":"data","mountPath":"/data"}]}],"volumes":[{"name":"data","persistentVolumeClaim":{"claimName":"centos9-vmdk-input"}}]}}'

kubectl cp /path/to/centos9.vmdk vm-migrations/vmdk-uploader:/data/centos9.vmdk

kubectl delete pod vmdk-uploader -n vm-migrations

2. Apply Complete Pipeline

kubectl apply -f complete-vm-migration.yaml

# Monitor conversion
kubectl get migrationjob -n vm-migrations -w

# Monitor DataVolume import
kubectl get datavolume -n vm-migrations -w

# Check VM status
kubectl get vm -n vm-migrations
kubectl get vmi -n vm-migrations

3. Access the VM

# Start VM (if not auto-started)
virtctl start centos9 -n vm-migrations

# Access console
virtctl console centos9 -n vm-migrations

# SSH access (if cloud-init configured)
virtctl ssh centos@centos9 -n vm-migrations

# VNC access
virtctl vnc centos9 -n vm-migrations

Advanced Features

Live Migration

Enable live migration between nodes:

spec:
  template:
    spec:
      evictionStrategy: LiveMigrate

Trigger migration:

virtctl migrate centos9 -n vm-migrations

VM Snapshots

apiVersion: snapshot.kubevirt.io/v1alpha1
kind: VirtualMachineSnapshot
metadata:
  name: centos9-snapshot-1
  namespace: vm-migrations
spec:
  source:
    apiGroup: kubevirt.io
    kind: VirtualMachine
    name: centos9

Multi-Disk VMs

spec:
  template:
    spec:
      domain:
        devices:
          disks:
            - name: rootdisk
              disk:
                bus: virtio
            - name: datadisk
              disk:
                bus: virtio
      volumes:
        - name: rootdisk
          dataVolume:
            name: centos9-root
        - name: datadisk
          dataVolume:
            name: centos9-data

Integration with Hyper2KVM Operator

Enhanced MigrationJob with KubeVirt Output

apiVersion: hyper2kvm.io/v1alpha1
kind: MigrationJob
metadata:
  name: vm-migration-with-kubevirt
  namespace: vm-migrations
spec:
  operation: offline_fix

  image:
    path: /data/input/windows10.vmdk
    format: vmdk

  artifacts:
    output_path: /data/output
    output_format: qcow2

  # KubeVirt integration (custom extension)
  kubevirt:
    enabled: true
    vmName: windows10
    dataVolumeName: windows10-disk
    autoStart: false
    vmSpec:
      resources:
        requests:
          memory: 4Gi
          cpu: 4
      cloudInit:
        enabled: false  # Windows doesn't support cloud-init

Monitoring & Observability

Check Migration Status

# Hyper2KVM conversion
kubectl get migrationjob -n vm-migrations
kubectl describe migrationjob centos9-migration -n vm-migrations

# DataVolume import
kubectl get dv -n vm-migrations
kubectl describe dv centos9-rootdisk -n vm-migrations

# VM status
kubectl get vm,vmi -n vm-migrations
virtctl info centos9 -n vm-migrations

Logs

# Hyper2KVM worker logs
kubectl logs -n vm-migrations <migration-worker-pod>

# KubeVirt virt-launcher logs
kubectl logs -n vm-migrations <virt-launcher-pod>

# CDI importer logs
kubectl logs -n vm-migrations <importer-pod>

Troubleshooting

Issue 1: VM Won’t Boot

Symptoms:

Fix: Ensure offline fixes were applied during conversion:

spec:
  parameters:
    regen_initramfs: true      # Essential for virtio drivers
    regenerate_grub: true      # Essential for boot
    fstab_mode: stabilize-all  # Essential for filesystem mounts

Issue 2: Network Not Working

Check:

virtctl console centos9 -n vm-migrations
# Inside VM
ip a

Fix: Ensure network was fixed during conversion:

spec:
  parameters:
    network_fixes: moderate  # Updates NetworkManager configs

Issue 3: DataVolume Import Fails

Check:

kubectl get events -n vm-migrations --sort-by='.lastTimestamp'
kubectl describe dv centos9-rootdisk -n vm-migrations

Common causes:


Performance Optimization

Storage Class Selection

For best VM performance:

spec:
  storage:
    storageClassName: fast-ssd  # High-performance SSD

Resource Tuning

spec:
  template:
    spec:
      domain:
        cpu:
          cores: 4
          dedicatedCpuPlacement: true  # CPU pinning
        memory:
          hugepages:
            pageSize: 2Mi  # Use hugepages
        resources:
          requests:
            memory: 8Gi
          limits:
            memory: 8Gi

Security Considerations

Image Validation

Always validate converted images:

# Check QCOW2 integrity
qemu-img check centos9-fixed.qcow2

# Scan for malware (if applicable)
clamscan centos9-fixed.qcow2

Network Policies

Restrict VM network access:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: vm-network-policy
  namespace: vm-migrations
spec:
  podSelector:
    matchLabels:
      kubevirt.io/vm: centos9
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              role: admin
  egress:
    - to:
        - podSelector:
            matchLabels:
              role: database

Comparison: Traditional vs Hyper2KVM + KubeVirt

Aspect Traditional Migration Hyper2KVM + KubeVirt
Conversion Manual qemu-img Automated with offline fixes
Driver injection Manual Automatic (virtio drivers)
Boot config Manual GRUB/fstab edits Automatic
Deployment VM install, manual config Declarative YAML
Scaling Manual, per-VM Kubernetes-native
Storage Separate management Integrated PVCs
Networking Separate SDN Kubernetes CNI
Monitoring Separate tools Kubernetes-native
HA Complex setup Built-in (live migration)

Production Checklist

Before production deployment:


Tested Configurations

Successful migrations:

Guest OS Support:

Cluster Platforms:


References


Last Updated: 2026-01-31 Status: Production Ready Integration Tested: ✅ CentOS 9 local conversion validated KubeVirt Compatibility: v1.0+