Skip to content
Published on

Live Migration 3: migration proxy, 포트, TLS, 소켓은 왜 필요한가

Authors

들어가며

live migration을 생각하면 흔히 "source QEMU가 target QEMU로 메모리를 복사한다"라고 끝내기 쉽다. 하지만 실제 Kubernetes 환경에서는 그 사이에 훨씬 더 복잡한 transport 문제가 있다.

  • launcher 내부 libvirt는 주로 unix socket과 로컬 자원을 쓴다
  • target Pod는 동적으로 생긴다
  • source와 target은 서로 다른 노드에 있다
  • 추가 TLS 계층이 필요하다
  • block migration일 때는 포트 요구가 달라진다

이 문제를 풀기 위해 KubeVirt는 pkg/virt-handler/migration-proxy/migration-proxy.go의 migration proxy 계층을 둔다.

proxy가 왜 필요한가

핵심 이유는 로컬 libvirt 제어 socket과 클러스터 네트워크 경로를 직접 1:1로 묶기 어렵기 때문이다.

launcher 내부에서는 libvirt와 QEMU가 unix socket, 로컬 파일, Pod namespace 기반으로 동작한다. 하지만 migration은 source 노드와 target 노드 사이의 네트워크 연결이 필요하다. 따라서 KubeVirt는 중간에 proxy를 둬서:

  • target 측에서는 TCP listener를 열고
  • source 측에서는 local unix socket을 제공하며
  • 두 쪽을 TLS 또는 plain transport로 연결

하는 방식으로 transport를 정리한다.

즉 migration proxy는 source와 target 사이의 주소 번역기이자 transport shim이다.

source와 target에서 각각 하는 일

ProxyManager 인터페이스를 보면 역할이 선명하다.

target 측

  • StartTargetListener
  • GetTargetListenerPorts
  • StopTargetListener

즉 target 노드는 외부에서 들어올 TCP 연결을 받을 listener를 연다.

source 측

  • StartSourceListener
  • GetSourceListenerFiles
  • StopSourceListener

즉 source 노드는 local unix socket 파일을 만들고, 그것을 target 주소와 포트로 포워딩한다.

이 구조 덕분에 launcher 입장에서는 익숙한 local endpoint로 보이지만, 실제 transport는 network를 타고 target까지 간다.

포트는 어떻게 쓰이는가

코드는 기본 migration 포트를 다음처럼 정의한다.

  • direct migration port: 49152
  • block migration port: 49153

GetMigrationPortsList는 block migration 여부에 따라 필요한 포트 집합을 결정한다. 즉 디스크를 추가로 옮겨야 하면 포트 요구가 늘어난다.

운영자가 여기서 알아야 할 핵심은 live migration이 "보이지 않는 내부 통신"이 아니라, 명확한 포트와 listener를 가진 네트워크 작업이라는 점이다.

target listener는 왜 random port를 쓸 수 있는가

StartTargetListener는 target unix file들에 대해 proxy를 만들고, TCP bind port를 0으로 넘겨 random port를 사용할 수 있다. 이후 GetTargetListenerPorts가 실제 바인딩된 포트를 source 쪽에 알려 준다.

이 방식의 장점은 포트 충돌을 줄이고, Pod와 노드의 동적 환경에서 더 유연하게 listener를 열 수 있다는 점이다.

즉 고정 논리 포트와 실제 bind 포트를 분리해 두는 구조다.

소켓 파일은 왜 필요한가

source 쪽의 SourceUnixFile을 보면 migration proxy는 base directory 아래에 source socket 파일을 만든다. 이유는 launcher 내부 libvirt가 여전히 local unix endpoint를 기준으로 동작하는 흐름과 잘 맞기 때문이다.

이 말은 migration proxy가 네트워크를 위해 기존 local runtime 모델을 버리는 것이 아니라, unix socket 모델을 네트워크 가능한 형태로 포장한다는 뜻이다.

TLS는 어디에 붙는가

migration-proxy.go는 server TLS config, client TLS config, migration TLS config를 따로 관리한다. 또 cluster migration configuration에 DisableTLS 옵션이 있으면 target listener에서 추가 TLS 레이어를 끌 수 있다.

하지만 코드와 API 설명 모두 공통적으로 시사하는 바는 명확하다. DisableTLS는 보통 나쁜 생각이다.

왜냐하면 migration 트래픽은 guest 메모리 상태와 실행 상태를 담기 때문에, 네트워크 경로 보호가 중요하기 때문이다.

즉 migration proxy는 단순 포워더가 아니라 보안 경계이기도 하다.

migration network를 왜 따로 둘 수 있는가

KubeVirt configuration에는 migration traffic이 기본 Pod network가 아니라 별도 network를 타게 하는 옵션이 있다. 이는 migration 트래픽이 애플리케이션 Pod 네트워크와 경쟁하거나, 대역폭과 보안 측면에서 분리되어야 할 수 있기 때문이다.

이 설정이 있는 이유를 proxy 관점에서 보면 이해가 쉽다. proxy는 어차피 source와 target 사이의 transport shim이므로, 그 transport를 어느 네트워크로 보낼지 바꾸기 쉬운 구조가 된다.

block migration과 direct migration이 왜 다른가

direct migration은 주로 메모리와 실행 상태 중심이지만, block migration은 디스크 관련 데이터 경로까지 포함할 수 있다. 그래서 포트도 더 필요하고, 전체 bandwidth와 timeout 계산도 더 민감해진다.

proxy 계층이 이 둘을 구분해 포트 맵을 관리하는 이유가 여기에 있다.

운영자가 proxy를 봐야 하는 순간

다음 같은 상황에서는 migration proxy를 먼저 의심해야 한다.

  • target Pod는 살아 있는데 migration 연결이 시작되지 않음
  • source와 target 사이 TLS handshake 문제가 있음
  • block migration에서 특정 포트만 실패함
  • migration network 분리 후 연결 문제가 생김

이때는 단순히 libvirt error만 보면 안 되고, listener가 열렸는지, source socket이 만들어졌는지, target 포트 매핑이 맞는지를 함께 봐야 한다.

자주 하는 오해

오해 1: source와 target QEMU가 그냥 직접 붙는다

Kubernetes 환경에서는 중간 transport shim이 필요하다. 그 역할을 migration proxy가 한다.

오해 2: migration 포트는 항상 고정이다

논리 포트는 정해져 있어도 실제 bind 포트와 매핑은 유동적일 수 있다.

오해 3: TLS는 선택 사항이니 꺼도 큰 문제 없다

migration state는 민감하다. 보통은 추가 TLS 레이어를 유지하는 것이 맞다.

마무리

KubeVirt의 migration proxy는 live migration transport를 Kubernetes 환경에 맞게 다듬는 핵심 계층이다. target 쪽은 TCP listener를 열고, source 쪽은 unix socket을 제공하며, 그 사이를 TLS와 포트 매핑으로 연결한다. 이 구조 덕분에 launcher 내부의 로컬 libvirt 모델과 클러스터 간 네트워크 이동 모델이 깔끔하게 이어진다.

다음 글에서는 migration과 직접 연결되는 자원 문제, 즉 CPU, 메모리, NUMA, hugepages가 Pod 스케줄링과 guest 하드웨어 모델에서 어떻게 해석되는지 살펴보겠다.