Split View: Multus와 secondary network는 KubeVirt에서 어떻게 연결되는가
Multus와 secondary network는 KubeVirt에서 어떻게 연결되는가
- 들어가며
- 기본 아이디어
- 핵심 코드: pkg/network/multus/annotation.go
- interface 이름 계산이 왜 중요한가
- annotation에는 무엇이 들어가는가
- Pod가 secondary network를 받은 뒤에는 무엇이 일어나는가
- InfoSource가 왜 중요한가
- secondary network 변경이 migration을 유발할 수도 있다
- Multus와 binding plugin의 관계
- 실무에서 자주 생기는 문제
- 자주 하는 오해
- 마무리
들어가며
primary Pod network만으로는 부족한 경우가 많다. 데이터 plane, 스토리지 plane, 관리망, NFV 용도처럼 VM에 여러 NIC가 필요할 수 있다. KubeVirt는 이 문제를 자체 multi-network 시스템으로 풀지 않고, Multus와 NetworkAttachmentDefinition 생태계를 재사용한다.
이번 글은 secondary network가 VMI spec에서 Pod annotation, Pod interface, VMI status로 이어지는 경로를 정리한다.
기본 아이디어
primary network는 보통 Pod 기본 CNI가 제공한다. secondary network는 Multus가 추가로 붙인다. KubeVirt는 VMI spec를 읽고 필요한 Multus annotation을 생성해 launcher Pod에 싣는다.
즉 흐름은 다음과 같다.
- 사용자가 VMI spec에 secondary network를 적는다.
- KubeVirt가 Pod annotation을 생성한다.
- Multus가 annotation을 읽고 해당 NAD에 따라 추가 인터페이스를 붙인다.
- KubeVirt가 Pod network status를 읽어 VMI status를 갱신한다.
이 구조 덕분에 KubeVirt는 multi-network 기능을 core에서 다시 만들지 않는다.
핵심 코드: pkg/network/multus/annotation.go
이 파일의 GenerateCNIAnnotation과 GenerateCNIAnnotationFromNameScheme이 핵심이다. 역할은 아주 명확하다.
- secondary Multus network를 찾는다
- Pod interface 이름을 계산한다
NetworkSelectionElement목록을 만든다- 이를 JSON annotation 문자열로 직렬화한다
즉 controller는 단순히 "NAD 이름이 있다"에서 끝나지 않고, Pod 안에서 어떤 이름의 인터페이스로 붙어야 하는지까지 계산한다.
interface 이름 계산이 왜 중요한가
KubeVirt는 logical network 이름과 Pod interface 이름을 동일하게 쓰지 않는다. namescheme 계층을 통해 hashed name을 만든다. 이 방식의 목적은:
- 인터페이스 이름 길이 제한 대응
- 예측 가능한 naming
- secondary network가 많아져도 충돌 최소화
network-binding-plugin.md도 secondary network 쪽에서 pod<hash>나 tap<hash> 같은 이름 규칙을 설명한다.
즉 이름 규칙은 단순 미관 문제가 아니라, launcher Pod와 CNI plugin, libvirt domain이 서로 같은 인터페이스를 가리키게 만드는 계약이다.
annotation에는 무엇이 들어가는가
Multus용 NetworkSelectionElement에는 보통 다음 정보가 들어간다.
- namespace
- NAD name
- interface request
- 필요 시 MAC request
binding plugin이 붙는 경우에는 CNIArgs도 들어갈 수 있다. annotation.go는 plugin binding의 경우 logical network name을 CNI arg로 넣는다.
이 말은 plugin binding과 secondary network 모델이 단순 병렬 관계가 아니라, annotation 단계에서 서로 결합될 수 있다는 뜻이다.
Pod가 secondary network를 받은 뒤에는 무엇이 일어나는가
KubeVirt는 Pod annotation만 쓰고 끝내지 않는다. Pod가 실제로 secondary network를 받은 후, 그 결과를 다시 VMI status에 반영해야 한다.
이 역할을 하는 코드가 pkg/network/controllers/vmi.go다.
여기서는:
- Pod의 Multus network status annotation을 읽고
- primary interface status를 계산하고
- secondary interface status를 계산하고
- VMI status의
Interfaces에 반영한다
즉 VMI status는 "guest가 네트워크를 어떻게 봐야 하는가"뿐 아니라, "Pod가 실제로 어떤 secondary interface를 받았는가"도 담는다.
InfoSource가 왜 중요한가
vmi.go를 보면 interface status에 InfoSource 개념이 있다. 이는 정보가 어디서 왔는지 구분하기 위한 장치다.
- Pod의 Multus network status에서 왔는가
- libvirt domain에서 왔는가
- guest agent에서 왔는가
이 구분은 매우 중요하다. 예를 들어 interface가 Pod 쪽에는 있는데 domain에는 아직 반영되지 않았으면, 네트워크 hotplug가 진행 중이거나 미완료일 수 있다.
즉 InfoSource는 단순 메타데이터가 아니라 reconciliation 판단 재료다.
secondary network 변경이 migration을 유발할 수도 있다
pkg/network/migration/evaluator.go는 흥미로운 사실을 보여준다. secondary interface hotplug나 unplug, NAD 참조 변경은 경우에 따라 VMI를 migration-required 상태로 만들 수 있다.
이건 왜 중요할까?
network 구성이 바뀌면 현재 launcher Pod 위에서 안전하게 반영하기 어려운 경우가 있다. 이때 KubeVirt는:
- 즉시 migration 필요
- 잠시 pending 후 migration 필요
같은 판단을 내려 새로운 target Pod에서 network 구성을 정리해 반영하려고 한다.
즉 secondary network는 단순 CNI attach 문제가 아니라, VM 재배치 전략과도 연결되는 동적 상태다.
Multus와 binding plugin의 관계
많은 사람이 Multus와 binding plugin을 별개로 생각하지만, 실제로는 꽤 자주 함께 등장한다.
- Multus는 secondary network attach 메커니즘
- binding plugin은 guest wiring 방법
즉 하나는 Pod에 네트워크 endpoint를 들여오는 역할이고, 다른 하나는 그 endpoint를 guest NIC와 어떻게 묶을지 정하는 역할이다.
이 둘이 만나야 "secondary network가 guest에서 어떤 장치로 보이는가"가 완성된다.
실무에서 자주 생기는 문제
1. NAD는 맞는데 interface status가 비어 있다
Pod annotation은 생성되었지만 Multus attach 결과가 기대대로 안 들어왔을 수 있다.
2. Pod에는 interface가 있는데 guest에는 안 보인다
Pod 단계와 domain 단계가 어긋난 것이다. InfoSource를 봐야 한다.
3. hotplug 후 migration required가 붙는다
dynamic network update가 현재 Pod에서 안전하게 마무리되지 않아 재배치가 필요하다는 뜻일 수 있다.
자주 하는 오해
오해 1: secondary network는 Pod에 붙기만 하면 guest에도 자동이다
아니다. Pod attach와 guest wiring은 다른 단계다.
오해 2: Multus가 guest 네트워크까지 모두 책임진다
아니다. Multus는 Pod에 네트워크를 붙이고, guest wiring은 KubeVirt binding과 domain 설정이 책임진다.
오해 3: interface status는 보기 좋은 출력용이다
아니다. source별 상태를 합쳐 reconcile 판단에 쓰이는 중요한 데이터다.
마무리
KubeVirt에서 secondary network는 Multus와 NAD를 통해 Pod에 들어오고, KubeVirt가 그 결과를 annotation, naming scheme, interface status, guest wiring으로 이어서 완성한다. 즉 Multus는 endpoint provisioning, KubeVirt는 guest integration을 맡는 분업 구조다. 이 구조를 이해하면 여러 NIC를 가진 VM의 문제를 훨씬 체계적으로 디버깅할 수 있다.
다음 글에서는 네트워크에서 migration으로 넘어가서, migration CR이 생긴 뒤 target Pod가 어떤 control plane 과정을 거쳐 준비되는지 살펴보겠다.
How Multus and Secondary Networks Connect in KubeVirt
- Introduction
- Basic Idea
- Core Code: pkg/network/multus/annotation.go
- Why Interface Name Calculation Matters
- What Goes into the Annotation
- What Happens After the Pod Receives the Secondary Network
- Why InfoSource Matters
- Secondary Network Changes Can Trigger Migration
- The Relationship Between Multus and Binding Plugins
- Common Issues in Practice
- Common Misconceptions
- Conclusion
Introduction
The primary Pod network alone is often insufficient. VMs may need multiple NICs for data plane, storage plane, management networks, or NFV purposes. KubeVirt does not solve this with its own multi-network system but instead reuses the Multus and NetworkAttachmentDefinition ecosystem.
This post traces the path from secondary networks in the VMI spec through Pod annotations, Pod interfaces, and VMI status.
Basic Idea
The primary network is usually provided by the default Pod CNI. Secondary networks are additionally attached by Multus. KubeVirt reads the VMI spec and generates the necessary Multus annotations to place on the launcher Pod.
The flow is as follows:
- User specifies secondary networks in the VMI spec.
- KubeVirt generates Pod annotations.
- Multus reads the annotations and attaches additional interfaces according to the NAD.
- KubeVirt reads the Pod network status and updates the VMI status.
Thanks to this structure, KubeVirt does not recreate multi-network functionality in its core.
Core Code: pkg/network/multus/annotation.go
The key functions in this file are GenerateCNIAnnotation and GenerateCNIAnnotationFromNameScheme. The role is very clear:
- Find secondary Multus networks
- Calculate Pod interface names
- Build a list of
NetworkSelectionElement - Serialize them into a JSON annotation string
The controller does not stop at just "there is a NAD name" -- it also calculates what interface name it should be attached to inside the Pod.
Why Interface Name Calculation Matters
KubeVirt does not use logical network names and Pod interface names identically. It creates hashed names through the namescheme layer. The purpose of this approach is:
- Handling interface name length limits
- Predictable naming
- Minimizing conflicts even with many secondary networks
network-binding-plugin.md also explains naming conventions like pod-hash or tap-hash for the secondary network side.
In other words, naming conventions are not just aesthetics -- they are the contract that ensures the launcher Pod, CNI plugin, and libvirt domain all point to the same interface.
What Goes into the Annotation
The Multus NetworkSelectionElement typically contains:
- Namespace
- NAD name
- Interface request
- MAC request if needed
When a binding plugin is attached, CNIArgs may also be included. annotation.go inserts the logical network name as a CNI arg for plugin bindings.
This means the plugin binding and secondary network model are not simply parallel -- they can be coupled at the annotation stage.
What Happens After the Pod Receives the Secondary Network
KubeVirt does not stop at just writing Pod annotations. After the Pod actually receives the secondary network, the result must be reflected back into the VMI status.
The code handling this role is pkg/network/controllers/vmi.go.
Here:
- Pod Multus network status annotations are read
- Primary interface status is calculated
- Secondary interface status is calculated
- Results are reflected in the VMI status
Interfaces
In other words, the VMI status contains not only "how the guest should see the network" but also "what secondary interfaces the Pod actually received."
Why InfoSource Matters
Looking at vmi.go, there is an InfoSource concept in the interface status. This is a mechanism to distinguish where information came from.
- Did it come from Pod Multus network status?
- Did it come from the libvirt domain?
- Did it come from the guest agent?
This distinction is very important. For example, if an interface exists on the Pod side but is not yet reflected in the domain, a network hotplug may be in progress or incomplete.
In other words, InfoSource is not just metadata -- it is material for reconciliation decisions.
Secondary Network Changes Can Trigger Migration
pkg/network/migration/evaluator.go reveals an interesting fact. Secondary interface hotplug or unplug, and NAD reference changes, can in some cases put a VMI into a migration-required state.
Why is this important?
When network configuration changes, there are cases where it is difficult to safely apply them on the current launcher Pod. In such cases, KubeVirt may decide:
- Immediate migration required
- Pending for a moment then migration required
and attempt to apply the network configuration cleanly on a new target Pod.
In other words, secondary networks are not just a CNI attach issue -- they are a dynamic state that is also connected to VM re-placement strategy.
The Relationship Between Multus and Binding Plugins
Many people think of Multus and binding plugins as separate things, but in practice they often appear together.
- Multus is the secondary network attach mechanism
- Binding plugin is the guest wiring method
One is responsible for bringing network endpoints into the Pod, and the other determines how those endpoints are bound to guest NICs.
When both come together, "how the secondary network appears as a device in the guest" is completed.
Common Issues in Practice
1. NAD is correct but interface status is empty
The Pod annotation was created but the Multus attach result may not have been populated as expected.
2. Interface exists on Pod but is not visible in the guest
The Pod stage and domain stage are out of sync. Check InfoSource.
3. Migration required appears after hotplug
This may mean that a dynamic network update cannot be safely finalized on the current Pod and re-placement is needed.
Common Misconceptions
Misconception 1: Once attached to Pod, secondary networks are automatic in the guest
No. Pod attach and guest wiring are different stages.
Misconception 2: Multus handles everything up to guest networking
No. Multus attaches the network to the Pod, and guest wiring is the responsibility of KubeVirt binding and domain configuration.
Misconception 3: Interface status is just display output
No. It is important data used for reconcile decisions by combining status from multiple sources.
Conclusion
In KubeVirt, secondary networks enter the Pod through Multus and NAD, and KubeVirt completes them by connecting annotations, naming schemes, interface status, and guest wiring. Multus handles endpoint provisioning, KubeVirt handles guest integration -- a division of labor structure. Understanding this structure enables much more systematic debugging of VMs with multiple NICs.
In the next post, we will move from networking to migration and examine the control plane process that the target Pod goes through after a migration CR is created.