Split View: Eviction, drain, migration failure modes: KubeVirt는 실패를 어떻게 처리하는가
Eviction, drain, migration failure modes: KubeVirt는 실패를 어떻게 처리하는가
- 들어가며
- 1. KubeVirt는 drain을 곧바로 kill로 처리하지 않는다
- 2. EvictionRequested와 EvacuationNodeName은 운영자가 꼭 봐야 한다
- 3. live migration 불가 사유를 미리 reason으로 남긴다
- 4. MigrationState는 실패 분석의 중심축이다
- 5. pre-copy는 실패해도 복구 여지가 있지만, post-copy는 더 위험하다
- 6. abort도 하나의 상태 머신이다
- 7. migration failure는 control plane과 data plane 둘 다에서 생긴다
- 8. secure feature는 종종 migration flexibility와 충돌한다
- 9. migration 중 다중 Pod 존재는 실패 분석을 더 어렵게 만든다
- 10. drain 운영 전략은 워크로드 특성에 따라 달라져야 한다
- 운영자가 기억해야 할 핵심
- 마무리
들어가며
운영 관점에서 KubeVirt의 진짜 난도는 VM을 띄우는 순간보다, 실패를 다루는 순간에 드러난다. Pod는 다시 스케줄링하면 끝나는 일이 많지만, VM은 메모리 상태와 디스크, 네트워크 세션, guest 실행 문맥까지 함께 고려해야 한다. 그래서 drain과 eviction, migration 실패는 KubeVirt 내부 설계를 가장 잘 드러내는 장면이다.
이번 글에서는 "무엇이 실패라고 간주되는가"와 "실패를 KubeVirt가 어떻게 표준화해서 드러내는가"를 본다.
1. KubeVirt는 drain을 곧바로 kill로 처리하지 않는다
API 타입의 EvictionStrategy 주석을 보면 node drain 상황에서 어떤 전략을 쓸지 정의돼 있다. 즉 KubeVirt는 drain을 단순 Kubernetes eviction 이벤트로 보지 않고, VM 전용 정책 판단이 필요한 사건으로 취급한다.
그 이유는 명확하다.
- migratable이면 먼저 옮기는 편이 낫다.
- non-migratable이면 중단이나 보류가 필요할 수 있다.
- 외부 컨트롤러가 소유한 VM이면 다른 처리 모델이 필요할 수 있다.
즉 KubeVirt에서 drain은 "Pod 하나 비우기"가 아니라 "이 VM을 어느 방식으로 대피시킬 것인가"라는 질문이다.
2. EvictionRequested와 EvacuationNodeName은 운영자가 꼭 봐야 한다
VirtualMachineInstanceStatus에는 EvacuationNodeName이 있고, condition 타입에는 EvictionRequested가 정의돼 있다. 이는 eviction이 단순 이벤트 로그에만 남는 것이 아니라, VMI status에 구조화된 신호로 남는다는 뜻이다.
운영자가 이 값을 봐야 하는 이유는 다음과 같다.
- drain이 시작됐는지
- 어떤 node에서 evacuate 하려는지
- 이후 migration이 붙어야 하는지
- 외부 컨트롤러가 후속 조치를 해야 하는지
즉 KubeVirt는 drain을 status-first 방식으로 드러낸다.
3. live migration 불가 사유를 미리 reason으로 남긴다
KubeVirt API 타입에는 non-migratable reason이 매우 많이 정의돼 있다.
DisksNotLiveMigratableInterfaceNotLiveMigratableHotplugNotLiveMigratableVirtIOFSNotLiveMigratableHostDeviceNotLiveMigratableSEVNotLiveMigratableSecureExecutionNotLiveMigratableTDXNotLiveMigratableHypervPassthroughNotLiveMigratablePersistentReservationNotLiveMigratable
이 설계가 좋은 이유는 실패를 뒤늦게 runtime error로만 보지 않기 때문이다. KubeVirt는 미리 condition reason으로 "왜 이 VM은 옮길 수 없는가"를 말해 준다.
즉 운영자는 migration을 걸고 실패 로그를 기다리기 전에, API 상태만 보고도 대략적인 한계를 읽을 수 있다.
4. MigrationState는 실패 분석의 중심축이다
VirtualMachineInstanceMigrationState에는 실패 분석에 필요한 정보가 많이 들어 있다.
- source node와 source pod
- target node와 target pod
- sync address
- direct migration ports
- completed 여부
- failed 여부
- abort requested 여부
- abort status
- failure reason
- 현재 migration mode
이 구조를 보면 KubeVirt가 migration을 단순 boolean 상태로 보지 않는다는 점이 드러난다. migration은 시간에 따라 source와 target이 바뀌는 분산 프로토콜이고, 실패도 여러 단계에서 발생한다.
5. pre-copy는 실패해도 복구 여지가 있지만, post-copy는 더 위험하다
앞선 글에서 봤듯 pre-copy는 source에 원본 메모리가 남아 있는 상태에서 점진적으로 전송한다. 반면 post-copy는 target이 먼저 실행을 시작하고, 필요한 페이지를 뒤늦게 source에서 받아 온다.
그래서 post-copy 실패는 훨씬 위험하다. pkg/virt-handler/vm.go에는 formatIrrecoverableErrorMessage가 있고, post-copy 실패로 domain이 paused 상태가 되면 "VMI is irrecoverable due to failed post-copy migration" 메시지를 만든다.
이건 매우 강한 신호다. 단순히 migration job이 실패한 것이 아니라, 실행 중이던 VM 상태 자체가 복구 불가능한 쪽으로 무너질 수 있음을 뜻한다.
즉 post-copy는 바쁜 워크로드를 옮기기 위한 강력한 도구지만, 실패 비용도 더 크다.
6. abort도 하나의 상태 머신이다
VirtualMachineInstanceMigrationState에는 AbortRequested와 AbortStatus가 있고, abort 상태도 Succeeded, Failed, Aborting으로 따로 모델링된다.
이 설계는 현실적이다. migration 중단은 버튼 하나 누른다고 즉시 끝나는 일이 아니다.
- 아직 중단 가능한 단계인지
- 이미 target이 handoff를 받았는지
- storage나 network side effect가 얼마나 진행됐는지
에 따라 결과가 달라진다. 즉 KubeVirt는 abort도 일반 API 취소가 아니라, 별도 상태 머신으로 본다.
7. migration failure는 control plane과 data plane 둘 다에서 생긴다
실패 원인을 크게 나누면 두 가지다.
control plane 쪽 실패
- target Pod가 스케줄되지 않음
- 적절한 node selector를 만족하지 못함
- quota나 policy에 걸림
- utility volume이나 backup 때문에 block됨
data plane 쪽 실패
- dirty page 비율이 너무 높아 pre-copy가 수렴하지 않음
- post-copy 전환 이후 source와 target 동기화가 깨짐
- migration socket 또는 proxy 경로 문제
- target에서 domain 준비가 늦음
즉 "migration failed" 한 줄만으로는 부족하고, 어느 plane에서 실패했는지 나눠 봐야 한다.
8. secure feature는 종종 migration flexibility와 충돌한다
API 수준에서 이미 드러나듯 SEV, Secure Execution, TDX, host device passthrough 같은 기능은 migration 제약과 자주 충돌한다.
이는 우연이 아니다. 이런 기능은 대체로 다음 중 하나를 요구한다.
- 특정 host 하드웨어와 강하게 결합
- guest 메모리를 특수 방식으로 보호
- 외부에서 재현하기 어려운 장치 상태 사용
즉 보안을 강화하거나 하드웨어 성능을 극대화할수록, "아무 node로나 무중단 이동"이라는 특성과 충돌하기 쉬워진다.
9. migration 중 다중 Pod 존재는 실패 분석을 더 어렵게 만든다
ActivePods가 왜 중요한지 실패 모드에서 더 잘 드러난다. migration 중에는 source와 target launcher Pod가 잠깐 공존하므로, 로그와 상태를 볼 때 어떤 Pod가 진짜 현재 source인지 헷갈리기 쉽다.
실패를 볼 때는 최소한 다음을 같이 봐야 한다.
- VMI의
activePods - migration CR phase
- target pod 이름
- source pod 이름
- VMI
migrationState
이 정보를 섞지 않으면, 이미 정리된 Pod 로그를 보고 현재 문제라고 오해하기 쉽다.
10. drain 운영 전략은 워크로드 특성에 따라 달라져야 한다
모든 VM에 같은 eviction strategy를 적용하면 안 된다. 예를 들어:
- stateless에 가까운 테스트 VM
- SR-IOV와 host device를 쓰는 성능 민감 VM
- RWX 볼륨과 라이브 마이그레이션이 가능한 일반 업무 VM
- post-copy 허용 여부가 중요한 메모리 write-intensive VM
은 실패 비용과 허용 가능한 조치가 전혀 다르다.
즉 drain 전략은 infra 기본값이 아니라, VM 특성에 맞는 운영 정책이어야 한다.
운영자가 기억해야 할 핵심
- drain은 Pod 제거가 아니라 VM 대피 전략 문제다.
EvictionRequested,EvacuationNodeName,MigrationState를 같이 봐야 한다.- non-migratable reason은 실패 후 로그가 아니라, 사전 판단 신호다.
- post-copy 실패는 irrecoverable 상태로 이어질 수 있으므로 훨씬 조심해야 한다.
마무리
KubeVirt는 실패를 숨기지 않고 상태 머신으로 구조화한다. drain은 eviction 전략과 연결되고, live migration 가능 여부는 condition reason으로 미리 드러나며, 실제 migration 진행 상황과 실패 사유는 MigrationState에 축적된다. 특히 post-copy 실패를 irrecoverable로 분리한 점은, KubeVirt가 VM 실패를 단순 Pod 재시작 문제와 다르게 본다는 사실을 잘 보여 준다.
다음 글에서는 시리즈를 마무리하면서, KubeVirt 소스코드를 어떤 순서로 읽으면 이 전체 구조를 가장 빠르게 이해할 수 있는지 소스 읽기 지도를 정리하겠다.
Eviction, Drain, Migration Failure Modes: How KubeVirt Handles Failures
- Introduction
- 1. KubeVirt Does Not Immediately Kill on Drain
- 2. Operators Must Check EvictionRequested and EvacuationNodeName
- 3. Non-Migratable Reasons Are Recorded in Advance
- 4. MigrationState Is the Central Axis for Failure Analysis
- 5. Pre-copy Failures Have Recovery Potential, but Post-copy Is More Dangerous
- 6. Abort Is Also a State Machine
- 7. Migration Failures Occur in Both the Control Plane and Data Plane
- 8. Secure Features Often Conflict with Migration Flexibility
- 9. Multiple Pod Existence During Migration Makes Failure Analysis Harder
- 10. Drain Strategy Should Vary Based on Workload Characteristics
- Key Points for Operators
- Conclusion
Introduction
From an operational perspective, the real difficulty of KubeVirt reveals itself not when launching a VM, but when handling failures. Pods can often be resolved by simply rescheduling, but VMs require considering memory state, disks, network sessions, and guest execution context together. That is why drain, eviction, and migration failures are the scenes that best expose KubeVirt's internal design.
In this post, we examine "what is considered a failure" and "how KubeVirt standardizes and exposes failures."
1. KubeVirt Does Not Immediately Kill on Drain
Looking at the EvictionStrategy annotation in the API types, it defines what strategy to use during node drain situations. KubeVirt does not treat drain as a simple Kubernetes eviction event but as an event requiring VM-specific policy decisions.
The reason is clear.
- If migratable, it is better to move the VM first.
- If non-migratable, suspension or deferral may be needed.
- If the VM is owned by an external controller, a different handling model may be required.
In other words, drain in KubeVirt is not "emptying one Pod" but the question "how should this VM be evacuated?"
2. Operators Must Check EvictionRequested and EvacuationNodeName
VirtualMachineInstanceStatus has EvacuationNodeName, and EvictionRequested is defined among condition types. This means eviction is not just left in event logs but remains as a structured signal in the VMI status.
Operators need to check these values for the following reasons:
- Whether drain has started
- Which node is being evacuated from
- Whether a migration should follow
- Whether an external controller needs to take follow-up action
In other words, KubeVirt exposes drain in a status-first manner.
3. Non-Migratable Reasons Are Recorded in Advance
The KubeVirt API types define a large number of non-migratable reasons.
DisksNotLiveMigratableInterfaceNotLiveMigratableHotplugNotLiveMigratableVirtIOFSNotLiveMigratableHostDeviceNotLiveMigratableSEVNotLiveMigratableSecureExecutionNotLiveMigratableTDXNotLiveMigratableHypervPassthroughNotLiveMigratablePersistentReservationNotLiveMigratable
This design is good because it does not just surface failures as runtime errors after the fact. KubeVirt tells you in advance via condition reasons "why this VM cannot be migrated."
Operators can read the approximate limitations from the API status alone, before triggering a migration and waiting for failure logs.
4. MigrationState Is the Central Axis for Failure Analysis
VirtualMachineInstanceMigrationState contains a wealth of information needed for failure analysis.
- Source node and source pod
- Target node and target pod
- Sync address
- Direct migration ports
- Completed status
- Failed status
- Abort requested status
- Abort status
- Failure reason
- Current migration mode
This structure reveals that KubeVirt does not view migration as a simple boolean state. Migration is a distributed protocol where source and target change over time, and failures can occur at multiple stages.
5. Pre-copy Failures Have Recovery Potential, but Post-copy Is More Dangerous
As seen in the previous post, pre-copy progressively transfers data while the source retains the original memory. In contrast, post-copy starts execution on the target first and retrieves needed pages from the source later.
Therefore, post-copy failures are much more dangerous. In pkg/virt-handler/vm.go, there is formatIrrecoverableErrorMessage, and when a post-copy failure causes the domain to enter a paused state, the message "VMI is irrecoverable due to failed post-copy migration" is generated.
This is a very strong signal. It does not just mean the migration job failed -- it means the running VM state itself may have collapsed into an unrecoverable state.
In other words, post-copy is a powerful tool for moving busy workloads, but the cost of failure is also greater.
6. Abort Is Also a State Machine
VirtualMachineInstanceMigrationState has AbortRequested and AbortStatus, and the abort state is separately modeled as Succeeded, Failed, and Aborting.
This design is realistic. Migration abort does not end instantly just by pressing a button.
- Whether the abort is still possible at this stage
- Whether the target has already received the handoff
- How far storage or network side effects have progressed
These factors affect the outcome. KubeVirt treats abort not as a simple API cancellation but as a separate state machine.
7. Migration Failures Occur in Both the Control Plane and Data Plane
Failure causes can be broadly divided into two categories.
Control plane failures
- Target Pod fails to schedule
- Cannot satisfy the appropriate node selector
- Blocked by quota or policy
- Blocked due to utility volumes or backups
Data plane failures
- Dirty page rate is too high for pre-copy to converge
- Source-target synchronization breaks after post-copy transition
- Migration socket or proxy path issues
- Domain preparation on the target is delayed
In other words, a single "migration failed" message is insufficient -- you need to distinguish which plane the failure occurred in.
8. Secure Features Often Conflict with Migration Flexibility
As already apparent at the API level, features like SEV, Secure Execution, TDX, and host device passthrough frequently conflict with migration constraints.
This is not coincidental. These features generally require one of the following:
- Strong coupling with specific host hardware
- Special protection of guest memory
- Use of device state that is difficult to reproduce externally
In other words, the more you strengthen security or maximize hardware performance, the easier it becomes to conflict with the characteristic of "zero-downtime movement to any node."
9. Multiple Pod Existence During Migration Makes Failure Analysis Harder
Why ActivePods is important becomes clearer in failure modes. During migration, the source and target launcher Pods briefly coexist, making it easy to confuse which Pod is the actual current source when examining logs and status.
When analyzing failures, you should look at least the following together:
- VMI's
activePods - Migration CR phase
- Target pod name
- Source pod name
- VMI
migrationState
Without cross-referencing this information, it is easy to mistake logs from an already cleaned up Pod as the current problem.
10. Drain Strategy Should Vary Based on Workload Characteristics
The same eviction strategy should not be applied to all VMs. For example:
- Nearly stateless test VMs
- Performance-sensitive VMs using SR-IOV and host devices
- General workload VMs with RWX volumes and live migration capability
- Memory write-intensive VMs where post-copy allowance is important
These have entirely different failure costs and acceptable responses.
In other words, drain strategy should not be an infrastructure default but an operational policy tailored to VM characteristics.
Key Points for Operators
- Drain is a VM evacuation strategy issue, not Pod removal.
- You must look at
EvictionRequested,EvacuationNodeName, andMigrationStatetogether. - Non-migratable reasons are pre-judgment signals, not post-failure logs.
- Post-copy failures can lead to irrecoverable states and require much greater caution.
Conclusion
KubeVirt does not hide failures but structures them as state machines. Drain is connected to eviction strategies, live migration feasibility is revealed in advance via condition reasons, and actual migration progress and failure reasons accumulate in MigrationState. The fact that post-copy failures are separated as irrecoverable demonstrates that KubeVirt views VM failures differently from simple Pod restart issues.
In the next post, we will wrap up the series by organizing a source code reading map that shows the order in which to read the KubeVirt source code to understand the entire structure most quickly.