Skip to content

✍️ 필사 모드: KubeVirt 네트워크 1: Pod 네트워크를 VM 네트워크로 바꾸는 기본 원리

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

들어가며

사용자 입장에서 가장 직관적인 질문은 이것이다. "VM은 IP를 어디서 받는가? CNI가 직접 VM에 IP를 주는가?" KubeVirt 네트워크를 이해하려면 먼저 답을 짧게 말할 수 있어야 한다.

기본적으로 CNI는 먼저 virt-launcher Pod에 네트워크를 준다. KubeVirt는 그 Pod 네트워크 namespace 안에서 TAP, bridge, DHCP, NAT를 구성해 guest NIC를 연결한다.

즉 guest는 보통 CNI를 직접 호출하지 않는다. CNI가 준비한 Pod 네트워크를 KubeVirt가 재배선한다.

왜 이런 구조를 택했을까

KubeVirt 설계 철학은 네트워크를 새로 만들기보다 Kubernetes와 CNI를 최대한 재사용하는 것이다. docs/components.mddocs/network/libvirt-pod-networking.md는 이를 꽤 분명하게 설명한다.

이 구조를 쓰면:

  • Pod 스케줄링과 네트워크 attach는 그대로 Kubernetes가 맡고
  • KubeVirt는 guest 관점의 NIC wiring만 담당하면 된다
  • Services, NetworkPolicy, Pod 네트워크 생태계와의 정합성이 좋아진다

즉 KubeVirt 네트워크의 핵심은 "VM 전용 네트워크 시스템"이 아니라 Pod 네트워크의 guest-side adaptation이다.

가장 먼저 일어나는 일: Pod가 네트워크를 받는다

virt-launcher Pod가 생성되면 먼저 CNI가 Pod 네트워크를 붙인다. 기본 primary interface는 보통 eth0로 보인다. 이 시점까지는 일반 Pod와 크게 다르지 않다.

중요한 것은 여기서 KubeVirt가 아직 guest NIC를 만들지 않았다는 점이다. 우선 Pod가 네트워크를 갖고, 그 다음 KubeVirt가 이 네트워크 namespace 내부에서 guest용 구성을 더한다.

pkg/network/setup이 하는 일

