- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 들어가며
- 왜 객체를 셋으로 나누는가
- VirtualMachine은 "지속적인 의도"다
- VirtualMachineInstance는 실행 중 guest의 실체다
- VMI status는 왜 이렇게 많은가
- VirtualMachineInstanceMigration은 "작업 요청"이다
- 객체 관계를 한 그림으로 이해하기
- schema.go에서 보이는 설계 철학
- 왜 VM과 VMI를 합치지 않았을까
- 운영자가 status에서 먼저 봐야 할 것
- 자주 하는 오해
- 마무리
들어가며
KubeVirt의 구조를 따라가다 보면 가장 먼저 만나는 타입이 VirtualMachine, VirtualMachineInstance, VirtualMachineInstanceMigration이다. 이름이 비슷해서 처음에는 헷갈리지만, 이 세 객체를 구분하지 못하면 이후 controller, migration, 상태 추적 로직도 전부 흐릿해진다.
이번 글은 staging/src/kubevirt.io/api/core/v1/types.go와 schema.go를 중심으로, 각 객체가 의도, 실행 중 인스턴스, 일회성 이동 요청 중 무엇을 나타내는지 정리한다.
왜 객체를 셋으로 나누는가
Kubernetes의 기본 패턴을 떠올리면 이해가 쉽다.
- Deployment는 "원하는 상태"를 표현한다.
- Pod는 "현재 실행 중인 인스턴스"를 표현한다.
- Job는 "한 번 수행할 작업"을 표현한다.
KubeVirt도 비슷한 분리를 택한다.
VirtualMachine: 장기적인 VM 의도와 운영 정책VirtualMachineInstance: 지금 살아 있는 guest 인스턴스VirtualMachineInstanceMigration: 특정 VMI를 다른 노드로 옮기려는 작업 객체
이 분리가 있어야 start, stop, restart, migration, eviction, status 반영이 서로 꼬이지 않는다.
VirtualMachine은 "지속적인 의도"다
VirtualMachine은 "이 VM을 어떤 형태로 유지하고 싶은가"를 표현하는 상위 객체다. 사용자는 보통 여기에 template, run strategy, data volume 연결 정책을 선언한다.
핵심 포인트는 VirtualMachine이 곧바로 guest 프로세스 자체를 뜻하지 않는다는 점이다. 실제 실행 주체는 VirtualMachineInstance다.
언제 VirtualMachine이 유용한가
- VM을 껐다 켜도 동일한 의도를 유지하고 싶을 때
- 운영 정책을 상태와 분리하고 싶을 때
- start 또는 stop 요청을 선언적으로 관리하고 싶을 때
즉 VirtualMachine은 "이 VM은 존재해야 하는가"를 다루고, VirtualMachineInstance는 "지금 현재 어디서 어떻게 실행 중인가"를 다룬다.
VirtualMachineInstance는 실행 중 guest의 실체다
VirtualMachineInstance, 줄여서 VMI는 KubeVirt의 핵심 런타임 객체다. types.go에서 이 객체는 spec와 status를 함께 가지는 runtime definition으로 설명된다.
실제로 controller와 node agent가 가장 자주 관찰하는 객체도 VMI다. 이유는 간단하다. Pod 생성, node assignment, network status, migration state, guest agent 정보 같은 실행 중 상태의 중심이 VMI이기 때문이다.
VMI spec에서 중요한 필드
domain: CPU, 메모리, 디스크, NIC 같은 guest 하드웨어 요구nodeSelector,affinity,tolerations: 어느 노드에 갈 수 있는가volumes: 어떤 스토리지를 guest에 붙일 것인가networks: 어떤 네트워크를 guest NIC에 연결할 것인가terminationGracePeriodSeconds: 종료 시그널과 강제 종료 사이의 유예 시간
즉 VMI spec는 "이 guest를 지금 실행하려면 런타임이 무엇을 준비해야 하는가"를 나타낸다.
VMI status는 왜 이렇게 많은가
VirtualMachineInstanceStatus는 생각보다 풍부하다. 이게 중요한 이유는 KubeVirt가 하나의 monolithic daemon이 아니라 여러 controller와 node component가 choreography 방식으로 협력하기 때문이다. 각 컴포넌트는 status를 보고 다음 행동을 결정한다.
대표적으로 중요한 필드는 다음과 같다.
NodeName: 현재 VMI가 어느 노드에서 실행 중인가Phase: Pending, Scheduling, Running 같은 상위 상태Conditions: paused, agent connected, migratable 같은 세부 플래그Interfaces: guest와 Pod 인터페이스 매핑 상태MigrationState: migration 진행 상황MigrationMethod: live migration인지 block migration인지MigrationTransport: 어떤 transport를 사용할지ActivePods: 하나의 VMI에 대해 어떤 launcher Pod들이 살아 있는지
특히 ActivePods는 migration 시기에 중요하다. 평소에는 VM 하나에 Pod 하나처럼 보이지만, migration 중에는 source Pod와 target Pod가 잠시 공존할 수 있다.
VirtualMachineInstanceMigration은 "작업 요청"이다
많은 사용자가 migration을 VMI status 안의 버튼처럼 생각하지만, KubeVirt는 별도 CRD를 사용한다. VirtualMachineInstanceMigration 객체를 생성하면 controller가 그것을 보고 migration orchestration을 시작한다.
이 설계의 장점은 명확하다.
- migration을 audit 가능한 독립 객체로 추적할 수 있다
- policy 적용이 쉬워진다
- 상태 전이를 별도 lifecycle로 모델링할 수 있다
- 실패 이유를 작업 단위로 남길 수 있다
pkg/virt-api/rest/lifecycle.go의 migrate handler를 보면, 실제로 migrate 요청은 내부에서 VirtualMachineInstanceMigration 객체 생성으로 번역된다. 즉 사용자의 "migrate" 버튼 클릭은 결국 "migration CR 생성"으로 바뀐다.
객체 관계를 한 그림으로 이해하기
VirtualMachine
-> template/spec/policy를 가진 장기 객체
-> 실행이 필요할 때 VMI를 유도
VirtualMachineInstance
-> 현재 실행 중인 guest 인스턴스
-> Pod, node, network, storage, migration 상태의 중심
VirtualMachineInstanceMigration
-> 특정 VMI를 옮기기 위한 일회성 작업 객체
-> controller가 target Pod 생성과 handoff를 시작
이 구조는 "desired state", "runtime instance", "operation request"를 섞지 않으려는 의도라고 보면 된다.
schema.go에서 보이는 설계 철학
schema.go를 보면 VMI spec 안에 네트워크와 볼륨, CPU, memory, firmware, launch security, access credentials 같은 필드가 매우 풍부하게 정의되어 있다. 이는 KubeVirt가 단순히 "QEMU 실행 wrapper"가 아니라 guest 하드웨어와 운영 환경을 Kubernetes API 형태로 선언 가능하게 만든 계층이라는 뜻이다.
반대로 status 쪽은 orchestration 친화적으로 설계되어 있다. 예를 들어 network interface status, guest OS info, migration state는 guest 내부 정보와 cluster orchestration 상태를 함께 담는다. 이 점이 컨테이너 Pod status와 다른 결이다.
왜 VM과 VMI를 합치지 않았을까
이 질문은 중요하다. 만약 하나의 객체에 모두 넣었다면 구현은 단순해 보일 수 있다. 하지만 실제 운영에서는 다음 문제가 생긴다.
1. 껐다 켜도 같은 "의도"를 유지하기 어렵다
실행 중 인스턴스 상태와 사용자의 영속적 설정이 한 객체에 섞이면 lifecycle이 복잡해진다.
2. status churn이 너무 커진다
런타임 상태는 자주 바뀌지만, 의도는 자주 바뀌지 않는다. 둘을 분리해야 watch 비용과 reconcile 책임이 정리된다.
3. migration 같은 작업을 모델링하기 어렵다
별도 작업 객체가 있어야 정책, 큐잉, 우선순위, 실패 이유를 깔끔하게 다룰 수 있다.
운영자가 status에서 먼저 봐야 할 것
실무에서는 이 세 객체를 모두 보지만, 상황별로 우선순위가 다르다.
VM이 켜지지 않을 때
VirtualMachine의 run strategy- VMI가 생성되었는지
- VMI phase가 어디까지 갔는지
네트워크나 부팅이 이상할 때
- VMI status의
Interfaces ConditionsActivePods
migration이 꼬였을 때
VirtualMachineInstanceMigrationstatus- VMI status의
MigrationState - source Pod와 target Pod 상태
자주 하는 오해
오해 1: VM과 VMI는 이름만 다르고 사실상 같다
아니다. 하나는 의도, 하나는 런타임 실체다. 이 차이를 이해해야 start 또는 stop, restart, migration, drain 로직이 보인다.
오해 2: migration은 VMI 필드 하나만 바꾸면 시작된다
아니다. 별도의 migration CR이 생성되고, controller가 그 객체를 작업 단위로 소비한다.
오해 3: status는 단순 UI 용도다
아니다. KubeVirt 내부 컴포넌트들이 서로 협력하는 중요한 계약면이다.
마무리
KubeVirt의 객체 모델은 우연히 복잡한 것이 아니다. VirtualMachine은 지속적인 의도, VirtualMachineInstance는 실행 중 인스턴스, VirtualMachineInstanceMigration은 일회성 이동 요청을 표현한다. 이 분리가 있기 때문에 KubeVirt는 Kubernetes 스타일의 선언적 API를 유지하면서도, VM 실행과 이동처럼 상태가 많은 작업을 안정적으로 처리할 수 있다.
다음 글에서는 이 객체들이 실제 API 진입점에서 어떻게 다뤄지는지, 그리고 virt-api가 어떤 검증과 subresource 동작을 수행하는지 살펴보겠다.