<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Muhammad Adel</title>
    <description>The latest articles on DEV Community by Muhammad Adel (@muhammad_adel_f144dae9138).</description>
    <link>https://dev.to/muhammad_adel_f144dae9138</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4234172%2Fdb9e42ce-d1a0-46ec-88a5-e61d9cae676a.png</url>
      <title>DEV Community: Muhammad Adel</title>
      <link>https://dev.to/muhammad_adel_f144dae9138</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/muhammad_adel_f144dae9138"/>
    <language>en</language>
    <item>
      <title>Part 6: Day-2 Operations, Live Migration &amp; Production Troubleshooting</title>
      <dc:creator>Muhammad Adel</dc:creator>
      <pubDate>Sun, 08 Nov 2026 23:09:46 +0000</pubDate>
      <link>https://dev.to/muhammad_adel_f144dae9138/part-6-day-2-operations-live-migration-production-troubleshooting-18og</link>
      <guid>https://dev.to/muhammad_adel_f144dae9138/part-6-day-2-operations-live-migration-production-troubleshooting-18og</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3ufzawpmrap82ilffrk4.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3ufzawpmrap82ilffrk4.jpg" alt=" " width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Recent licensing shifts across enterprise virtualization—most notably VMware by Broadcom—have accelerated a massive industry migration: &lt;strong&gt;moving enterprise virtual machines to Red Hat OpenShift Virtualization.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rebuilding hundreds or thousands of mission-critical VMs from scratch is simply not feasible. Enterprises need an automated, scalable migration pipeline that can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discover existing VMware vSphere clusters, datastores, and port groups.&lt;/li&gt;
&lt;li&gt;Convert proprietary virtual disks (&lt;code&gt;.vmdk&lt;/code&gt;) into cloud-native persistent storage volumes.&lt;/li&gt;
&lt;li&gt;Inject native VirtIO drivers for KVM performance.&lt;/li&gt;
&lt;li&gt;Cut downtime to minutes using continuous background pre-copying.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the exact purpose of &lt;strong&gt;MTV (Migration Toolkit for Virtualization)&lt;/strong&gt;, powered by the open-source &lt;strong&gt;Forklift&lt;/strong&gt; project.&lt;/p&gt;

&lt;p&gt;In this sixth and final installment of our &lt;strong&gt;OpenShift Virtualization (CNV) Zero to Hero&lt;/strong&gt; series, we explore how to perform large-scale VM migrations from VMware vSphere to OpenShift with near-zero downtime.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏗️ Architecture: Migration Toolkit for Virtualization (MTV)
&lt;/h2&gt;

&lt;p&gt;The MTV migration engine orchestrates data movement between legacy hypervisors and OpenShift:&lt;/p&gt;

&lt;p&gt;The MTV architecture centers on five core declarative primitives:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Providers:&lt;/strong&gt; Define source (e.g., VMware vCenter) and target (OpenShift Virtualization) endpoints.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network Map:&lt;/strong&gt; Maps source VMware Port Groups / VLANs to target OpenShift Pod Networks or Multus CNI bridges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage Map:&lt;/strong&gt; Maps source VMware Datastores (VMFS/vSAN) to target OpenShift StorageClasses (ODF Ceph or LVM).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Migration Plan:&lt;/strong&gt; Specifies the inventory of VMs to migrate, associated mappings, and cutover windows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conversion Pod (&lt;code&gt;virt-v2v&lt;/code&gt;):&lt;/strong&gt; A transient pod that mounts the incoming disk, installs VirtIO drivers, and prepares the OS to boot on KVM.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  ⚡ Cold Migration vs. Warm Migration
&lt;/h2&gt;

&lt;p&gt;Choosing the right migration strategy directly impacts application downtime:&lt;/p&gt;

&lt;h3&gt;
  
  
  🔹 Cold Migration (Direct Cutover)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;How it works:&lt;/strong&gt; The source VM on VMware is powered off immediately. MTV copies 100% of the virtual disk across the network to OpenShift, runs &lt;code&gt;virt-v2v&lt;/code&gt; conversion, and boots the new VM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Downtime:&lt;/strong&gt; Proportional to the size of the disk (e.g., a 500GB disk might require 1 to 2 hours of downtime).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best for:&lt;/strong&gt; Non-production environments, batch workloads, or systems that can tolerate scheduled maintenance windows.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔹 Warm Migration (Pre-Copy with CBT) — &lt;em&gt;The Enterprise Gold Standard&lt;/em&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;How it works:&lt;/strong&gt; The source VM &lt;strong&gt;remains online and actively serving users on VMware!&lt;/strong&gt; MTV leverages VMware &lt;strong&gt;CBT (Changed Block Tracking)&lt;/strong&gt; to take periodic snapshots, pre-copying 95%+ of the disk data in the background over days or weeks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Cutover:&lt;/strong&gt; When you are ready for the maintenance window, you trigger the final cutover. The source VM is powered down, only the tiny incremental delta of dirty blocks is transferred (taking just 2 to 5 minutes!), and the VM boots up in OpenShift.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best for:&lt;/strong&gt; Production databases, customer-facing applications, and business-critical systems.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🛠️ Step-by-Step: Migrating from VMware vCenter
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Install the MTV Operator
&lt;/h3&gt;

&lt;p&gt;Install the &lt;strong&gt;Migration Toolkit for Virtualization Operator&lt;/strong&gt; from OperatorHub inside the &lt;code&gt;openshift-mtv&lt;/code&gt; namespace.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Connect the VMware vCenter Provider
&lt;/h3&gt;

&lt;p&gt;Store vCenter credentials in a secret, then declare the vSphere Provider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Secret&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vcenter-credentials&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openshift-mtv&lt;/span&gt;
&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Opaque&lt;/span&gt;
&lt;span class="na"&gt;stringData&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;administrator@vsphere.local"&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YourSuperSecretPassword"&lt;/span&gt;
  &lt;span class="na"&gt;insecureSkipVerify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;

&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;forklift.konveyor.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Provider&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vmware-source&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openshift-mtv&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vsphere&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://vcenter.corp.local/sdk"&lt;/span&gt;
  &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vcenter-credentials&lt;/span&gt;
    &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openshift-mtv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3. Create Network and Storage Mappings
&lt;/h3&gt;

&lt;p&gt;These mappings tell OpenShift how to translate VMware infrastructure into Kubernetes equivalents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 1. Network Mapping: VMware Port Group -&amp;gt; Multus Bridge&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;forklift.konveyor.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NetworkMap&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vcenter-network-map&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openshift-mtv&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;multus&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vlan100-net&lt;/span&gt;
        &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vm-workloads&lt;/span&gt;
      &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;network-12"&lt;/span&gt;  &lt;span class="c1"&gt;# VMware Port Group ID (or name: "VM Network")&lt;/span&gt;
  &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vmware-source&lt;/span&gt;
    &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;host&lt;/span&gt;

&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="c1"&gt;# 2. Storage Mapping: VMware Datastore -&amp;gt; OpenShift StorageClass&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;forklift.konveyor.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;StorageMap&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vcenter-storage-map&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openshift-mtv&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;storageClass&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;odf-ceph-rbd&lt;/span&gt;
        &lt;span class="na"&gt;accessMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ReadWriteMany&lt;/span&gt;
        &lt;span class="na"&gt;volumeMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Block&lt;/span&gt;
      &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;datastore-34"&lt;/span&gt;  &lt;span class="c1"&gt;# VMware Datastore ID (or name: "vsanDatastore")&lt;/span&gt;
  &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vmware-source&lt;/span&gt;
    &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;host&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  4. Declare the Migration Plan (Warm Migration)
&lt;/h3&gt;

&lt;p&gt;Now we group the target VMs into a migration plan and enable &lt;code&gt;warm: true&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;forklift.konveyor.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Plan&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production-db-migration-plan&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openshift-mtv&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;warm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;--- Enables background pre-copy with CBT&lt;/span&gt;
  &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vmware-source&lt;/span&gt;
    &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;host&lt;/span&gt;
  &lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vcenter-network-map&lt;/span&gt;
    &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vcenter-storage-map&lt;/span&gt;
  &lt;span class="na"&gt;targetNamespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vm-workloads&lt;/span&gt;
  &lt;span class="na"&gt;vms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prod-oracle-db-01"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prod-app-server-02"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  5. Execute the Migration &amp;amp; Final Cutover
