Skip to content
Published on

Live Migration 1: migration CRD에서 target Pod 생성까지

Authors

들어가며

Live migration을 이해할 때 많은 사람이 곧바로 pre-copy나 dirty page로 들어간다. 하지만 그 전에 더 중요한 질문이 있다. 누가 target Pod를 만들고, 언제 source VM에 handoff를 걸며, 어떤 정책으로 migration 시작을 허용하는가? 이건 data plane이 아니라 control plane 문제다.

이번 글은 pkg/virt-controller/watch/migration/migration.go를 중심으로, migration CR이 생긴 뒤 target Pod가 준비되기까지의 orchestration을 본다.

migration은 API 액션이 아니라 작업 객체다

앞 글에서 보았듯, 사용자가 migrate를 요청하면 virt-api는 직접 옮기지 않는다. 대신 VirtualMachineInstanceMigration 객체를 만든다.

그 다음부터는 virt-controller의 migration controller가 이 객체를 감시한다. 이 설계의 장점은 명확하다.

  • 작업이 독립적으로 추적된다
  • controller가 비동기적으로 처리한다
  • 실패, pending, abort를 별도 lifecycle로 다룰 수 있다

즉 migration은 "함수 호출"이 아니라 Kubernetes 오브젝트 기반 orchestration이다.

migration controller가 보는 것

NewController 시그니처를 보면 migration controller는 꽤 많은 informer와 store를 가진다.

  • VMI informer
  • Pod informer
  • migration informer
  • node store
  • PVC store
  • storage class store
  • storage profile store
  • migration policy store
  • resource quota informer
  • KubeVirt CR store

왜 이렇게 많을까? live migration은 단순히 source와 target만 맞으면 되는 작업이 아니기 때문이다.

  • target Pod가 스케줄 가능한가
  • storage가 target에서도 보이는가
  • cluster 차원의 concurrent migration 제한은 어떤가
  • migration policy가 어떤 timeout과 bandwidth를 요구하는가
  • resource quota가 target Pod 생성을 막지 않는가

즉 migration controller는 정책과 용량의 중앙 판단기다.

priority queue를 쓰는 이유

흥미로운 점은 migration controller가 일반 workqueue가 아니라 priority queue를 쓴다는 것이다. 주석을 보면 active migration에 더 높은 우선순위를 주고, capacity 때문에 대기 중인 migration이 active migration 처리까지 지연시키지 않게 하려는 의도가 보인다.

이건 중요한 설계 포인트다. migration은 한번 시작되면 지속적으로 상태를 추적해야 하므로, pending migration과 active migration을 똑같이 큐잉하면 좋지 않다.

target Pod는 어떻게 만들어지는가

migration controller 역시 직접 raw Pod spec를 조립하지 않고 template service를 사용한다. 인터페이스에 RenderMigrationManifest가 있는 것을 보면, migration target Pod는 일반 launcher Pod와 닮았지만 별도의 렌더링 경로를 가진다.

즉 controller 흐름은 다음과 같다.

  1. migration CR 감지
  2. 해당 VMI의 migration 가능성 검토
  3. target Pod가 필요하면 migration용 launcher manifest 렌더링
  4. target Pod 생성
  5. source와 target 상태를 추적하며 handoff

즉 migration의 첫 단계는 "메모리를 복사한다"가 아니라 새 launcher Pod를 올릴 수 있느냐다.

왜 timeout이 এত 많을까

migration.go에는 여러 timeout 상수가 보인다.

  • unschedulable pending timeout
  • catch-all pending timeout

이건 target Pod가 단순히 늦는 것과, 사실상 불가능한 상태를 구분하기 위해서다.

예를 들어:

  • 노드가 잠깐 부족한 경우
  • 특정 resource가 영원히 맞지 않는 경우
  • PVC attach가 오래 걸리는 경우

이들을 모두 같은 실패로 처리하면 운영자가 원인을 파악하기 어렵다. 그래서 controller는 pending의 의미를 세분화한다.

migration policy와 cluster config는 어디서 반영되는가