핵심 코드는 pkg/network/setup/network.go, podnic.go, netpod/*, dhcp/*, link/*에 모여 있다.

여기서 보이는 흐름은 다음과 같다.

  1. VMI의 network spec과 interface spec를 읽는다.
  2. Pod에서 실제 인터페이스 이름을 찾는다.
  3. binding 방식에 따라 bridge, masquerade, passt, SR-IOV 등의 wiring을 결정한다.
  4. 필요하면 TAP 장치를 만들고 libvirt domain NIC spec를 생성한다.
  5. 필요하면 KubeVirt 내부 DHCP 서버를 띄운다.

즉 network setup은 "이 VM의 NIC가 guest에서 어떻게 보여야 하는가"를 Pod namespace 안에서 준비하는 단계다.

TAP은 왜 필요한가

guest 운영체제는 가상 NIC를 통해 패킷을 주고받는다. QEMU 쪽에서는 흔히 TAP 디바이스를 backend로 사용한다. cmd/virt-chroot/tap-device-maker.gopkg/network/setup/podnic.go를 보면 KubeVirt가 TAP 장치 생성을 중요한 primitive로 사용한다는 점이 드러난다.

단순화하면:

  • Pod 쪽 인터페이스 또는 bridge가 host-side endpoint 역할
  • TAP이 QEMU 쪽 endpoint 역할
  • libvirt domain spec가 둘을 이어주는 구성 정보를 담음

즉 TAP은 guest와 Pod 네트워크 사이의 실질적인 접점 중 하나다.

DHCP는 왜 KubeVirt가 직접 띄우는가

많은 경우 guest는 직접 CNI와 대화하지 않는다. 대신 KubeVirt가 guest에게 네트워크 설정을 알려 준다. pkg/network/setup/podnic.go를 보면 newDHCPConfigurator가 bridge 또는 masquerade binding에서 DHCP configurator를 만들고, EnsureDHCPServerStarted를 호출한다.

이 말은 곧:

  • Pod 인터페이스 정보는 host 쪽에 있고
  • guest가 받아야 할 IP와 gateway는 KubeVirt가 계산해
  • DHCP로 guest에 전달한다

는 뜻이다.

guest IP는 누가 할당하는가

여기서 binding 방식별로 구분해야 한다.

masquerade binding

pkg/network/dhcp/masquerade.gopkg/network/link/address.go를 보면, KubeVirt는 guest용 내부 CIDR에서 gateway와 guest IP를 계산한다. IPv4 기준으로 VMNetworkCIDR가 없으면 기본 CIDR을 쓰고, 거기서 gateway와 guest IP를 정한다.

즉 masquerade에서는:

  • Pod IP는 여전히 CNI가 Pod에 할당
  • guest IP는 KubeVirt가 내부 CIDR에서 계산
  • KubeVirt DHCP가 guest에 전달
  • egress와 inbound 일부는 NAT로 처리

이게 가장 흔한 "VM은 private IP를 받고 Pod IP 뒤에 숨어 있는" 모델이다.

bridge binding

bridge binding은 guest를 Pod 네트워크에 더 직접 가깝게 붙인다. 여기서는 bridge와 TAP을 이용해 guest가 Pod 네트워크의 endpoint처럼 동작하게 만든다. 역사적으로는 Pod 쪽 IP를 guest가 받아 가는 형태의 모델 설명이 많았고, 현재도 핵심은 guest가 더 직접적인 L2 또는 Pod 네트워크 연결성을 가진다는 데 있다.

즉 bridge binding의 핵심은 "Pod IP를 NAT 뒤에 숨기는 것"보다 guest를 네트워크에 더 직접 노출하는 데 있다.

masquerade binding에서는 NAT가 어떻게 구현되는가

pkg/network/setup/netpod/masquerade/masquerade.go를 보면 KubeVirt는 masquerade에서 nftables 기반 NAT 규칙을 구성한다.

중요한 흐름은 다음과 같다.

  • NAT table과 chain 생성
  • guest source address에 대한 masquerade 규칙 추가
  • 포트 포워딩 또는 DNAT 규칙 추가
  • loopback과 migration 관련 예외 포트 처리

즉 masquerade는 단순 "브리지 하나"가 아니라:

  • 내부 guest 주소 계산
  • DHCP
  • nftables NAT
  • 필요 포트의 DNAT 또는 SNAT

가 함께 작동하는 조합이다.

link와 interface status는 왜 중요할까

KubeVirt는 guest NIC가 어떤 Pod interface와 연결되었는지 추적해야 한다. 그래서 pkg/network/controllers/vmi.go는 Pod annotation의 Multus network status를 읽어 VMI status의 Interfaces 필드를 갱신한다.

이 status에는:

  • guest 쪽 logical network 이름
  • Pod interface 이름
  • info source가 Pod status인지 domain인지 guest agent인지

같은 정보가 들어간다. 네트워크 디버깅에서 이게 매우 중요하다.

이 구조가 운영상 주는 장점

1. Kubernetes 네트워크 모델을 크게 깨지 않는다

Pod는 여전히 CNI를 통해 attach된다.

2. guest 설정을 제어할 수 있다

KubeVirt가 DHCP와 bridge 또는 NAT를 직접 다루므로, guest 쪽 경험을 일관되게 만들 수 있다.

3. binding별 trade-off를 선택할 수 있다

성능, reachability, 서비스 노출, migration 적합성에 따라 binding을 고를 수 있다.

자주 하는 오해

오해 1: VM이 CNI에서 직접 IP를 받는다

기본적으로는 Pod가 먼저 받고, guest는 KubeVirt가 그 위에서 연결한다.

오해 2: Pod IP와 guest IP는 항상 같다

아니다. 특히 masquerade에서는 둘이 다르다. Pod IP는 CNI가, guest IP는 KubeVirt DHCP가 다룬다.

오해 3: KubeVirt 네트워크는 libvirt 내부 기능만으로 끝난다

아니다. Pod namespace 조작, TAP, DHCP, nftables, netlink가 함께 필요하다.

디버깅 체크리스트

  • Pod IP가 있는지와 guest IP가 있는지를 분리해서 본다.
  • masquerade면 guest CIDR, DHCP, nftables 규칙을 본다.
  • bridge면 Pod interface와 bridge, TAP, guest DHCP 경로를 본다.
  • VMI status의 Interfaces와 launcher Pod의 실제 인터페이스 이름이 일치하는지 본다.

마무리

KubeVirt 네트워크의 핵심은 VM이 CNI를 직접 쓰는 것이 아니라, Pod가 먼저 받은 네트워크를 guest 친화적으로 재배선하는 데 있다. 그래서 IP 할당도 binding에 따라 다르다. Pod IP는 CNI가 줄 수 있지만, guest IP는 KubeVirt의 DHCP와 내부 bridge 또는 NAT 로직이 결정할 수 있다. 이 기본 원리를 잡아 두면 다음 글에서 bridge, masquerade, passt, SR-IOV binding의 차이를 훨씬 명확하게 이해할 수 있다.

다음 글에서는 바로 그 binding들의 성격과 trade-off를 비교해 보겠다.

현재 단락 (1/71)

사용자 입장에서 가장 직관적인 질문은 이것이다. "VM은 IP를 어디서 받는가? CNI가 직접 VM에 IP를 주는가?" KubeVirt 네트워크를 이해하려면 먼저 답을 짧게 말할 ...

작성 글자: 0원문 글자: 3,907작성 단락: 0/71