Skip to content
Published on

VMI spec가 libvirt domain XML로 변환되는 과정

Authors

들어가며

사용자는 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 항목으로 그냥 복사될 것 같지만 실제로는 훨씬 복잡하다.

이유는 세 가지다.

  1. guest 장치 이름을 계산해야 한다
  2. Pod에 준비된 실제 디스크 경로를 찾아야 한다
  3. architecture, virtio 모델, security, migration 같은 정책을 반영해야 한다

즉 converter는 serialization layer가 아니라 정책 포함 변환기다.

핵심 진입점

converter.go에서 가장 중요한 함수 이름은 다음과 같다.

  • Convert_v1_VirtualMachineInstance_To_api_Domain
  • Convert_v1_Disk_To_api_Disk
  • Convert_v1_Volume_To_api_Disk
  • Convert_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에는 volumesdisks가 분리되어 있다.

  • 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은 언제 완성되는가

실무적으로는 다음 순서로 이해하면 된다.

  1. VMI spec를 읽는다.
  2. converter가 api.Domain을 만든다.
  3. 필요하면 hook이나 sidecar가 domain spec를 추가 수정한다.
  4. 최종 domain XML이 libvirt에 전달된다.
  5. 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에 어떤 경로로 붙는지 살펴보겠다.