Four VM Network Modes
한국어 원문으로 표시합니다.
한 줄 요약
VM 네트워킹의 선택은 누가 패킷을 처리하는가로 갈린다. 유저 공간(SLIRP), 커널(TAP), 커널 브리지, 그리고 커널 안의 virtio 처리(vhost-net).
왜 이게 필요했나
"VM 에서 인터넷은 되는데 밖에서 VM 으로 못 들어갑니다." 이 신고의 원인은 거의 항상 user(SLIRP) 모드다. 개발 환경에서 기본값으로 쓰이는데, 그 특성을 모르면 프로덕션에 그대로 나간다.
어떻게 동작하나
user (SLIRP)
QEMU 가 유저 공간에 TCP/IP 스택을 통째로 구현한다. 게스트가 보낸 패킷을 QEMU 가 해석해 호스트의 평범한 소켓으로 다시 만들어 보낸다.
- 특권이 전혀 필요 없다. 이 실습 환경에서 유일하게 쓸 수 있는 이유다.
- 게스트는
10.0.2.0/24대역에 놓이고 게이트웨이는10.0.2.2, DNS 는10.0.2.3이다. - 밖에서 게스트로 들어올 수 없다.
hostfwd로 개별 포트를 뚫어야 한다. - ICMP 가 제한적이다. 게스트에서
ping이 안 되는 경우가 많은데 정상이다. - 성능이 낮다. 모든 패킷이 유저 공간을 한 번 더 거친다.
tap
커널의 가상 이더넷 장치를 만들고 QEMU 가 그 한쪽 끝을 잡는다. 패킷이 커널 네트워크 스택에 그대로 올라오므로 호스트에서 보면 진짜 인터페이스처럼 보인다. 대신 TAP 장치를 만들려면 특권(또는 미리 만들어 둔 장치에 대한 권한)이 필요하다.
bridge
TAP 을 리눅스 브리지에 꽂는다. 브리지에 물리 NIC 도 함께 꽂혀 있으면 VM 이 물리망에 직접 붙은 것처럼 동작한다. VM 이 DHCP 로 사내망 주소를 받고, 다른 서버에서 그 주소로 바로 접속된다. 프로덕션의 기본 구성이다.
ip link add br0 type bridge
ip link set eth0 master br0
ip link set br0 up
qemu-system-x86_64 -netdev tap,id=n0,ifname=tap0,script=no -device virtio-net-pci,netdev=n0
vhost-net
virtio 링 버퍼 처리를 커널 안에서 한다. QEMU 유저 공간을 왕복하지 않으므로 지연과 CPU 사용이 크게 준다. -netdev tap,...,vhost=on.
| 모드 | 특권 | 밖→안 접속 | 성능 | 용도 |
|---|---|---|---|---|
| user | 불필요 | hostfwd 로만 | 낮음 | 개발/테스트 |
| tap | 필요 | 가능 | 중 | 격리된 VM 망 |
| bridge | 필요 | 가능(물리망 직결) | 중~높음 | 프로덕션 |
| vhost-net | 필요 | 가능 | 높음 | 고성능 워크로드 |
현장에서 만나는 모습
개발 환경에서 잘 되던 것이 스테이징에서 안 되는 경우. 개발은 user 모드라 hostfwd 로 22 번만 뚫어 뒀고, 스테이징은 bridge 라 전부 열려 있다. 반대로 hostfwd 로만 접근하다가 브리지로 옮기면 방화벽을 새로 설계해야 한다 — 지금까지는 QEMU 가 사실상 방화벽 역할을 하고 있었기 때문이다.
MTU 문제. 브리지 위에 VXLAN 같은 오버레이가 겹치면 실효 MTU 가 줄어든다. VM 안에서는 여전히 1500 이라 큰 패킷이 조용히 사라진다. 게스트 MTU 를 낮추거나 MSS 클램핑이 필요하다.
컨테이너 네트워킹과 무엇이 같고 다른가
여기까지 본 네 가지 모드는 컨테이너 쪽에서도 거의 그대로 반복된다. 이름만 다르고 하는 일이 같아서, 한쪽을 이해하면 다른 쪽이 공짜로 따라온다.
- VM 의 user 모드 ≈ 컨테이너의 포트 포워딩. 안에서 밖으로는 나가지만 밖에서 안으로는 뚫어 준 포트로만 들어온다.
- VM 의 tap ≈ 컨테이너의 veth 쌍. 커널에 가상 인터페이스를 만들고 한쪽을 안에, 한쪽을 밖에 둔다.
- VM 의 bridge ≈ 도커의 기본 브리지 네트워크. 여러 개를 같은 브리지에 꽂아 서로 통신하게 한다.
- VM 의 vhost-net ≈ 데이터 평면을 커널이나 eBPF 로 내리는 것. 유저 공간을 왕복하지 않아 빨라진다.
다른 점도 분명하다. 컨테이너는 호스트 커널을 공유하므로 네트워크 네임스페이스만 나뉘지만, VM 은 자기 커널을 들고 있다. 그래서 VM 안에서 커널 파라미터를 바꾸는 것은 호스트에 영향이 없지만, 컨테이너 안의 상당수 커널 설정은 호스트와 공유되거나 아예 바꿀 수 없다. 반대로 VM 은 부팅 시간이 길고 메모리를 통째로 잡는다.
실무의 판단으로 옮기면 이렇다. 커널 자체를 다뤄야 하거나(모듈 적재, 다른 커널 버전), 격리 수준이 정말 높아야 하거나, 리눅스가 아닌 것을 돌려야 하면 VM 이다. 그 외에는 컨테이너가 거의 언제나 가볍고 빠르다. 그리고 둘을 섞어 쓰는 것이 흔하다 — 쿠버네티스 노드 자체가 VM 이고 그 위에서 컨테이너가 도는 구성이 대표적이며, 이때 네트워크 층이 두 겹으로 쌓여서 앞에서 말한 MTU 문제가 특히 자주 나타난다.
다음에서 할 것
이 모듈은 개념만 다룬다. 앞의 vm-boot 실습에서 -netdev user,hostfwd=... 를 쓴 이유를 이제 설명할 수 있어야 한다 — 특권이 없는 환경에서 유일하게 가능한 선택이었기 때문이다.