&lt;/h3&gt;

&lt;p&gt;Initiate the background pre-copy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;forklift.konveyor.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Migration&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;run-db-migration&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openshift-mtv&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;plan&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production-db-migration-plan&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;MTV will begin streaming snapshots in the background while the VMs stay active on VMware.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When your maintenance window arrives, apply the final cutover timestamp:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc patch plan production-db-migration-plan &lt;span class="nt"&gt;-n&lt;/span&gt; openshift-mtv &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'merge'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'{"spec":{"cutover":"2026-11-10T22:00:00Z"}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;MTV powers off the VMware VMs, transfers the final delta, injects VirtIO drivers via &lt;code&gt;virt-v2v&lt;/code&gt;, and boots the virtual machines in OpenShift!&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚠️ Battle-Tested Gotchas &amp;amp; Troubleshooting
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Common Challenge&lt;/th&gt;
&lt;th&gt;Root Cause&lt;/th&gt;
&lt;th&gt;Proven Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Warm migration fails with CBT error&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Changed Block Tracking is disabled on the source VM.&lt;/td&gt;
&lt;td&gt;Enable CBT in VMware (&lt;code&gt;ctkEnabled = TRUE&lt;/code&gt;) or power-cycle the VM once in vSphere.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Windows VM Blue Screens (BSOD) on boot&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Missing KVM VirtIO storage/network drivers in Windows.&lt;/td&gt;
&lt;td&gt;MTV automatically injects the &lt;code&gt;virtio-win&lt;/code&gt; driver ISO. Ensure the &lt;code&gt;virtio-win&lt;/code&gt; container is available on OpenShift.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Slow transfer speeds (throttling)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Data is passing through default ingress or routed networks.&lt;/td&gt;
&lt;td&gt;Designate a dedicated, high-speed migration network in the &lt;code&gt;HyperConverged&lt;/code&gt; CR configuration.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MAC Address changes after import&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;VM network adapter regenerated during import.&lt;/td&gt;
&lt;td&gt;Check "Preserve static MAC addresses" in MTV so KubeMacPool retains the original VMware MACs.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🏁 Series Grand Finale: You Are Now a CNV Hero!
&lt;/h2&gt;

&lt;p&gt;Over the course of this 6-part journey, we built an end-to-end enterprise virtualization practice on Kubernetes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 1:&lt;/strong&gt; Decoded the KubeVirt engine (&lt;code&gt;virt-operator&lt;/code&gt;, &lt;code&gt;virt-api&lt;/code&gt;, and &lt;code&gt;virt-launcher&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 2:&lt;/strong&gt; Mastered Block vs. Filesystem storage and navigated the TopoLVM extent trap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 3:&lt;/strong&gt; Built automated golden OS pipelines with &lt;code&gt;containerDisk&lt;/code&gt; and &lt;code&gt;DataImportCron&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 4:&lt;/strong&gt; Connected VMs to physical enterprise VLANs using Multus and KubeMacPool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 5:&lt;/strong&gt; Built rock-solid DR strategies with OADP, NooBaa, and &lt;code&gt;fsfreeze&lt;/code&gt; hooks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 6:&lt;/strong&gt; Automated large-scale hypervisor migrations from VMware to OpenShift with &lt;strong&gt;MTV&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OpenShift Virtualization proves that containers and virtual machines are no longer rival architectures—they are collaborative primitives running under one unified, cloud-native control plane.&lt;/p&gt;

&lt;p&gt;Thank you for following along with this series! 🚀&lt;/p&gt;

&lt;p&gt;💬 &lt;strong&gt;Are you actively planning or currently executing a VMware-to-OpenShift migration? What are your biggest hurdles? Share your story in the comments below!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>infrastructure</category>
      <category>kubernetes</category>
      <category>sre</category>
    </item>
    <item>
      <title>Part 5: Enterprise Backup &amp; Disaster Recovery with OADP &amp; NooBaa</title>
      <dc:creator>Muhammad Adel</dc:creator>
      <pubDate>Sun, 08 Nov 2026 23:05:39 +0000</pubDate>
      <link>https://dev.to/muhammad_adel_f144dae9138/part-5-enterprise-backup-disaster-recovery-with-oadp-noobaa-535e</link>
      <guid>https://dev.to/muhammad_adel_f144dae9138/part-5-enterprise-backup-disaster-recovery-with-oadp-noobaa-535e</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frie2qozk4erxelpqmcng.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frie2qozk4erxelpqmcng.jpg" alt=" " width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
Backing up stateless microservices in Kubernetes is relatively straightforward: export your GitOps manifests, and redeploy. &lt;/p&gt;

&lt;p&gt;Virtual Machines, however, are massive stateful engines. &lt;/p&gt;

&lt;p&gt;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).&lt;/p&gt;

&lt;p&gt;To achieve true enterprise-grade &lt;strong&gt;Application Consistency&lt;/strong&gt;, we must coordinate three distinct layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The guest operating system's internal filesystem memory buffers.&lt;/li&gt;
&lt;li&gt;The hypervisor virtualization engine (KubeVirt).&lt;/li&gt;
&lt;li&gt;The underlying storage array CSI snapshot controller.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this fifth part of our series, we construct an end-to-end Enterprise Disaster Recovery strategy using &lt;strong&gt;OADP (OpenShift API for Data Protection / Velero)&lt;/strong&gt; and local &lt;strong&gt;NooBaa (S3 Object Storage)&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  🏗️ Architecture: The Application-Consistent Backup Workflow
&lt;/h2&gt;

&lt;p&gt;The coordinated backup flow operates in five discrete phases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pre-Backup Hook:&lt;/strong&gt; OADP communicates through KubeVirt to the &lt;code&gt;qemu-guest-agent&lt;/code&gt; inside the VM, executing &lt;code&gt;fsfreeze&lt;/code&gt; to flush all cached data from RAM to disk and freeze write operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSI Volume Snapshots:&lt;/strong&gt; TopoLVM or ODF instantly captures point-in-time storage snapshots of all associated disks (PVCs).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post-Backup Hook:&lt;/strong&gt; OADP signals KubeVirt to issue &lt;code&gt;fsthaw&lt;/code&gt;, immediately unfreezing guest OS writes with zero perceptible application disruption.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metadata &amp;amp; Archive Streaming:&lt;/strong&gt; VM definitions, cloud-init configurations, secrets, and metadata are bundled and encrypted into an S3-compatible object bucket.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Namespace Disaster Recovery:&lt;/strong&gt; The VM can be restored seamlessly to another namespace or an entirely separate recovery cluster.&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  🛠️ Step 1: Provision Local S3 Storage via NooBaa
&lt;/h2&gt;

&lt;p&gt;Instead of routing gigabytes of backup data across public cloud networks, we can use &lt;strong&gt;NooBaa (Multicloud Object Gateway)&lt;/strong&gt; to create a high-speed, local S3 bucket directly inside the cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;cat &amp;lt;&amp;lt; 'EOF' | oc apply -f -&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;objectbucket.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ObjectBucketClaim&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;oadp-backup-bucket&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openshift-adp&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;generateBucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;oadp-backup&lt;/span&gt;
  &lt;span class="na"&gt;storageClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openshift-storage.noobaa.io&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Extract Bucket Credentials
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;BUCKET_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;oc get obc oadp-backup-bucket &lt;span class="nt"&gt;-n&lt;/span&gt; openshift-adp &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.spec.bucketName}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;oc get secret oadp-backup-bucket &lt;span class="nt"&gt;-n&lt;/span&gt; openshift-adp &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.data.AWS_ACCESS_KEY_ID}'&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;oc get secret oadp-backup-bucket &lt;span class="nt"&gt;-n&lt;/span&gt; openshift-adp &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.data.AWS_SECRET_ACCESS_KEY}'&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; /tmp/credentials-velero
[default]
aws_access_key_id = &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ACCESS_KEY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
aws_secret_access_key = &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;oc create secret generic cloud-credentials &lt;span class="nt"&gt;-n&lt;/span&gt; openshift-adp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;cloud&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp/credentials-velero &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dry-run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;client &lt;span class="nt"&gt;-o&lt;/span&gt; yaml | oc apply &lt;span class="nt"&gt;-f&lt;/span&gt; -
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /tmp/credentials-velero
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ⚙️ Step 2: Configure the DataProtectionApplication (DPA)
&lt;/h2&gt;

