Split View: VMI spec가 libvirt domain XML로 변환되는 과정
VMI spec가 libvirt domain XML로 변환되는 과정
- 들어가며
- 변환은 단순한 필드 복사가 아니다
- 핵심 진입점
- api.Domain은 왜 중요한가
- 디스크 변환에서 어떤 일이 일어나는가
- 볼륨과 디스크는 왜 분리되어 있는가
- 변환 컨텍스트가 필요한 이유
- 네트워크와 CPU도 같은 방식으로 변환된다
- domain XML은 언제 완성되는가
- migration 시에는 왜 domain XML을 다시 다루는가
- 왜 이 레이어가 디버깅의 핵심인가
- 자주 하는 오해
- 마무리
들어가며
사용자는 Kubernetes 스타일의 YAML로 VMI를 선언한다. 하지만 libvirt와 QEMU는 결국 domain spec와 XML을 기준으로 guest를 실행한다. 따라서 KubeVirt에는 두 세계를 이어 주는 굉장히 중요한 translation layer가 필요하다. 이 역할을 하는 것이 pkg/virt-launcher/virtwrap/converter다.
이 글의 핵심 질문은 다음이다. VMI spec는 어떤 과정을 거쳐 libvirt domain XML이 되는가?
변환은 단순한 필드 복사가 아니다
처음에는 spec.domain.cpu가 libvirt CPU 필드로, spec.domain.devices.disks가 libvirt disk 항목으로 그냥 복사될 것 같지만 실제로는 훨씬 복잡하다.
이유는 세 가지다.
- guest 장치 이름을 계산해야 한다
- Pod에 준비된 실제 디스크 경로를 찾아야 한다
- architecture, virtio 모델, security, migration 같은 정책을 반영해야 한다
즉 converter는 serialization layer가 아니라 정책 포함 변환기다.
핵심 진입점
converter.go에서 가장 중요한 함수 이름은 다음과 같다.
Convert_v1_VirtualMachineInstance_To_api_DomainConvert_v1_Disk_To_api_DiskConvert_v1_Volume_To_api_DiskConvert_v1_BlockSize_To_api_BlockIO
이 이름만 봐도 구조가 보인다. 최종 목표는 VMI 전체를 api.Domain으로 바꾸는 것이고, 그 안에서 디스크, 볼륨, 블록 IO, CPU, NIC 같은 세부 변환 함수가 계층적으로 호출된다.
api.Domain은 왜 중요한가
KubeVirt 내부에서 api.Domain은 guest 실행을 위한 canonical intermediate representation에 가깝다.
- VMI는 Kubernetes 친화적 선언 모델
api.Domain은 libvirt 친화적 실행 모델- 최종적으로 XML marshal이 가능
즉 api.Domain은 KubeVirt 내부의 "가상화 중간 언어" 같은 역할을 한다.
디스크 변환에서 어떤 일이 일어나는가
Convert_v1_Disk_To_api_Disk를 보면 단순 매핑 이상의 일이 많이 보인다.
1. bus별 device name 계산
virtio, scsi, cdrom, lun에 따라 target bus와 device name이 달라진다. 예를 들어 virtio 디스크는 vda, vdb 식의 guest-visible 이름이 생긴다.
2. PCI 주소 검증
일부 bus 타입에서는 PCI 주소를 임의로 주는 것이 허용되지 않는다. converter는 이 제약을 적용한다.
3. shareable, cache, discard 정책 적용
shared disk면 cache가 none이어야 하는 등 단순히 YAML 값을 복사하는 것이 아니라 조합 제약을 검사한다.
4. boot order와 alias 추가
guest boot 순서와 사용자 정의 alias도 변환 과정에서 만들어진다.
즉 디스크 하나만 봐도 converter는 단순 serializer가 아니라 guest 하드웨어 모델 설계기다.
볼륨과 디스크는 왜 분리되어 있는가
Kubernetes 쪽 VMI spec에는 volumes와 disks가 분리되어 있다.
volumes: 데이터 소스가 무엇인가disks: guest에 어떤 장치로 붙일 것인가
예를 들면 같은 PVC라도:
- 일반 디스크로 붙일 수도 있고
- cdrom처럼 노출할 수도 있고
- boot order를 다르게 줄 수도 있다
converter는 이 둘을 결합해 "이 소스를 guest에 어떤 libvirt disk로 붙일지"를 계산한다.
변환 컨텍스트가 필요한 이유
convertertypes.ConverterContext 계열을 보면 converter가 단순 stateless 함수가 아니라는 것을 알 수 있다. 변환에는 주변 문맥이 필요하다.
대표적으로 다음 정보가 context에 들어간다.
- architecture
- allowEmulation 여부
- hypervisor device availability
- block PVC 또는 filesystem PVC 여부
- disk creator
- launch security 옵션
- virtio transitional 모델 사용 여부
이 말은 같은 VMI spec라도 노드 아키텍처와 실행 환경에 따라 최종 domain spec가 달라질 수 있다는 뜻이다.
네트워크와 CPU도 같은 방식으로 변환된다
디스크만 특별한 것이 아니다. NIC, CPU topology, NUMA, launch security, EFI, iothreads 같은 것들도 converter가 libvirt domain representation으로 변환한다.
예를 들어:
- dedicated CPU면 pinning 관련 필드가 달라진다
- launch security가 켜지면 IOMMU 같은 옵션이 들어간다
- architecture가 바뀌면 machine type과 일부 장치 표현이 바뀐다
즉 converter는 guest 하드웨어 구성 전체를 책임진다.
domain XML은 언제 완성되는가
실무적으로는 다음 순서로 이해하면 된다.
- VMI spec를 읽는다.
- converter가
api.Domain을 만든다. - 필요하면 hook이나 sidecar가 domain spec를 추가 수정한다.
- 최종 domain XML이 libvirt에 전달된다.
- libvirt가 이를 기반으로 QEMU를 실행한다.
따라서 "어떤 guest 장치가 왜 그렇게 보이는가"를 알고 싶다면 거의 항상 converter부터 읽어야 한다.
migration 시에는 왜 domain XML을 다시 다루는가
live-migration-source.go에는 migratableDomXML 같은 로직이 있다. migration 때는 source domain XML을 그대로 넘기면 안 되고, 메타데이터를 정리하거나 target 쪽 경로에 맞게 디스크 파일 경로를 수정해야 할 수 있다.
즉 domain XML은 launch 직전 한 번만 중요한 것이 아니라:
- 초기 boot 시
- migration 시
- 일부 hotplug와 target 준비 시
여러 단계에서 계속 핵심 데이터 구조로 사용된다.
왜 이 레이어가 디버깅의 핵심인가
많은 문제는 YAML과 QEMU 사이의 어딘가에서 생긴다. 그 중간이 바로 converter다.
이런 문제를 설명해 준다
- 왜 guest 디스크 이름이 예상과 다를까
- 왜 특정 옵션 조합이 거부될까
- 왜 virtio 대신 다른 모델이 들어갔을까
- 왜 migration 시 디스크 경로를 다시 바꾸는가
만약 launcher Pod는 살아 있고 libvirt define 단계에서 실패한다면, converter 결과가 가장 먼저 의심해야 할 대상이다.
자주 하는 오해
오해 1: VMI spec는 거의 libvirt XML과 같다
아니다. VMI spec는 Kubernetes 선언 모델이고, libvirt XML은 하이퍼바이저 실행 모델이다. 둘 사이에는 KubeVirt가 해석해야 할 정책이 많다.
오해 2: 디스크는 volume 경로만 맞으면 끝이다
아니다. bus, boot order, discard, cache, reservation, block size 같은 guest-visible 속성이 모두 중요하다.
오해 3: converter는 launch 때 한 번만 중요하다
아니다. migration과 hotplug, target 준비 과정에서도 domain spec와 XML 변형이 다시 중요해진다.
마무리
KubeVirt의 converter는 VMI spec를 libvirt domain model로 번역하는 가장 중요한 중간 계층이다. 이 레이어는 단순 필드 복사가 아니라, guest 하드웨어 모델, 실행 환경, 보안 옵션, migration 제약을 함께 반영한다. 그래서 KubeVirt를 깊게 이해하려면 controller 다음에 꼭 읽어야 할 패키지가 바로 pkg/virt-launcher/virtwrap/converter다.
다음 글에서는 이 변환 결과가 실제 스토리지 소스와 어떻게 만나며, PVC, DataVolume, container disk, hotplug 디스크가 guest에 어떤 경로로 붙는지 살펴보겠다.
How VMI Spec Gets Converted to libvirt Domain XML
- Introduction
- Conversion Is Not Simple Field Copying
- Key Entry Points
- Why api.Domain Is Important
- What Happens in Disk Conversion
- Why Volumes and Disks Are Separated
- Why Conversion Context Is Needed
- Network and CPU Are Converted the Same Way
- When Is Domain XML Completed
- Why Domain XML Is Revisited During Migration
- Why This Layer Is the Key to Debugging
- Common Misconceptions
- Conclusion
Introduction
Users declare VMIs in Kubernetes-style YAML. But libvirt and QEMU ultimately execute guests based on domain spec and XML. Therefore KubeVirt needs a critically important translation layer that bridges these two worlds. This role is filled by pkg/virt-launcher/virtwrap/converter.
The core question of this article is: What process does a VMI spec go through to become libvirt domain XML?
Conversion Is Not Simple Field Copying
At first, it seems like spec.domain.cpu would simply copy to the libvirt CPU field, and spec.domain.devices.disks to libvirt disk entries. But in reality, it is much more complex.
The reasons are threefold:
- Guest device names must be calculated
- Actual disk paths prepared in the Pod must be found
- Policies like architecture, virtio model, security, and migration must be reflected
In other words, the converter is not a serialization layer but a policy-inclusive transformer.
Key Entry Points
The most important function names in converter.go are:
Convert_v1_VirtualMachineInstance_To_api_DomainConvert_v1_Disk_To_api_DiskConvert_v1_Volume_To_api_DiskConvert_v1_BlockSize_To_api_BlockIO
Just from these names, the structure is visible. The ultimate goal is to convert the entire VMI to api.Domain, and within that, detailed conversion functions for disk, volume, block IO, CPU, NIC, etc. are called hierarchically.
Why api.Domain Is Important
Within KubeVirt, api.Domain is close to a canonical intermediate representation for guest execution.
- VMI is a Kubernetes-friendly declarative model
api.Domainis a libvirt-friendly execution model- It can ultimately be XML marshalled
In other words, api.Domain serves as an "intermediate language for virtualization" within KubeVirt.
What Happens in Disk Conversion
Looking at Convert_v1_Disk_To_api_Disk, you can see much beyond simple mapping.
1. Device Name Calculation by Bus
Target bus and device names differ depending on virtio, scsi, cdrom, or lun. For example, virtio disks get guest-visible names like vda, vdb.
2. PCI Address Verification
For some bus types, assigning arbitrary PCI addresses is not allowed. The converter applies these constraints.
3. Shareable, Cache, Discard Policy Application
For shared disks, cache must be none, etc. -- it's not simply copying YAML values but checking combination constraints.
4. Boot Order and Alias Addition
Guest boot order and user-defined aliases are also created during the conversion process.
In other words, looking at just one disk, the converter is not a simple serializer but a guest hardware model designer.
Why Volumes and Disks Are Separated
In the Kubernetes-side VMI spec, volumes and disks are separated:
volumes: what is the data sourcedisks: what device to attach to the guest
For example, the same PVC can be:
- Attached as a regular disk
- Exposed like a cdrom
- Given a different boot order
The converter combines these two to calculate "which libvirt disk to attach this source to the guest as."
Why Conversion Context Is Needed
Looking at convertertypes.ConverterContext types, you can see the converter is not a simple stateless function. Conversion requires surrounding context.
Typically the following information is included in the context:
- Architecture
- Whether emulation is allowed
- Hypervisor device availability
- Whether PVC is block or filesystem
- Disk creator
- Launch security options
- Whether to use virtio transitional model
This means that even for the same VMI spec, the final domain spec can differ depending on node architecture and execution environment.
Network and CPU Are Converted the Same Way
Disks are not special alone. NICs, CPU topology, NUMA, launch security, EFI, iothreads, etc. are also converted by the converter into libvirt domain representation.
For example:
- Dedicated CPU changes pinning-related fields
- Enabling launch security adds options like IOMMU
- Changing architecture changes machine type and some device representations
In other words, the converter is responsible for the entire guest hardware configuration.
When Is Domain XML Completed
Practically, understand it in this order:
- VMI spec is read.
- The converter creates
api.Domain. - If needed, hooks or sidecars additionally modify the domain spec.
- The final domain XML is delivered to libvirt.
- libvirt executes QEMU based on this.
Therefore, if you want to know "why a certain guest device looks that way," you almost always need to start by reading the converter.
Why Domain XML Is Revisited During Migration
live-migration-source.go contains logic like migratableDomXML. During migration, the source domain XML cannot be passed as-is -- metadata may need to be cleaned up or disk file paths may need to be modified to match the target side's paths.
In other words, domain XML is not important just once right before launch:
- At initial boot
- During migration
- During some hotplug and target preparation
It continues to be used as a core data structure at multiple stages.
Why This Layer Is the Key to Debugging
Many problems arise somewhere between YAML and QEMU. That middle ground is the converter.
It Explains Problems Like
- Why guest disk names are different from expected
- Why certain option combinations are rejected
- Why a different model was used instead of virtio
- Why disk paths are changed again during migration
If the launcher Pod is alive and there's a failure at the libvirt define stage, the converter result should be the first suspect.
Common Misconceptions
Misconception 1: VMI Spec Is Almost the Same as libvirt XML
No. VMI spec is a Kubernetes declarative model, and libvirt XML is a hypervisor execution model. There are many policies between them that KubeVirt must interpret.
Misconception 2: Disks Just Need the Volume Path to Be Correct
No. Guest-visible attributes like bus, boot order, discard, cache, reservation, and block size are all important.
Misconception 3: The Converter Is Only Important Once at Launch
No. During migration, hotplug, and target preparation, domain spec and XML transformations become important again.
Conclusion
KubeVirt's converter is the most important intermediate layer that translates VMI spec into the libvirt domain model. This layer is not simple field copying but reflects guest hardware model, execution environment, security options, and migration constraints together. So to deeply understand KubeVirt, the package you must read right after the controller is pkg/virt-launcher/virtwrap/converter.
In the next article, we will look at how these conversion results meet actual storage sources, and through what paths PVCs, DataVolumes, container disks, and hotplug disks attach to the guest.