migrationPolicyStore, clusterConfig가 controller에 들어 있는 이유는 migration이 VMI마다 같은 정책으로 돌지 않기 때문이다.

대표적으로 정책은 다음에 영향을 준다.

  • 동시 migration 개수
  • node별 outbound migration 개수
  • bandwidth 제한
  • progress timeout
  • completion timeout
  • post-copy 허용 여부
  • TLS 비활성화 여부

즉 "migration을 한다"는 말 뒤에는 항상 정책 레이어가 숨어 있다.

storage와 quota가 migration control plane에 들어오는 이유

운영자 입장에서는 network와 CPU만 생각하기 쉽지만, target Pod를 실제로 띄우려면 storage와 quota도 중요하다.

storage

target node에서 필요한 volume이 준비되지 않으면 migration은 시작도 못 한다.

quota

target launcher Pod를 새로 생성해야 하므로 namespace resource quota에 걸릴 수 있다.

즉 migration control plane은 guest memory copying보다 훨씬 더 Kubernetes스러운 문제들을 먼저 해결해야 한다.

handoff는 언제 일어나는가

migration.go에는 handOffMap 같은 구조도 보인다. 이는 source 쪽에서 virt-handler로 넘기는 순간을 관리하기 위한 장치다. 쉽게 말하면 controller는 영원히 모든 것을 붙잡고 있지 않는다. 일정 단계가 지나면 node-local execution plane이 더 큰 역할을 맡는다.

이 분리가 필요한 이유는 다음과 같다.

  • controller는 cluster-wide 상태 판단에 강하다
  • 실제 migration 수행은 source와 target 노드의 virt-handler와 launcher가 더 잘 안다

즉 live migration은 control plane과 execution plane이 단계적으로 책임을 나누는 구조다.

자동 migration 요구와 동적 네트워크 변화

pkg/network/migration/evaluator.go는 또 다른 흥미로운 사실을 보여준다. secondary network hotplug나 unplug, NAD 변경은 migration required condition을 만들 수 있다.

이 말은 migration이 꼭 사용자가 "지금 옮겨"라고 해서만 일어나는 것이 아니라, 현재 Pod에서 안전하게 반영하기 어려운 네트워크 변화가 있을 때 재배치 수단으로도 사용될 수 있다는 뜻이다.

즉 migration control plane은 maintenance 기능이면서 configuration convergence 메커니즘이기도 하다.

자주 하는 오해

오해 1: migration은 source QEMU가 target QEMU와 직접 이야기하면 끝난다

아니다. 그 전에 migration CR, target Pod 생성, 스케줄링, 정책, quota, storage 준비가 있다.

오해 2: migration target Pod는 그냥 기존 Pod를 재사용한다

아니다. 별도의 target launcher Pod가 새로 만들어질 수 있다.

오해 3: migration 실패는 거의 data plane 문제다

아니다. 실제 운영에서는 control plane에서 pending, unschedulable, quota, policy mismatch로 막히는 경우도 많다.

운영자가 먼저 확인할 것

  • migration CR이 생성되었는가
  • target Pod가 생겼는가
  • target Pod가 pending인지 running인지
  • migration policy와 cluster-wide concurrency 제한은 어떤가
  • namespace quota와 storage 가시성은 괜찮은가

이 단계가 통과되어야 다음 글의 pre-copy와 post-copy가 의미를 갖는다.

마무리

KubeVirt live migration의 control plane은 migration CR 생성에서 시작해, virt-controller가 policy, quota, storage, scheduling 상태를 보고 target Pod를 준비하는 과정으로 이어진다. 즉 migration은 libvirt data transfer 이전에 이미 Kubernetes orchestration 문제다. 이 관점을 잡아야 실제 운영에서 왜 migration이 시작도 못 하고 pending에서 멈추는지 이해할 수 있다.

다음 글에서는 이 control plane 준비가 끝난 뒤 실제 메모리와 디스크 상태를 옮기는 pre-copy, post-copy, dirty page 모델을 설명하겠다.