&lt;p&gt;To support OpenShift Virtualization, your OADP deployment &lt;strong&gt;must include 4 mandatory plugins&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;openshift&lt;/code&gt;: Handles OpenShift-specific resources (Routes, ImageStreams).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aws&lt;/code&gt;: Interfaces with NooBaa's S3-compatible API.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubevirt&lt;/code&gt;: Orchestrates VM states and QEMU guest agent hooks.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;csi&lt;/code&gt;: Interacts with storage drivers for instantaneous volume snapshots.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;cat &amp;lt;&amp;lt; EOF | oc apply -f -&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;oadp.openshift.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DataProtectionApplication&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;oadp-dpa&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openshift-adp&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;configuration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;velero&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;defaultPlugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;openshift&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;aws&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;kubevirt&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;csi&lt;/span&gt;
    &lt;span class="na"&gt;nodeAgent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;enable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;uploaderType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kopia&lt;/span&gt;
    &lt;span class="na"&gt;backupLocations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;velero&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws&lt;/span&gt;
          &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;objectStorage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${BUCKET_NAME}&lt;/span&gt;
            &lt;span class="na"&gt;prefix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;velero&lt;/span&gt;
          &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;profile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
            &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;noobaa&lt;/span&gt;
            &lt;span class="na"&gt;s3Url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://s3.openshift-storage.svc:443&lt;/span&gt;
            &lt;span class="na"&gt;s3ForcePathStyle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
            &lt;span class="na"&gt;insecureSkipTLSVerify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
          &lt;span class="na"&gt;credential&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cloud&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cloud-credentials&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Wait until the backup location is ready:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc get backupstoragelocation &lt;span class="nt"&gt;-n&lt;/span&gt; openshift-adp
&lt;span class="c"&gt;# Output must show: PHASE: Available&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧊 Step 3: Enable Application-Consistent Hooks
&lt;/h2&gt;

&lt;p&gt;Inside your virtual machines (RHEL, CentOS, Ubuntu, Windows), ensure the &lt;strong&gt;QEMU Guest Agent&lt;/strong&gt; is installed and running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# On RHEL/CentOS guest:&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; qemu-guest-agent
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; qemu-guest-agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the &lt;code&gt;kubevirt&lt;/code&gt; plugin installed in your DPA, KubeVirt automatically communicates with the QEMU Guest Agent over an internal virtio-serial socket to trigger &lt;code&gt;fsfreeze&lt;/code&gt; before snapshots and &lt;code&gt;fsthaw&lt;/code&gt; immediately after!&lt;/p&gt;




&lt;h2&gt;
  
  
  📦 Step 4: Execute the VM Namespace Backup
&lt;/h2&gt;

&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;cat &amp;lt;&amp;lt; 'EOF' | oc apply -f -&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;velero.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Backup&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod-vm-backup&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openshift-adp&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;includedNamespaces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;vm-workloads&lt;/span&gt;
  &lt;span class="na"&gt;snapshotVolumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;storageLocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;oadp-dpa-1&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Monitor progress until completion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc get backup prod-vm-backup &lt;span class="nt"&gt;-n&lt;/span&gt; openshift-adp &lt;span class="nt"&gt;-w&lt;/span&gt;
&lt;span class="c"&gt;# Wait for Phase: Completed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔄 Step 5: Disaster Recovery &amp;amp; The KubeMacPool Bypass
&lt;/h2&gt;

&lt;p&gt;When restoring a VM into a new namespace (e.g., during disaster recovery drills or staging clone tests), &lt;strong&gt;KubeMacPool&lt;/strong&gt; will reject the restore because the original VM's MAC address already exists in the cluster!&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create the Recovery Namespace and Bypass KubeMacPool
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc new-project vm-workloads-restored &lt;span class="nt"&gt;--skip-config-write&lt;/span&gt;

&lt;span class="c"&gt;# Instruct KubeMacPool to ignore this namespace during restoration:&lt;/span&gt;
oc label namespace vm-workloads-restored &lt;span class="se"&gt;\&lt;/span&gt;
  mutatevirtualmachines.kubemacpool.io&lt;span class="o"&gt;=&lt;/span&gt;ignore &lt;span class="nt"&gt;--overwrite&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Execute the Restore CR
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;cat &amp;lt;&amp;lt; 'EOF' | oc apply -f -&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;velero.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod-vm-restore&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openshift-adp&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;backupName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod-vm-backup&lt;/span&gt;
  &lt;span class="na"&gt;restorePVs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;namespaceMapping&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;vm-workloads&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vm-workloads-restored&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Power On the Restored VM
&lt;/h3&gt;

&lt;p&gt;Once the restore reaches &lt;code&gt;Completed&lt;/code&gt;, boot up the recovered machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;virtctl start production-db-vm &lt;span class="nt"&gt;-n&lt;/span&gt; vm-workloads-restored
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;As soon as the VM boots, any pending PVCs will bind immediately, bringing your application back online with zero data corruption!&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Never rely on crash-consistent backups&lt;/strong&gt; for databases or production VMs. Always run the &lt;code&gt;qemu-guest-agent&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Ensure your DPA includes the &lt;strong&gt;&lt;code&gt;kubevirt&lt;/code&gt; and &lt;code&gt;csi&lt;/code&gt; plugins&lt;/strong&gt; to automate pre/post snapshot hooks.&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;mutatevirtualmachines.kubemacpool.io=ignore&lt;/code&gt; namespace label to prevent webhook rejections during cross-namespace DR restores.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  ⏭️ Next Up: Day-2 Operations, Live Migration &amp;amp; Troubleshooting
&lt;/h2&gt;

&lt;p&gt;Now that we have covered architecture, storage, golden images, networking, and enterprise backups, it's time to master day-to-day cluster operations!&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Part 6 (The Grand Finale)&lt;/strong&gt;, we explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performing zero-downtime &lt;strong&gt;Live Migrations&lt;/strong&gt; during node kernel patching.&lt;/li&gt;
&lt;li&gt;Draining worker nodes (&lt;code&gt;oc adm drain&lt;/code&gt;) without dropping VM user sessions.&lt;/li&gt;
&lt;li&gt;The top production debugging commands every OpenShift Virtualization administrator must know.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💬 &lt;strong&gt;What is your current RPO and RTO target for virtual machines? Let us know in the comments below!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>devops</category>
      <category>infrastructure</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Part 4: Advanced VM Networking &amp; MAC Address Governance</title>
      <dc:creator>Muhammad Adel</dc:creator>
      <pubDate>Sun, 08 Nov 2026 23:02:43 +0000</pubDate>
      <link>https://dev.to/muhammad_adel_f144dae9138/part-4-advanced-vm-networking-mac-address-governance-40c7</link>
      <guid>https://dev.to/muhammad_adel_f144dae9138/part-4-advanced-vm-networking-mac-address-governance-40c7</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F54hqlx0tzlygau1v62jf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F54hqlx0tzlygau1v62jf.jpg" alt=" " width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
Containers thrive on ephemeral, dynamic IP addresses. If a container dies, Kubernetes replaces it with a new pod, a new IP, and services automatically route traffic via kube-proxy or OVN-Kubernetes.&lt;/p&gt;

&lt;p&gt;Virtual Machines, however, come from an entirely different enterprise heritage. &lt;/p&gt;

&lt;p&gt;Legacy enterprise workloads running inside VMs often require:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Static, predictable Layer-2 IP addresses.&lt;/li&gt;
&lt;li&gt;Direct connectivity to external corporate VLANs without NAT or Ingress proxies.&lt;/li&gt;
&lt;li&gt;Multiple Network Interface Cards (NICs) separating management, storage, and application traffic.&lt;/li&gt;
&lt;li&gt;Persistent MAC addresses that survive reboots, live migrations, and cluster restarts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this fourth part of our series, we dive deep into &lt;strong&gt;OpenShift Virtualization Networking&lt;/strong&gt;, uncovering how &lt;strong&gt;Multus CNI&lt;/strong&gt; bridges the gap between Kubernetes and physical networks, and how &lt;strong&gt;KubeMacPool&lt;/strong&gt; prevents devastating Layer-2 MAC address collisions.&lt;/p&gt;


&lt;h2&gt;
  
  
  🏗️ Dual-Layer Architecture: Pod Network vs. Secondary Networks
&lt;/h2&gt;

&lt;p&gt;OpenShift Virtualization solves the networking dilemma by offering a flexible, dual-layer networking model:&lt;/p&gt;

&lt;p&gt;The two networking layers operate simultaneously:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Default Pod Network (Primary):&lt;/strong&gt; Connects the VM to the internal cluster SDN (OVN-Kubernetes) using Masquerade (NAT) mode. The VM can reach Kubernetes Services, DNS, and Ingress routers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secondary Physical Networks (Multus CNI):&lt;/strong&gt; Bypasses the cluster SDN entirely, plugging the VM directly into an external VLAN via a Linux Bridge or Open vSwitch (OVS).&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  🔌 Connecting VMs Directly to External VLANs
&lt;/h2&gt;

&lt;p&gt;To attach a virtual machine directly to an existing physical corporate network (e.g., VLAN 100 on subnet 192.168.10.0/24), we follow a three-step configuration:&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Configure the Physical Bridge on Worker Nodes (NMState)
&lt;/h3&gt;

&lt;p&gt;Using the &lt;strong&gt;Kubernetes NMState Operator&lt;/strong&gt;, we declare a &lt;code&gt;NodeNetworkConfigurationPolicy&lt;/code&gt; (NNCP) that creates a Linux Bridge on the physical NIC of worker nodes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nmstate.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NodeNetworkConfigurationPolicy&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;br-vlan100-policy&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;nodeSelector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;node-role.kubernetes.io/worker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
  &lt;span class="na"&gt;desiredState&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;interfaces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;br-vlan100&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;linux-bridge&lt;/span&gt;
        &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;up&lt;/span&gt;
        &lt;span class="na"&gt;bridge&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;stp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
          &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;eth1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  2. Define the &lt;code&gt;NetworkAttachmentDefinition&lt;/code&gt; (NAD)
&lt;/h3&gt;

&lt;p&gt;Multus CNI requires a &lt;code&gt;NetworkAttachmentDefinition&lt;/code&gt; in the workload namespace to make the bridge available:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;k8s.cni.cncf.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NetworkAttachmentDefinition&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vlan100-net&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vm-workloads&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"cniVersion":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"0.3.1",&lt;/span&gt;
    &lt;span class="s"&gt;"name":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"vlan100-net",&lt;/span&gt;
    &lt;span class="s"&gt;"type":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"cnv-bridge",&lt;/span&gt;
    &lt;span class="s"&gt;"bridge":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"br-vlan100",&lt;/span&gt;
    &lt;span class="s"&gt;"macspoofchk":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
  &lt;span class="s"&gt;}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3. Attach Dual NICs to the VirtualMachine
&lt;/h3&gt;

&lt;p&gt;Now, in your &lt;code&gt;VirtualMachine&lt;/code&gt; manifest, declare both interfaces under &lt;code&gt;devices.interfaces&lt;/code&gt; and map them under &lt;code&gt;networks&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kubevirt.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;VirtualMachine&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dual-nic-enterprise-vm&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vm-workloads&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;running&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;devices&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;interfaces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default-pod-net&lt;/span&gt;
              &lt;span class="na"&gt;masquerade&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;        &lt;span class="c1"&gt;# Cluster SDN access&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;physical-vlan&lt;/span&gt;   &lt;span class="c1"&gt;# Direct external VLAN access&lt;/span&gt;
              &lt;span class="na"&gt;bridge&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
        &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;4Gi&lt;/span&gt;
            &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
      &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default-pod-net&lt;/span&gt;
          &lt;span class="na"&gt;pod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;physical-vlan&lt;/span&gt;
          &lt;span class="na"&gt;multus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;networkName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vlan100-net&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Inside the guest OS, you will now see two physical network adapters (&lt;code&gt;eth0&lt;/code&gt; and &lt;code&gt;eth1&lt;/code&gt;).&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🏷️ MAC Address Governance: Enter KubeMacPool
&lt;/h2&gt;

&lt;p&gt;In a standard Kubernetes cluster, pods receive random MAC addresses upon startup because Layer-2 MAC uniqueness rarely matters across routed overlay networks.&lt;/p&gt;

&lt;p&gt;However, when multiple VMs connect directly to an external Layer-2 bridge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If two VMs accidentally receive the same MAC address, an enterprise network collision occurs, dropping packets across the physical switch.&lt;/li&gt;
&lt;li&gt;If a VM loses its MAC address after a live migration or restart, external DHCP reservations and ARP tables break.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How KubeMacPool Works
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;KubeMacPool&lt;/strong&gt; is an intelligent admission controller deployed automatically with OpenShift Virtualization:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It manages a dedicated, configured pool of unique IEEE MAC addresses (e.g., &lt;code&gt;02:00:00:00:00:00&lt;/code&gt; to &lt;code&gt;02:FF:FF:FF:FF:FF&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;When a &lt;code&gt;VirtualMachine&lt;/code&gt; is submitted, KubeMacPool's mutating webhook intercepts the creation request and injects a guaranteed-unique static MAC address into the manifest.&lt;/li&gt;
&lt;li&gt;It tracks allocated MAC addresses in real-time, preventing duplicate assignments across all namespaces.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  ⚠️ Battle-Tested Gotchas &amp;amp; Production Traps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The Backup/Restore MAC Collision Trap
&lt;/h3&gt;

&lt;p&gt;When restoring a VM backup (via OADP/Velero) into a new namespace for disaster recovery testing, KubeMacPool will intercept the restore request and &lt;strong&gt;block it&lt;/strong&gt; with a webhook validation error:&lt;br&gt;
&lt;code&gt;MAC address already allocated to another VM&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  🛠️ The Solution:
&lt;/h4&gt;

&lt;p&gt;Temporarily instruct KubeMacPool to ignore the target restore namespace using an exclusion label:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc label namespace vm-workloads-restored &lt;span class="se"&gt;\&lt;/span&gt;
  mutatevirtualmachines.kubemacpool.io&lt;span class="o"&gt;=&lt;/span&gt;ignore &lt;span class="nt"&gt;--overwrite&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  2. Promiscuous Mode on Nested Hypervisors
&lt;/h3&gt;

&lt;p&gt;If your OpenShift cluster is running inside a nested virtualization platform (such as VMware vSphere or OpenStack):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The physical Linux bridge needs to pass foreign MAC addresses through the underlying vSwitch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ensure Promiscuous Mode and MAC Address Changes are set to "Accept"&lt;/strong&gt; on the host port group / security group, otherwise incoming traffic will be silently discarded by the hypervisor host.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. MTU Mismatch
&lt;/h3&gt;

&lt;p&gt;If your cluster SDN uses an MTU of &lt;code&gt;1450&lt;/code&gt; (Geneve encapsulation) but your external VLAN uses standard Ethernet MTU &lt;code&gt;1500&lt;/code&gt; or Jumbo Frames &lt;code&gt;9000&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensure your &lt;code&gt;NetworkAttachmentDefinition&lt;/code&gt; specifies the correct MTU matching the physical switch port to avoid silent TCP packet fragmentation and packet drops.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎯 Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Default Pod Network (&lt;code&gt;masquerade&lt;/code&gt;)&lt;/strong&gt; is ideal for Kubernetes-internal communications and standard Ingress routing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multus CNI (&lt;code&gt;cnv-bridge&lt;/code&gt;)&lt;/strong&gt; allows seamless, direct connection to physical enterprise VLANs and legacy subnets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KubeMacPool&lt;/strong&gt; ensures carrier-grade Layer-2 stability by preventing duplicate MAC addresses across your cluster fleet.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  ⏭️ Next Up: Enterprise Backup &amp;amp; Disaster Recovery (DR)
&lt;/h2&gt;

&lt;p&gt;Now that our VMs have reliable compute, high-performance storage, and enterprise networking, how do we protect them against catastrophic failures?&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Part 5&lt;/strong&gt;, we will construct an end-to-end Enterprise Disaster Recovery strategy using &lt;strong&gt;OADP&lt;/strong&gt; (Velero) and &lt;strong&gt;NooBaa (S3)&lt;/strong&gt;—including how to configure &lt;code&gt;QEMU Guest Agent&lt;/code&gt; hooks (&lt;code&gt;fsfreeze&lt;/code&gt; / &lt;code&gt;fsthaw&lt;/code&gt;) for 100% application-consistent snapshots.&lt;/p&gt;

&lt;p&gt;💬 &lt;strong&gt;How are you handling VM VLANs and IP management in your container platform? Let us know in the comments below!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>infrastructure</category>
      <category>kubernetes</category>
      <category>networking</category>
    </item>
    <item>
      <title>Part 3: Automated Boot Sources &amp; Custom Golden Images with CDI</title>
      <dc:creator>Muhammad Adel</dc:creator>
      <pubDate>Sun, 08 Nov 2026 22:53:03 +0000</pubDate>
      <link>https://dev.to/muhammad_adel_f144dae9138/part-3-automated-boot-sources-custom-golden-images-with-cdi-593f</link>
      <guid>https://dev.to/muhammad_adel_f144dae9138/part-3-automated-boot-sources-custom-golden-images-with-cdi-593f</guid>
      <description>&lt;p&gt;In a traditional virtualization world, provisioning a virtual machine often meant downloading a 15GB .iso file, mounting it through an administrative console, clicking through an OS installer wizard, and running post-install scripts.&lt;/p&gt;

&lt;p&gt;In a cloud-native platform like OpenShift Virtualization, that manual approach is unacceptable. &lt;/p&gt;

&lt;p&gt;We need VMs to spin up in seconds—just like containers—using pre-baked, customized Golden Images (Boot Sources) that update automatically on a schedule.&lt;/p&gt;

&lt;p&gt;In this third part of our series, we will walk through the end-to-end production workflow of building custom golden OS images, pushing them to the OpenShift Internal Registry, and automating recurring imports with CDI (Containerized Data Importer) and DataImportCron.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏗️ Architecture: The Automated Boot Source Pipeline
&lt;/h2&gt;

&lt;p&gt;The automated pipeline consists of three core stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Packaging: Wrapping a raw/qcow2 virtual disk into an ultra-lightweight containerDisk.&lt;/li&gt;
&lt;li&gt;Distribution: Storing the image inside the secured OpenShift Internal Registry.&lt;/li&gt;
&lt;li&gt;Import &amp;amp; Consumption: CDI continuously pulls updates, populates a DataVolume, and registers a ready-to-use DataSource for instant VM cloning.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🛠️ Stage 1: Build the containerDisk from Scratch
&lt;/h2&gt;

&lt;p&gt;A containerDisk is simply an OCI container image containing a disk image located at &lt;code&gt;/disk/&lt;/code&gt;. To keep it minimal, secure, and disconnected-friendly, we build it directly FROM scratch without pulling heavy base images like UBI.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Prepare File Permissions &amp;amp; Containerfile
&lt;/h3&gt;

&lt;p&gt;Before building, ensure the host qcow2 file has read-only permissions (0440), and assign ownership to UID 107:107 (the internal QEMU user inside KubeVirt pods):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;0440 custom-disk.qcow2

&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;' &amp;gt; Containerfile
FROM scratch
ADD --chown=107:107 custom-disk.qcow2 /disk/
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🚀 Stage 2: Push to the OpenShift Internal Registry
&lt;/h2&gt;

&lt;p&gt;To push images without relying on external registries, we leverage OpenShift's built-in registry.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Expose the Internal Registry Route
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc patch configs.imageregistry.operator.openshift.io/cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;merge &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'{"spec":{"defaultRoute":true}}'&lt;/span&gt;

&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;REGISTRY_ROUTE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;oc get route default-route &lt;span class="nt"&gt;-n&lt;/span&gt; openshift-image-registry &lt;span class="nt"&gt;--template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{{ .spec.host }}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Internal Registry: &lt;/span&gt;&lt;span class="nv"&gt;$REGISTRY_ROUTE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Login and Push
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;podman login &lt;span class="nt"&gt;--tls-verify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; kubeadmin &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;oc &lt;span class="nb"&gt;whoami&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$REGISTRY_ROUTE&lt;/span&gt;

podman build &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nv"&gt;$REGISTRY_ROUTE&lt;/span&gt;/openshift-virtualization-os-images/custom-os-disk:latest &lt;span class="nb"&gt;.&lt;/span&gt;
podman push &lt;span class="nt"&gt;--tls-verify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="nv"&gt;$REGISTRY_ROUTE&lt;/span&gt;/openshift-virtualization-os-images/custom-os-disk:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔐 Stage 3: Configure Authentication &amp;amp; TLS Certificates
&lt;/h2&gt;

&lt;p&gt;When the CDI importer pod pulls the image from the internal service address (&lt;code&gt;image-registry.openshift-image-registry.svc:5000&lt;/code&gt;), it requires credentials and cluster trust:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create the Pull Secret
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc create secret generic kubeadmin-reg-secret &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;accessKeyId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kubeadmin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;secretKey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;oc &lt;span class="nb"&gt;whoami&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; openshift-virtualization-os-images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Extract Cluster CA for TLS Verification
&lt;/h3&gt;

&lt;p&gt;To prevent x509 certificate errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc extract configmap/openshift-service-ca.crt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; openshift-virtualization-os-images &lt;span class="nt"&gt;--to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;- &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; service-ca.crt

oc create configmap internal-registry-ca &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ca.crt&lt;span class="o"&gt;=&lt;/span&gt;service-ca.crt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; openshift-virtualization-os-images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ⏰ Stage 4: Deploy the Automated DataImportCron
&lt;/h2&gt;

&lt;p&gt;Now we wire CDI to monitor the registry and automatically update a DataSource.&lt;/p&gt;

&lt;p&gt;Notice the crucial annotation &lt;code&gt;cdi.kubevirt.io/storage.bind.immediate.requested: "true"&lt;/code&gt;, which forces storage allocation immediately rather than getting stuck in WaitForFirstConsumer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;cat &amp;lt;&amp;lt; 'EOF' | oc apply -f -&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cdi.kubevirt.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DataImportCron&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;custom-os-image-cron&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openshift-virtualization-os-images&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;cdi.kubevirt.io/storage.bind.immediate.requested&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;garbageCollect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Outdated&lt;/span&gt;
  &lt;span class="na"&gt;importsToKeep&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;managedDataSource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;custom-os-datasource&lt;/span&gt;
  &lt;span class="na"&gt;retentionPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;None&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*/15&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;pullMethod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pod&lt;/span&gt;
          &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docker://image-registry.openshift-image-registry.svc:5000/openshift-virtualization-os-images/custom-os-disk:latest"&lt;/span&gt;
          &lt;span class="na"&gt;secretRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kubeadmin-reg-secret&lt;/span&gt;
          &lt;span class="na"&gt;certConfigMap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;internal-registry-ca&lt;/span&gt;
      &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;15Gi&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify Importer Status
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc get dataimportcron,dv,pvc,datasource &lt;span class="nt"&gt;-n&lt;/span&gt; openshift-virtualization-os-images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Wait until the DataSource reports READY: True.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Stage 5: Spin Up a VM in 5 Seconds!
&lt;/h2&gt;

&lt;p&gt;With our managed DataSource active, developers can spin up a production VM with a single CLI command using fast CSI cloning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;virtctl create vm &lt;span class="nt"&gt;--name&lt;/span&gt; production-rhel-vm &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume-datasource&lt;/span&gt; src:openshift-virtualization-os-images/custom-os-datasource &lt;span class="se"&gt;\&lt;/span&gt;
  | oc apply &lt;span class="nt"&gt;-f&lt;/span&gt; -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ⚠️ Battle-Tested Gotchas &amp;amp; Troubleshooting Guide
&lt;/h2&gt;

&lt;p&gt;Here are the top 5 errors engineers face when working with custom boot sources and CDI:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Common Error&lt;/th&gt;
&lt;th&gt;Root Cause&lt;/th&gt;
&lt;th&gt;Proven Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;error: token is not available&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Relying on stale KUBECONFIG without active login.&lt;/td&gt;
&lt;td&gt;Always run &lt;code&gt;oc login -u kubeadmin&lt;/code&gt; before generating tokens.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Permission denied on VM boot&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Disk file inside container not owned by user 107.&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;chmod 0440&lt;/code&gt; and &lt;code&gt;ADD --chown=107:107&lt;/code&gt; in &lt;code&gt;Containerfile&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;x509: certificate signed by unknown authority&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Importer pod does not trust internal service CA.&lt;/td&gt;
&lt;td&gt;Extract &lt;code&gt;openshift-service-ca.crt&lt;/code&gt; and bind via &lt;code&gt;certConfigMap&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;pullMethod: node ignores secretRef&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Node pull method uses kubelet service accounts, not user secrets.&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;pullMethod: pod&lt;/code&gt; when pulling with custom secrets.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stuck in WaitForFirstConsumer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;StorageClass waits for VM scheduling before binding PVC.&lt;/td&gt;
&lt;td&gt;Add annotation &lt;code&gt;cdi.kubevirt.io/storage.bind.immediate.requested: "true"&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🎯 Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Use &lt;strong&gt;FROM scratch&lt;/strong&gt; and assign ownership to &lt;code&gt;107:107&lt;/code&gt; for lightweight, secure &lt;code&gt;containerDisk&lt;/code&gt; images.&lt;/li&gt;
&lt;li&gt;Automate lifecycle updates with &lt;strong&gt;DataImportCron&lt;/strong&gt; so developers always provision from the latest patched OS baseline.&lt;/li&gt;
&lt;li&gt;Don't forget immediate binding annotations to eliminate PVC provisioning delays.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  ⏭️ Next Up: Advanced VM Networking &amp;amp; KubeMacPool
&lt;/h2&gt;

&lt;p&gt;Now that our VMs can boot in seconds, how do we connect them to enterprise networks, VLANs, and external subnets?&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Part 4&lt;/strong&gt;, we dive deep into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pod Networking (Masquerade) vs. Bridged Secondary Networks with Multus CNI.&lt;/li&gt;
&lt;li&gt;Managing MAC address allocation and avoiding duplicate MAC clashes with KubeMacPool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💬 &lt;strong&gt;How do you currently build and distribute OS golden images in your infrastructure? Share your workflow in the comments below!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>automation</category>
      <category>devops</category>
      <category>infrastructure</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Part 2: Storage Deep Dive — Block vs Filesystem &amp; TopoLVM vs ODF</title>
      <dc:creator>Muhammad Adel</dc:creator>
      <pubDate>Sun, 08 Nov 2026 22:44:50 +0000</pubDate>
      <link>https://dev.to/muhammad_adel_f144dae9138/part-2-storage-deep-dive-block-vs-filesystem-topolvm-vs-odf-15je</link>
      <guid>https://dev.to/muhammad_adel_f144dae9138/part-2-storage-deep-dive-block-vs-filesystem-topolvm-vs-odf-15je</guid>
      <description>&lt;p&gt;Storage is often the make-or-break factor when migrating traditional virtual machines to Kubernetes. &lt;/p&gt;

&lt;p&gt;In traditional environments (like VMware ESXi or Nutanix), storage is handled by centralized SAN/NAS arrays or vSAN. But in &lt;strong&gt;OpenShift Virtualization&lt;/strong&gt;, storage is managed entirely through the Kubernetes &lt;strong&gt;CSI (Container Storage Interface)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you misconfigure your storage layers, you risk sluggish I/O latency, broken live migrations, or failing CSI snapshot backups.&lt;/p&gt;

&lt;p&gt;In this second part of our series, we demystify the two most critical storage decisions in OpenShift Virtualization:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Block vs. Filesystem Volume Modes&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LVM Storage (TopoLVM) vs. OpenShift Data Foundation (ODF)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The infamous TopoLVM extent mismatch trap&lt;/strong&gt; (and how to fix it).&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🏗️ Storage Architectural Overview
&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://googleusercontent.com/image_generation_content/10_28" rel="noopener noreferrer"&gt;http://googleusercontent.com/image_generation_content/10_28&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's dissect these architectures to understand where each shines.&lt;/p&gt;




&lt;h2&gt;
  
  
  1️⃣ Volume Modes: Block vs. Filesystem
&lt;/h2&gt;

&lt;p&gt;When creating a &lt;code&gt;PersistentVolumeClaim&lt;/code&gt; (PVC) for a VM disk, Kubernetes lets you declare a &lt;code&gt;volumeMode&lt;/code&gt;. Choosing the wrong one can degrade write throughput by up to 25–40%.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔹 Filesystem Mode (&lt;code&gt;volumeMode: Filesystem&lt;/code&gt;)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;How it works:&lt;/strong&gt; A standard Linux filesystem (such as &lt;code&gt;ext4&lt;/code&gt; or &lt;code&gt;xfs&lt;/code&gt;) is formatted onto the underlying storage volume. The VM's virtual disk is stored as an image file (e.g., &lt;code&gt;disk.img&lt;/code&gt; or &lt;code&gt;.qcow2&lt;/code&gt;) inside that filesystem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Penalty:&lt;/strong&gt; Double filesystem overhead! The guest OS inside the VM writes to its own filesystem, which writes to an image file, which in turn writes to the host node's filesystem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When to use:&lt;/strong&gt; Great for simple scratch disks, file sharing, or legacy CSI drivers that do not support raw block volumes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔹 Block Mode (&lt;code&gt;volumeMode: Block&lt;/code&gt;) — &lt;em&gt;The Gold Standard for VMs&lt;/em&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;How it works:&lt;/strong&gt; The raw block device is passed directly into the &lt;code&gt;virt-launcher&lt;/code&gt; pod and mapped straight to the QEMU/KVM process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Advantage:&lt;/strong&gt; Zero host filesystem overhead, direct I/O execution, and lower CPU usage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When to use:&lt;/strong&gt; Production enterprise VMs, high-throughput databases (PostgreSQL, Oracle, SQL Server), and I/O-intensive workloads.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Example: Production Block Mode PVC
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PersistentVolumeClaim&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;database-disk-pvc&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vm-workloads&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;accessModes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ReadWriteOnce&lt;/span&gt;
  &lt;span class="na"&gt;volumeMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Block&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;--- Bypasses host filesystem overhead&lt;/span&gt;
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;50Gi&lt;/span&gt;
  &lt;span class="na"&gt;storageClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;odf-ceph-rbd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2️⃣ Storage Engines: TopoLVM (LVMS) vs. ODF (Ceph)
&lt;/h2&gt;

&lt;p&gt;Choosing the right storage backend depends on your cluster topology and business requirements:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature / Criterion&lt;/th&gt;
&lt;th&gt;LVM Storage (TopoLVM / LVMS)&lt;/th&gt;
&lt;th&gt;OpenShift Data Foundation (ODF)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Backend Technology&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Local Linux LVM Volume Groups&lt;/td&gt;
&lt;td&gt;Distributed Ceph (RBD &amp;amp; CephFS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ideal Deployment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single Node OpenShift (SNO), Edge&lt;/td&gt;
&lt;td&gt;Multi-node HA Clusters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;I/O Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Ultra-low&lt;/strong&gt; (Direct NVMe speeds)&lt;/td&gt;
&lt;td&gt;Low to moderate (Network traversal)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Live Migration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ &lt;strong&gt;No&lt;/strong&gt; (Disks pinned to node)&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;Yes&lt;/strong&gt; (Shared network storage)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  ⚠️ Why does the TopoLVM Extent bug happen?
&lt;/h3&gt;

&lt;p&gt;When TopoLVM provisions a Logical Volume (LV) from a physical volume group, it rounds the requested disk size up to the nearest Physical Extent (PE) boundary.&lt;/p&gt;

&lt;p&gt;If your snapshot hangs, patch the PVC request size upward by 1Gi:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc patch pvc &amp;lt;pvc-name&amp;gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &amp;lt;vm-namespace&amp;gt; &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'[{"op": "replace", "path": "/spec/resources/requests/storage", "value": "36Gi"}]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🎯 Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Always prefer volumeMode: Block&lt;/strong&gt; for production VM disks to achieve native bare-metal I/O performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use ODF&lt;/strong&gt; if live migration is a business requirement; &lt;strong&gt;use TopoLVM&lt;/strong&gt; if raw NVMe speed on Single-Node is your priority.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>architecture</category>
      <category>infrastructure</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Part 1: Architecture Under the Hood</title>
      <dc:creator>Muhammad Adel</dc:creator>
      <pubDate>Sun, 08 Nov 2026 22:40:59 +0000</pubDate>
      <link>https://dev.to/muhammad_adel_f144dae9138/part-1-architecture-under-the-hood-1006</link>
      <guid>https://dev.to/muhammad_adel_f144dae9138/part-1-architecture-under-the-hood-1006</guid>
      <description>&lt;p&gt;When engineers first hear about &lt;strong&gt;OpenShift Virtualization&lt;/strong&gt; (formerly CNV / Container-Native Virtualization), their immediate reaction is often skepticism:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Wait... are you putting an entire virtual machine inside a container? Isn't that anti-pattern virtualization-ception?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The short answer: &lt;strong&gt;No.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;OpenShift Virtualization does not run a hypervisor inside a container. Instead, it leverages the open-source &lt;strong&gt;KubeVirt&lt;/strong&gt; technology to treat Virtual Machines as native Kubernetes workloads—giving VMs the same declarative deployment, GitOps pipelines, software-defined networking, and storage capabilities as regular containers.&lt;/p&gt;

&lt;p&gt;In this first part of our series, we're popping the hood to see how KubeVirt actually coordinates compute, memory, and kernel primitives across an OpenShift cluster.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏗️ The Big Picture Architecture
&lt;/h2&gt;

&lt;p&gt;At its core, a virtual machine on Linux is simply a &lt;strong&gt;QEMU process&lt;/strong&gt; running in user space, accelerated by the &lt;strong&gt;KVM&lt;/strong&gt; kernel module. &lt;/p&gt;

&lt;p&gt;KubeVirt encapsulates that QEMU process inside standard Linux namespaces (cgroups, network, PID) through a specialized pod called &lt;code&gt;virt-launcher&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is how the control plane and worker nodes interact:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://googleusercontent.com/image_generation_content/8_25" rel="noopener noreferrer"&gt;http://googleusercontent.com/image_generation_content/8_25&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's break down each component shown above.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 The Control Plane Components
&lt;/h2&gt;

&lt;p&gt;The control plane orchestrates the lifecycle of virtual machines using standard Kubernetes controller patterns:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;virt-operator&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The operator responsible for deploying, upgrading, and managing the lifecycle of all KubeVirt custom resource definitions (CRDs) and services across the cluster. It ensures your virtualization stack stays healthy and up-to-date.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;code&gt;virt-api&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Acts as the entry door for virtualization requests. It provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Admission Webhooks:&lt;/strong&gt; Validates and mutates VM specifications before they hit &lt;code&gt;etcd&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subresource Endpoints:&lt;/strong&gt; Powers interactive features like VNC graphical consoles, serial consoles, and power actions (start, stop, pause, restart) via &lt;code&gt;virtctl&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. &lt;code&gt;virt-controller&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The brain behind state reconciliation. When you request a virtual machine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It reads your &lt;code&gt;VirtualMachine&lt;/code&gt; manifest.&lt;/li&gt;
&lt;li&gt;It coordinates with the Kubernetes scheduler by generating a matching Pod specification.&lt;/li&gt;
&lt;li&gt;It tracks VM lifecycle transitions (e.g., Scheduling ➔ Starting ➔ Running ➔ Stopping).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚙️ The Node-Level Components
&lt;/h2&gt;

&lt;p&gt;Down on the worker nodes where compute happens, two key components make virtualization possible:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;virt-handler&lt;/code&gt; (DaemonSet)
&lt;/h3&gt;

&lt;p&gt;Runs as a privileged pod on every worker node eligible to run virtual machines. Its responsibilities include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Communicating with the local node's &lt;code&gt;libvirt&lt;/code&gt; daemon.&lt;/li&gt;
&lt;li&gt;Interfacing with host hardware devices (like &lt;code&gt;/dev/kvm&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Executing node-specific operations triggered by the &lt;code&gt;virt-controller&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. &lt;code&gt;virt-launcher&lt;/code&gt; Pod
&lt;/h3&gt;

&lt;p&gt;This is the pod scheduled by Kubernetes to host your virtual machine. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It creates the cgroup and namespace boundaries.&lt;/li&gt;
&lt;li&gt;It executes &lt;code&gt;libvirt&lt;/code&gt; and boots the &lt;code&gt;qemu-kvm&lt;/code&gt; process.&lt;/li&gt;
&lt;li&gt;If your VM crashes or shuts down, the &lt;code&gt;virt-launcher&lt;/code&gt; pod terminates cleanly.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📄 The Two Essential CRDs: VM vs. VMI
&lt;/h2&gt;

&lt;p&gt;One of the most common early confusions in KubeVirt is the difference between:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Custom Resource&lt;/th&gt;
&lt;th&gt;Equivalent in K8s&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;VirtualMachine&lt;/code&gt; (VM)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Deployment&lt;/code&gt; / &lt;code&gt;StatefulSet&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Declarative definition of the VM (CPU, memory, disks). Maintains desired running state even across reboots.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;VirtualMachineInstance&lt;/code&gt; (VMI)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Pod&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The active, ephemeral running instance of a VM. Created dynamically when a &lt;code&gt;VM&lt;/code&gt; is started (&lt;code&gt;running: true&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
yaml`
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
  name: fedora-workload
spec:
  running: true  # &amp;lt;--- Setting this creates the active VMI
  template:
    spec:
      domain:
        devices:
          disks:
            - name: containerdisk
              disk:
                bus: virtio
        resources:
          requests:
            memory: 2Gi
            cpu: 2
      volumes:
        - name: containerdisk
          containerDisk:
            image: quay.io/containerdisks/fedora:latest

🛠️ Step-by-Step: Enabling OpenShift Virtualization

To get started on your OpenShift cluster via CLI:
1. Subscribe to the Operator
Bash

cat &amp;lt;&amp;lt;EOF | oc apply -f -
apiVersion: [operators.coreos.com/v1alpha1](https://operators.coreos.com/v1alpha1)
kind: Subscription
metadata:
  name: hco-operatorhub
  namespace: openshift-cnv
spec:
  channel: stable
  name: kubevirt-hyperconverged
  source: redhat-operators
  sourceNamespace: openshift-marketplace
EOF

2. Deploy the HyperConverged Cluster (HCO) CR

Once the operator CSV reports Succeeded, initialize the control plane:
Bash

cat &amp;lt;&amp;lt;EOF | oc apply -f -
apiVersion: hco.kubevirt.io/v1beta1
kind: HyperConverged
metadata:
  name: kubevirt-hyperconverged
  namespace: openshift-cnv
spec: {}
EOF

3. Verify Health

Verify that all control plane and node daemon pods are running:
Bash

oc get pods -n openshift-cnv

💡 Battle-Tested Gotchas &amp;amp; Pro-Tips
⚠️ Gotcha: Is Hardware Virtualization Enabled?

If your worker nodes run inside a public cloud or nested lab environment, KVM hardware acceleration (/dev/kvm) might not be exposed by default.

Verify your node labels:
Bash

oc get nodes -l kubevirt.io/schedulable=true

If you see no nodes matching this label, check if nested virtualization is enabled on the hypervisor host:
Bash

# On your node terminal:
ls -l /dev/kvm
egrep -c '(vmx|svm)' /proc/cpuinfo

For dev/lab testing only: You can enable software emulation inside the HyperConverged CR under spec.configuration.developerConfiguration.useEmulation: true, though it is strictly not supported for production.
⏭️ What's Next?

Now that the control plane is live and you understand the anatomy of a virt-launcher pod, the next critical hurdle is Storage.

In Part 2, we will dissect:

    Block vs. Filesystem volume modes.

    TopoLVM / LVM Storage vs. ODF.

    Avoiding the dangerous extent size calculation bug when creating CSI snapshots.

💬 Question for you: Are you currently planning a migration from legacy hypervisors to OpenShift Virtualization, or experimenting in labs? Drop your thoughts in the comments!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>architecture</category>
      <category>devops</category>
      <category>infrastructure</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>OpenShift Virtualization (CNV) Zero to Hero: Roadmap</title>
      <dc:creator>Muhammad Adel</dc:creator>
      <pubDate>Sun, 08 Nov 2026 22:34:11 +0000</pubDate>
      <link>https://dev.to/muhammad_adel_f144dae9138/openshift-virtualization-cnv-zero-to-hero-roadmap-4p6j</link>
      <guid>https://dev.to/muhammad_adel_f144dae9138/openshift-virtualization-cnv-zero-to-hero-roadmap-4p6j</guid>
      <description>&lt;h2&gt;
  
  
  🚀 Welcome to the Series!
&lt;/h2&gt;

&lt;p&gt;Virtualization isn't dead—it's evolving. As enterprise infrastructure shifts toward unified, cloud-native platforms, running Virtual Machines (VMs) alongside containerized microservices has become a major game changer.&lt;/p&gt;

&lt;p&gt;Whether you're looking into modernizing legacy infrastructure, evaluating alternatives to traditional hypervisors (like VMware ESXi), or managing high-performance stateful workloads, &lt;strong&gt;OpenShift Virtualization&lt;/strong&gt; (powered by open-source &lt;strong&gt;KubeVirt&lt;/strong&gt;) brings VMs into the Kubernetes control plane as first-class citizens.&lt;/p&gt;

&lt;p&gt;In this hands-on, battle-tested series, we will bridge the gap between theory and production realities—complete with copy-paste YAML manifests, CLI commands (&lt;code&gt;oc&lt;/code&gt; and &lt;code&gt;virtctl&lt;/code&gt;), architecture breakdowns, and troubleshooting guides for real-world edge cases.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Who Is This Series For?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DevOps &amp;amp; Platform Engineers&lt;/strong&gt; responsible for managing hybrid workloads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Systems Administrators&lt;/strong&gt; transitioning from traditional virtualization (VMware / Proxmox / Hyper-V) to cloud-native platforms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes Practitioners &amp;amp; SREs&lt;/strong&gt; looking to master KubeVirt, storage provisioning, and disaster recovery.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📑 The Master Index (Table of Contents)
&lt;/h2&gt;

&lt;p&gt;Bookmark this page! As each part goes live, links will be updated here:&lt;/p&gt;

&lt;h3&gt;
  
  
  🔹 Part 1: The Foundations &amp;amp; Architecture Under the Hood
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Status: Coming Soon&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What we cover:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Why run VMs on Kubernetes?&lt;/li&gt;
&lt;li&gt;KubeVirt architecture explained: &lt;code&gt;virt-operator&lt;/code&gt;, &lt;code&gt;virt-api&lt;/code&gt;, &lt;code&gt;virt-controller&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Node-level mechanics: &lt;code&gt;virt-handler&lt;/code&gt; and the &lt;code&gt;virt-launcher&lt;/code&gt; pod.&lt;/li&gt;
&lt;li&gt;Installing the OpenShift Virtualization Operator step-by-step.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔹 Part 2: Storage Architecture &amp;amp; The Volume Extent Trap
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Status: Coming Soon&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What we cover:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Persistent Storage for VMs: Block vs. Filesystem mode.&lt;/li&gt;
&lt;li&gt;Working with LVM Storage (TopoLVM) and OpenShift Data Foundation (ODF).&lt;/li&gt;
&lt;li&gt;StorageClasses and VolumeSnapshotClasses configuration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production Gotcha:&lt;/strong&gt; Solving the infamous TopoLVM extent mismatch (&lt;code&gt;OutOfRange: requested size is smaller than source logical volume&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔹 Part 3: Automated Boot Sources &amp;amp; Custom Golden Images
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Status: Coming Soon&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What we cover:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Understanding CDI (Containerized Data Importer) and &lt;code&gt;DataVolume&lt;/code&gt; CRDs.&lt;/li&gt;
&lt;li&gt;Building minimal &lt;code&gt;containerDisk&lt;/code&gt; images from scratch using &lt;code&gt;Containerfile&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Pushing custom disks to the OpenShift Internal Registry with &lt;code&gt;kubeadmin&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Scheduling automated gold image updates using &lt;code&gt;DataImportCron&lt;/code&gt; and &lt;code&gt;DataSource&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔹 Part 4: Advanced VM Networking &amp;amp; MAC Address Governance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Status: Coming Soon&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What we cover:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Default Pod Network (Masquerade) vs. Secondary Networks.&lt;/li&gt;
&lt;li&gt;Bridged networking with &lt;strong&gt;Multus CNI&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Managing Layer-2 MAC addresses seamlessly with &lt;strong&gt;KubeMacPool&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Handling MAC address collisions during workload cloning and migration.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔹 Part 5: Enterprise Backup &amp;amp; Disaster Recovery (DR)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Status: Coming Soon&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What we cover:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Backup &amp;amp; Restore architecture using &lt;strong&gt;OADP&lt;/strong&gt; (Velero) and local &lt;strong&gt;NooBaa (S3)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Application consistency: Configuring &lt;code&gt;QEMU Guest Agent&lt;/code&gt; and hooks (&lt;code&gt;fsfreeze&lt;/code&gt; / &lt;code&gt;fsthaw&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;End-to-end VM backup and cross-namespace restore.&lt;/li&gt;
&lt;li&gt;Bypassing mutating webhooks and snapshot binding errors during recovery.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔹 Part 6: Day-2 Operations, Live Migration &amp;amp; Troubleshooting
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Status: Coming Soon&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What we cover:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Zero-downtime Live Migration across worker nodes.&lt;/li&gt;
&lt;li&gt;Node evacuation (&lt;code&gt;oc adm drain&lt;/code&gt;) with active VM workloads.&lt;/li&gt;
&lt;li&gt;Top 5 production failure modes and how to debug them (&lt;code&gt;virtctl&lt;/code&gt;, crash loops, and stuck PVCs).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🛠️ Recommended Lab Environment
&lt;/h2&gt;

&lt;p&gt;To follow along with the hands-on labs in this series, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;strong&gt;OpenShift 4.12+&lt;/strong&gt; cluster (SNO / Single Node OpenShift or a standard multi-node cluster).&lt;/li&gt;
&lt;li&gt;Bare-metal or nested virtualization enabled on worker nodes (&lt;code&gt;/dev/kvm&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Access to &lt;code&gt;oc&lt;/code&gt; and &lt;code&gt;virtctl&lt;/code&gt; CLI tools.&lt;/li&gt;
&lt;li&gt;Cluster-admin privileges.&lt;/li&gt;
&lt;/ul&gt;







&lt;h2&gt;
  
  
  💬 Stay Connected
&lt;/h2&gt;

&lt;p&gt;Follow my profile on DEV.to and save this post to your reading list. If there is a specific OpenShift Virtualization challenge you're currently facing in your environment, drop a comment below and we might include it in the series!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next Up:&lt;/strong&gt; &lt;em&gt;Part 1: The Foundations &amp;amp; Architecture Under the Hood.&lt;/em&gt; Stay tuned!&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>devops</category>
      <category>infrastructure</category>
      <category>kubernetes</category>
    </item>
  </channel>
</rss>
