feat: 가상화 문서들 추가

This commit is contained in:
DongHyeonka
2026-09-10 08:54:05 +09:00
parent e9f6a93327
commit 43e1aadef0
695 changed files with 153404 additions and 12754 deletions
@@ -0,0 +1,341 @@
---
id: 5b1de31c-0495-4226-8607-73ed7284b314
kind: CONCEPT
slug: guest-packet-path-to-physical-nic
title: Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge
topic: network-virtualization
topicName: 네트워크 가상화
project: virtualization
status: 초안
basisVersion: Linux KVM/QEMU/libvirt · virtio-net frontend · vhost-net kernel backend · TAP · Linux Bridge
studio: "https://hyeonworks.com/studio/documents/5b1de31c-0495-4226-8607-73ed7284b314/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
assets:
- key: packet-control-and-data-paths
file: ../../../final/assets/diagrams/packet-control-and-data-paths/packet-control-and-data-paths.svg
source:
- final/document.md#94-전체-네트워크-계층
- final/document.md#121-이-ssot에서-파생될-concept
- final/document.md#125-최종-기준-구조
- final/document.md#89-문서-목적
- final/document.md#90-virsh-libvirt-virtio-구분
- final/document.md#91-virtio-net은-정확히-어디에-있는가
- final/document.md#92-frontend와-backend
- final/document.md#93-guest-os는-왜-qemu가-아니라-virtio-net을-사용하는가
- final/document.md#95-physical-nic의-역할
- final/document.md#96-linux-bridge의-역할
- final/document.md#97-routing의-역할
- final/document.md#98-nat의-역할
- final/document.md#99-tap의-역할
- final/document.md#100-virtqueue의-역할
- final/document.md#101-guest-tcp-ip-stack의-역할
- final/document.md#102-packet이-keycloak까지-올라오는-과정
- final/document.md#103-qemu-virtio-device-model의-역할
- final/document.md#104-왜-tap-→-vhost-net-→-qemu-→-virtqueue-라고-일반화하면-안-되는가
- final/document.md#105-control-path와-data-path
- final/document.md#106-qemu가-userspace인데-packet이-qemu를-안-거칠-수-있는-이유
- final/document.md#107-vhost-net-최적화
- final/document.md#108-vhost-net은-qemu를-제거하지-않는다
- final/document.md#109-fast-path와-slow-control-path
- final/document.md#110-data-copy-최적화
- final/document.md#111-interrupt-notification-최적화
- final/document.md#112-multi-queue-최적화
- final/document.md#113-offload-최적화
- final/document.md#114-linux-bridge가-항상-host-tcp-ip-stack을-거치는-것은-아니다
- final/document.md#115-host-physical-nic로-나갈-때-virtio를-다시-거치지-않는다
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-4
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-5
- final/document.md#124-핵심-claim
---
# Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge
가상 머신 안의 Keycloak 이 HTTP 요청 하나를 받으려면 그 패킷이 먼저 호스트의 물리 NIC 에 닿아야 한다. 거기서 Linux Bridge 와 TAP, vhost-net, virtqueue 를 지나 게스트 커널의 TCP/IP 스택까지 올라온다. virtio-net 은 그 경로 어딘가에 놓인 프로그램 하나가 아니다. 게스트 쪽 프런트엔드 드라이버와 호스트 쪽 백엔드를 잇는 I/O 계약이고, 그 백엔드 자리는 QEMU 사용자 공간이 맡을 수도 호스트 커널의 vhost-net 이 맡을 수도 있다. 가상 머신 두 대에서 Keycloak 멀티 노드 실험을 돌리면 요청이 느려지거나 아예 닿지 않는 일이 생긴다. 이 경로를 알아 두면 그 증상을 애플리케이션·저장소 쪽 문제와 네트워크 가상화 계층 문제로 갈라 볼 수 있다.
## 관계
- **packet 이 어디서 끊겼는지는 계층마다 capture 해서 가른다**
이 글이 세운 계층을 그대로 관측 지점으로 쓰는 절차다. 어느 계층까지 패킷이 보였는지로 의심 구간을 좁힌다.
- **Keycloak 실험 결과를 애플리케이션 원인으로 읽기 전에 network 계층을 따로 검증한다**
이 경로가 실험의 여러 노드에 공유되기 때문에 생기는 오귀속을 막는 기준이다.
- **이 호스트의 가상 머신 네트워크는 Bridge 인가 NAT 인가 Routing 인가**
이 글은 Bridge 와 라우팅, NAT 가 각각 무엇을 하는지까지만 적었다. 이 호스트가 그중 무엇으로 구성돼 있는지는 확인하지 않았다.
- **VM1 과 VM2 의 TAP/vnet interface 는 무엇이고 어디에 붙어 있는가**
TAP 이 게스트의 이더넷 프레임과 호스트 네트워크를 잇는 접점이라는 설명이 이 물음의 전제다.
- **이 호스트에서 vhost-net 이 실제로 packet datapath 를 맡고 있는가**
백엔드가 QEMU 사용자 공간일 수도 vhost-net 일 수도 있다고 적었다. 이 호스트가 어느 쪽인지는 아직 모른다.
- **QEMU backend 와 vhost-net 의 차이가 이 호스트에서 실제로 보이는가**
두 백엔드의 경로 차이는 여기 적었지만, 그 차이가 지연이나 CPU 사용량으로 드러나는지는 재지 않았다.
- **이 가상 머신들의 virtio-net 에 multi-queue 가 켜져 있는가**
큐를 하나만 쓰면 부하가 몰릴 수 있다고만 적었다. 지금 큐가 몇 개인지는 확인하지 않았다.
- **Host Nginx 에서 Keycloak 까지 packet 은 실제로 어디를 지나는가**
여기 그린 경로는 가장 기본적인 조합을 기준으로 한 것이고, 이 테스트 환경의 실제 경로는 추적하지 않았다.
- **부하 중 network 가상화가 지연을 바꿀 만큼 호스트 CPU 를 쓰는가**
vhost-net 과 QEMU 스레드, softirq 도 호스트 CPU 를 쓴다는 것까지가 이 글의 범위다.
- **KVM 에서 vCPU 가 물리 CPU 에서 실행되기까지**
QEMU 가 장치를 만들지만 반복 실행은 커널이 맡는다는 구조가 CPU 쪽과 같다. 그쪽 글이 같은 구분을 명령 실행으로 설명한다.
## 본문
<!-- body:start -->
## virtio-net 은 어디에 있는가
virtio 는 명령어가 아니고 프로그램 하나도 커널 모듈 하나도 아니다. 게스트와 호스트/하이퍼바이저가 가상 I/O 장치를 효율적으로 쓰려고 정해 둔 표준화된 인터페이스이자 프로토콜이다. 그 계열에는 네트워크의 virtio-net 과 블록 I/O 의 virtio-blk 가 있다. SCSI(Small Computer System Interface) 의 virtio-scsi 와 메모리 벌룬의 virtio-balloon 도 같은 계열이고, 이 글은 그중 virtio-net 을 다룬다.
가상 NIC 를 성립시키는 구현은 게스트와 호스트로 나뉘어 있다. 게스트 커널 쪽에는 TCP/IP 스택과 virtio-net 프런트엔드 드라이버, virtqueue 가 들어 있다.
```text label="Guest 측"
Guest Kernel
├─ TCP/IP Stack
├─ virtio-net Frontend Driver
└─ virtqueue
```
호스트 쪽은 사용자 공간과 커널로 다시 갈린다. QEMU 프로세스 안에 virtio-net 장치 모델이 있고, 커널에는 vhost-net 과 TAP, Linux Bridge 와 라우팅·NAT, 물리 NIC 드라이버가 있다.
```text label="Host 측"
Host Userspace
└─ QEMU virtio-net Device Model
Host Kernel
├─ vhost-net (사용하는 경우)
├─ TAP
├─ Linux Bridge / Routing / NAT
└─ Physical NIC Driver
```
그래서 virtio 는 특정 커널 계층 자체가 아니라 게스트 쪽 프런트엔드(frontend)와 호스트 쪽 백엔드(backend) 사이의 I/O 계약이다. 프런트엔드는 게스트 커널의 virtio-net 드라이버이고, 백엔드는 게스트가 넘긴 패킷 버퍼를 호스트 쪽에서 처리하는 구현이다. 이 백엔드 자리에는 QEMU 사용자 공간이 올 수도 있고 호스트 커널의 vhost-net 이 올 수도 있다.
둘 사이에 놓인 virtqueue 는 NIC 도 아니고 Linux 네트워크 인터페이스도 아니다. 게스트와 호스트 백엔드가 I/O 버퍼를 주고받으려고 쓰는 디스크립터 기반 공유 큐다. 네트워크에서는 보통 게스트에서 호스트로 가는 TX(transmit) virtqueue 와 호스트에서 게스트로 가는 RX(receive) virtqueue 를 쓴다. 페이로드를 매번 전통적인 사용자 공간 API 호출로 넘기는 방식이 아니라, 게스트 메모리의 버퍼와 디스크립터를 효율적으로 공유하고 참조하도록 설계돼 있다.
게스트 입장에서는 이 구조가 보이지 않는다. 가상 머신을 시작할 때 QEMU 가 가상 PCI(Peripheral Component Interconnect) 버스에 virtio NIC 를 노출한다. 그러면 게스트 Linux 가 그 장치를 발견하고 virtio-net 드라이버를 붙여 `ens3` 나 `eth0` 같은 인터페이스를 만든다. 그다음부터 게스트는 QEMU 를 호출한다고 동작하지 않고 자기 NIC 를 쓴다고 동작한다.
## 장치를 만드는 쪽과 패킷을 나르는 쪽
`virsh` 는 사용자가 libvirt 에 가상 머신 관리 명령을 전달하는 명령줄 도구여서 패킷이 지나는 길에는 직접 참여하지 않는다. libvirt 는 그 아래에서 vCPU 와 메모리, 디스크, NIC 모델, MAC 주소, 가상 네트워크, 브리지, QEMU 인자 같은 가상 머신의 수명 주기와 구성을 관리한다. 실제 프로세스로 실행되는 것은 QEMU 다.
QEMU 안의 virtio 장치 모델이 하는 일은 둘로 갈라서 봐야 뒤가 이어진다. 하나는 장치를 만들고 설정하고 관리하는 일이다. virtio-net 장치 모델을 만들어 게스트에게 노출하고, 기능 협상(feature negotiation)을 하고, virtqueue 를 설정하고, 백엔드를 연결한다. 다른 하나는 패킷을 실제로 나르는 처리인데, 이쪽은 QEMU 가 맡을 수도 맡지 않을 수도 있다. QEMU 백엔드를 직접 쓰면 패킷이 QEMU 를 지난다.
```text label="QEMU backend 를 직접 사용하는 경우"
TAP
QEMU virtio backend
virtqueue
Guest
```
vhost-net 을 쓰면 반복되는 패킷 I/O 를 호스트 커널에서 처리하고 QEMU 사용자 공간을 우회한다.
```text label="vhost-net 을 사용하는 경우"
TAP
vhost-net
virtqueue
Guest
```
이 구분에는 경로 이름이 따로 붙어 있다. 설정 경로(control/setup path)와 데이터 경로(data path)다. 여기서 control 은 Kubernetes 의 Control Plane 을 뜻하지 않고, 일반적인 시스템 용어로 설정·제어 경로를 가리킨다. `virsh` 에서 libvirt 와 QEMU, virtio-net 장치 모델로 내려가면서 기능 협상과 virtqueue 설정, vhost-net 설정이 일어나는 쪽이 설정 경로다. 그 설정이 끝난 뒤 패킷이 반복해서 흐르는 쪽이 데이터 경로다.
## 밖에서 들어온 패킷이 Keycloak 까지 올라오는 순서
가장 기본적인 virtio-net 과 vhost-net, TAP, Linux Bridge 조합을 기준으로 하면 수신 방향은 이 순서다.
```text label="수신 방향"
Internet / Client
Physical NIC
Physical NIC Driver
Linux Bridge / Routing / NAT
TAP
vhost-net
RX virtqueue
virtio-net Frontend Driver
Guest TCP/IP Stack
Socket
Keycloak
```
송신은 이 순서를 거꾸로 밟는다. Keycloak 이 소켓에 쓰면 게스트 TCP/IP 스택과 virtio-net 프런트엔드 드라이버를 지나 TX virtqueue 에 실린다. 거기서부터는 vhost-net 과 TAP, Linux Bridge 와 라우팅·NAT, 물리 NIC 드라이버를 차례로 지나 물리 NIC 로 나간다.
맨 앞의 물리 NIC(Network Interface Card, 네트워크 인터페이스 카드) 는 실제 네트워크 링크와 서버를 잇는 하드웨어다. 그 하드웨어를 호스트 커널의 드라이버가 제어하고 Linux 가 인터페이스로 노출하기 때문에, `ip link` 에 보이는 `enp3s0` 같은 인터페이스와 NIC 하드웨어를 같은 것으로 세지 않는다.
그다음의 Linux Bridge 는 호스트 커널 안에 있는 L2 소프트웨어 스위치다. 이더넷 프레임의 목적지 MAC(Media Access Control) 주소를 보고 어느 포트로 넘길지 정하고, MAC 주소를 학습하고, 여러 가상·물리 포트를 잇는다.
라우팅은 다른 계층에서 다른 값을 본다. 브리지가 L2 에서 MAC 주소를 보고 같은 이더넷 네트워크를 잇는다면, 라우팅은 L3 에서 목적지 IP(Internet Protocol) 주소를 보고 어느 인터페이스나 next-hop 으로 패킷을 보낼지 정한다. NAT(Network Address Translation) 는 또 다른 일을 해서 패킷의 IP 와 포트 정보를 바꾼다. 가상 머신이 사설 서브넷을 쓰면 호스트가 NAT 게이트웨이처럼 동작할 수 있다.
경로 그림에서는 셋이 한 줄에 나란히 적히지만 보는 값도 바꾸는 값도 서로 다르다. 그래서 실제 가상 머신 네트워크를 분석할 때는 브리지 기반인지 라우팅 기반인지 NAT 기반인지를 먼저 가른다.
TAP 은 호스트 Linux 커널이 제공하는 가상 이더넷 인터페이스이고 물리 장치가 아니다. `tap0` 나 `vnet0` 같은 이름으로 보이며, 가상 머신의 이더넷 프레임과 호스트 Linux 네트워크를 잇는 접점이 된다. 수신할 때는 Linux Bridge 에서 TAP 을 거쳐 가상 머신으로 들어가고, 송신할 때는 가상 머신에서 TAP 을 거쳐 Linux Bridge 로 나온다.
TAP 을 지나 게스트 안으로 들어오면 그다음은 평범한 Linux 네트워크 스택이다. 게스트의 TCP/IP 스택도 게스트 Linux 커널의 실제 스택이어서, 게스트 커널에는 소켓과 TCP, UDP, IP, 라우팅, neighbor/ARP, 방화벽, 네트워크 드라이버가 그대로 있다. TCP 는 연결 관리와 포트, 시퀀스, 순서 보장, 재전송, 중복 처리, 흐름 제어, 혼잡 제어를 맡는다. IP 계층은 IP 주소와 라우팅을 담당하고, NIC 에 가까운 계층에서는 이더넷 프레임과 MAC 주소를 다룬다.
그 위에서 애플리케이션이 보는 것은 소켓 하나다. 이더넷 프레임이 IP 패킷과 TCP 세그먼트를 지나 소켓에 닿고 그 위에서 HTTP 로 읽히면 Keycloak 이 요청을 처리한다.
```text label="Keycloak 이 요청 하나를 받기까지의 계층"
Ethernet Frame
IP Packet
TCP Segment / Stream
Socket
HTTP
Keycloak
```
그래서 Keycloak 은 virtqueue 와 vhost-net, TAP, Bridge, 물리 NIC 를 직접 알 필요가 없고, 게스트 Linux 가 주는 TCP 소켓 위에서만 동작한다. 아래쪽 계층이 어긋나도 애플리케이션 로그에는 느렸다는 것과 실패했다는 것만 남는 이유가 여기에 있다.
## vhost-net 이 우회하는 것
QEMU 사용자 공간이 패킷마다 I/O 를 처리하면 호스트 커널과 사용자 공간을 오가는 전환 비용이 쌓인다. 초당 지나는 패킷이 많아질수록 그 전환과 스케줄링, 복사, 알림 비용이 커질 수 있다. vhost-net 은 그 반복 경로를 호스트 커널 쪽으로 옮겨서 컨텍스트 스위치와 사용자 공간 오버헤드를 줄인다.
QEMU 가 장치를 만들고 관리하는 주체라는 것과, 흐르는 패킷을 전부 QEMU 가 처리한다는 것은 다른 말이다. CPU 가상화에서 같은 구분을 이미 한 번 한다. QEMU 가 vCPU 를 만들지만 게스트의 `ADD`, `MOV`, `SUB` 를 전부 QEMU 가 실행하지는 않는다. 그 명령은 KVM(Kernel-based Virtual Machine) 과 VMX(Virtual Machine Extensions) 가 실행한다. 네트워크에서도 QEMU 가 가상 NIC 를 만들지만, 패킷 100만 개를 반드시 QEMU 가 하나씩 처리할 필요는 없다.
vhost-net 을 써도 QEMU 는 계속 필요하다. 가상 머신 수명 주기와 가상 하드웨어 모델, virtio 장치 생성, 기능 협상, 큐 설정, 백엔드 연결, 장치 reset, 설정 처리가 QEMU 에 남는다. 그래서 vhost-net 은 QEMU 를 없애는 것이 아니라, QEMU 가 맡던 반복적인 virtio 데이터 경로의 상당 부분을 호스트 커널로 넘긴다고 읽는다.
두 경로에는 이름이 따로 있다. 자주 반복되는 패킷 전달과 데이터 전송이 fast path 다. 장치 초기화와 기능 협상, 큐 설정, 설정 변경, 장치 reset 처럼 드물게 일어나면서 설정과 예외 처리를 맡는 쪽이 control/slow path 다. QEMU 는 뒤쪽에서 계속 중요한 역할을 한다.
![두 줄이 나란히 내려오다 아래에서 만나는 구성도. 왼쪽 줄은 virsh 에서 libvirt 와 QEMU 를 지나 virtio-net Device Model 로 내려가는 control/setup path 이고 오른쪽 줄은 Physical NIC 에서 Physical NIC Driver 와 Linux Bridge, TAP 으로 내려가는 data path 다. Device Model 은 점선으로 vhost-net 과 QEMU virtio backend 를 설정하고 TAP 은 실선으로 그 둘 가운데 한쪽에 packet 을 넘긴다. 두 backend 는 다시 virtqueue 하나로 모여 Guest 로 올라간다.](../../../final/assets/diagrams/packet-control-and-data-paths/packet-control-and-data-paths.svg)
## 이 경로를 일반화할 때 어긋나는 것
경로를 한 줄로 굳혀 놓으면 틀리는 그림이 셋 있다. 첫째는 패킷이 vhost-net 다음에 QEMU 를 반드시 지나는 것처럼 그린 것이다.
```text label="이렇게 일반화하지 않는다"
TAP
vhost-net
QEMU
virtqueue
```
vhost-net 의 중요한 목적 하나가 데이터 경로에서 QEMU 사용자 공간을 우회하는 데 있다. 그래서 vhost-net 을 쓸 때의 fast path 는 TAP 에서 vhost-net 과 virtqueue 를 지나 게스트로 간다. QEMU 는 사라지지 않고 장치의 수명 주기와 설정을 관리한다.
둘째는 Bridge 를 지나는 프레임이 항상 호스트의 TCP/IP 스택을 거친다고 본 것이다. Bridge 가 L2 전달만 하는 경우 프레임이 호스트의 일반적인 L3 TCP/IP 스택을 반드시 거치지는 않는다. VM1 의 TAP 에서 Linux Bridge 를 지나 VM2 의 TAP 으로 가는 경로가 그렇다. 호스트가 라우팅이나 NAT, host-local termination, 방화벽 역할을 하면 그때 L3/Netfilter 경로가 개입한다. 그래서 물리 NIC 에서 호스트 TCP/IP 스택을 거쳐 Bridge 로 간다는 순서를 고정된 경로로 두지 않는다. 실제 경로는 브리지와 라우팅, NAT 구성에 따라 달라진다.
셋째는 호스트 물리 NIC 로 나갈 때 virtio 를 한 번 더 거친다고 본 것이다.
```text label="Host 물리 NIC 로 나가는 경로"
Guest
virtio-net
vhost-net
TAP
Linux Bridge
Intel NIC Driver
Intel Physical NIC
```
게스트 virtio 에서 호스트 virtio 를 거쳐 물리 NIC 로 가는 구조가 아니다. virtio 는 게스트의 가상 I/O 장치와 호스트 백엔드 사이의 인터페이스이고, 물리 NIC 로 나갈 때는 그 카드의 실제 드라이버를 쓴다.
## 복사와 알림, 큐 개수와 오프로드
네트워크 성능에서 큰 비용 하나가 패킷 데이터를 복사하는 일이다. virtio 와 virtqueue, vhost 구조는 버퍼 디스크립터를 써서 불필요한 복사와 컨텍스트 스위치를 줄이는 방향으로 설계돼 있다. 다만 이것을 항상 zero-copy 라고 일반화하지 않는다. 실제로 복사가 일어나는지는 커널 버전과 QEMU 버전, vhost 설정, 오프로드, NIC 의 기능, 패킷이 지나는 경로, GSO/GRO/TSO 에 따라 달라질 수 있다.
게스트와 호스트는 큐에 새 패킷이나 버퍼가 들어왔다는 것을 서로 알려야 한다. 게스트가 보낼 때는 virtqueue 에 디스크립터를 등록하고 호스트 백엔드에 알리면 백엔드가 처리하고, 받을 때는 호스트가 virtqueue 에 버퍼를 반영하고 게스트에 알리면 게스트 드라이버가 처리한다. 패킷마다 인터럽트와 알림이 지나치게 많이 발생하면 오버헤드가 커지기 때문에 batching 과 interrupt moderation, queueing 을 쓴다.
큐를 하나만 쓰면 특정 vCPU 나 처리 경로에 부하가 몰릴 수 있다. virtio-net 은 multi-queue 를 쓸 수 있고, RX 큐를 vCPU 마다 하나씩 붙여 패킷 처리를 병렬로 돌리고 큐 하나에 몰리는 병목을 완화한다. 효과는 부하의 성격과 CPU affinity, IRQ(Interrupt Request) 배치, 큐 설정에 따라 달라진다.
오프로드는 작은 패킷을 하나씩 처리하는 CPU 부담과 나누고 합치는 비용을 줄인다. TSO(TCP Segmentation Offload)와 GSO(Generic Segmentation Offload), GRO(Generic Receive Offload), 그리고 checksum offload 가 대표적이다. 오프로드가 켜져 있으면 `tcpdump` 에서 보이는 패킷 크기나 checksum 이 실제 wire 에서 보이는 것과 다르게 보일 수 있다.
## 이 구조에서 성능이 갈리는 두 조건
초당 지나는 패킷이 많은데 QEMU 사용자 공간이 데이터 경로를 직접 처리하면 CPU 부담이 커질 수 있다. 그때는 아래 다섯 가지를 함께 본다.
```text label="vhost-net 미사용 또는 비효율적 datapath 일 때 관찰"
QEMU CPU usage
vhost thread
packet rate
latency
context switch
```
큐 하나나 vCPU 하나에 패킷 처리가 몰릴 수도 있다. 이때는 큐 개수와 분산 설정을 확인 대상으로 둔다.
```text label="single queue bottleneck 일 때 확인 대상"
virtio multi-queue
IRQ distribution
per-vCPU CPU usage
RSS/RPS/XPS
```
두 조건 모두 호스트 CPU 를 함께 쓴다. vhost-net 과 QEMU 스레드, softirq 가 호스트 CPU 를 쓰기 때문에, 네트워크 문제처럼 보이는 지연이 실은 CPU 스케줄링 문제일 수도 있다.
## 호스트에서 이 경로를 확인하는 명령
여기까지가 구조이고, 이 호스트가 실제로 그 구조인지는 명령으로 하나씩 확인한다. 물리 NIC 와 Bridge 는 인터페이스 목록과 전달 상태로 본다.
```bash label="Physical NIC 와 Linux Bridge"
ip link
ip addr
ethtool <interface>
ip link show type bridge
bridge link
bridge fdb show
```
TAP 과 가상 머신의 NIC 연결, libvirt 가상 네트워크는 libvirt 쪽에서 확인한다. `virsh domiflist` 가 어느 가상 머신이 어느 TAP 에 붙어 있는지 알려 주고, `virsh net-dumpxml` 이 그 네트워크가 어떤 구성인지 알려 준다.
```bash label="TAP 과 libvirt VM NIC · 가상 network"
ip tuntap show
virsh domiflist <domain>
virsh net-list --all
virsh net-info <network>
virsh net-dumpxml <network>
```
라우팅과 게스트 안쪽, 그리고 virtio 장치와 vhost 모듈이 실제로 올라와 있는지는 각각 따로 본다.
```bash label="Routing · Guest NIC · virtio 와 vhost 모듈"
ip route
ip rule
ip neigh
lspci
lsmod | grep virtio
lsmod | grep vhost
```
경로 위의 어느 지점까지 패킷이 보이는지는 계층마다 캡처해서 가른다.
```bash label="계층마다 packet 이 보이는지"
sudo tcpdump -ni <physical-nic>
sudo tcpdump -ni <bridge>
sudo tcpdump -ni <tap-or-vnet>
sudo tcpdump -ni <guest-interface>
```
## 이 문서가 확인하지 않은 것
이 호스트에서 잰 값은 하나도 없다. 위의 명령들은 무엇을 볼 수 있는지 적어 둔 목록이고 아직 실행하지 않았다. 이 가상 머신들의 네트워크가 Bridge 인지 NAT 인지 라우팅인지, VM1 과 VM2 의 TAP 또는 vnet 인터페이스가 무엇인지를 아직 확인하지 않았다. vhost-net 이 실제로 데이터 경로를 맡고 있는지, multi-queue 가 켜져 있는지도 마찬가지다. 그래서 이 글은 「이 구조에서는 이렇게 동작한다」까지만 말하고 「이 서버가 그 구조다」라고는 말하지 않는다. QEMU 백엔드와 vhost-net 의 성능 차이가 이 호스트에서 관찰되는지, Keycloak 부하 시험에서 네트워크 가상화가 지연에 영향을 줄 만큼 호스트 CPU 를 쓰는지도 같은 상태다.
이 문서가 Keycloak 테스트 환경에 얹어 그린 경로도 마찬가지다. 호스트 Nginx 아래에 가상 머신 두 대가 있고 그 안에서 K3s 와 Keycloak 노드가 돈다는 앞부분은 실험 구성으로 밝혀 두었다. 그 아래를 브리지와 라우팅·NAT, TAP, vhost-net, virtqueue 로 펼친 뒷부분은 네트워크 구성과 TAP 이름, vhost-net 사용 여부를 확인하기 전의 가정이다. 이 환경이 실제로 그렇다는 주장이어서, 재기 전에는 이 글의 그림으로 올리지 않고 근거가 모자란 후보로 남겼다.
여기 적은 것은 가장 기본적인 조합 안에서만 성립한다. 실제 환경은 Bridge 와 NAT, routed network, macvtap, SR-IOV, VFIO(Virtual Function I/O) passthrough, Open vSwitch, Kubernetes CNI(Container Network Interface) 에 따라 달라질 수 있다. 이 문서에 커널과 QEMU, libvirt 버전이 한 번도 적혀 있지 않아서 특정 버전에 고정하지도 못한다. 복사 동작 하나만 봐도 커널 버전과 QEMU 버전, vhost 설정, 오프로드, NIC 의 기능에 따라 달라지므로, 버전이 바뀌어 이 서술이 낡았는지는 위 확인 명령을 실제 호스트에서 돌릴 때 드러난다.
가상 머신 위에 올린 K3s 안쪽도 이 문서 밖이다. K3s 의 CNI 와 Service, Pod 네트워크는 이 네트워크 가상화 위에 더해지는 계층이라 따로 분석한다.
확인 순서는 물리 NIC 에서 시작한다. 그다음 libvirt 가상 네트워크와 Bridge·NAT·Route 구성, 가상 머신별 TAP 또는 vnet, virtio-net 장치, vhost-net 사용 여부, 게스트 NIC 와 route 를 차례로 본다. 이어서 호스트 Nginx 에서 가상 머신까지 패킷이 지나는 길을 `tcpdump` 로 추적하고, VM1 과 VM2 사이의 경로와 Keycloak 요청 때의 패킷 흐름을 확인한다. 마지막으로 부하를 걸었을 때 QEMU 와 vhost 의 CPU 사용량을 비교하고 multi-queue 와 오프로드 설정을 본다. 검증되지 않은 항목은 열린 질문으로 두고, 실제 결과가 나오면 재현 조건을 붙인 기록으로 옮긴다.
<!-- body:end -->
@@ -0,0 +1,110 @@
---
id: 600a2621-f6d8-42df-9629-7db65011545d
kind: QUESTION
slug: actual-packet-path-nginx-to-keycloak
title: Host Nginx 에서 Keycloak 까지 packet 은 실제로 어디를 지나는가
topic: network-virtualization
topicName: 네트워크 가상화
project: virtualization
status: 초안
questionStatus: OPEN
studio: "https://hyeonworks.com/studio/documents/600a2621-f6d8-42df-9629-7db65011545d/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#122-open-question-oq-6
- final/document.md#116-현재-keycloak-k3s-테스트-환경과-연결
- final/document.md#119-실제-packet-path-추적
- final/document.md#94-전체-네트워크-계층
- final/document.md#125-최종-기준-구조
---
# Host Nginx 에서 Keycloak 까지 packet 은 실제로 어디를 지나는가
§116 은 이 테스트 환경의 요청 경로를 클라이언트에서 Keycloak 까지 한 줄로 그렸다. 가상 머신 네트워크까지 펼치면 물리 NIC 에서 브리지와 TAP 을 지나 vhost-net 과 virtqueue 를 거쳐 게스트 안으로 들어간다고 적었다. 그 펼친 그림은 §94 가 기준으로 삼은 구조를 이 환경에 얹은 것이고, 이 호스트에서 패킷을 잡아 본 결과가 아니다. 여기서 묻는 것은 호스트 Nginx 를 지난 요청이 실제로 어느 브리지와 어느 TAP 을 거쳐 어느 가상 머신으로 들어가는가 하나다.
## 관계
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
§94 와 §125 가 그린 기준 경로를 이 개념이 설명한다. 이 물음은 그 경로가 이 호스트에서도 같은지를 묻는다.
- **packet 이 어디서 끊겼는지는 계층마다 capture 해서 가른다**
§119 가 든 계층별 캡처 방법을 규칙으로 편 기준이다. 요청이 중간에서 끊기면 그쪽이 받는다.
- **이 호스트의 가상 머신 네트워크는 Bridge 인가 NAT 인가 Routing 인가**
구성이 셋 중 무엇이냐에 따라 캡처를 걸 지점이 달라진다.
- **VM1 과 VM2 의 TAP/vnet interface 는 무엇이고 어디에 붙어 있는가**
tcpdump 를 걸 인터페이스의 이름을 그쪽이 댄다.
- **Keycloak 실험 결과를 애플리케이션 원인으로 읽기 전에 network 계층을 따로 검증한다**
§120 이 요구한 별도 검증에서 이 경로 확인이 첫 항목이 된다.
## 사실
- §116 이 그린 테스트 환경의 요청 경로
Client → Host Physical NIC → Host Nginx → Host Network → VM1 / VM2 → K3s → Keycloak Node 1 / 2
- §116 이 가상 머신 네트워크까지 펼친 경로
Client → Physical NIC → Host Network Stack / Bridge / Route / NAT → TAP(vm1) / TAP(vm2) → vhost-net → virtqueue → virtio-net → Guest Network Stack → K3s networking → Keycloak
- §116 은 그 뒤에 K3s 내부의 CNI · Service · Pod network 가 추가되므로 별도 계층으로 분석한다고 적었다.
- §94 는 이 문서가 기준으로 삼은 구조를 virtio-net + vhost-net + TAP + Linux Bridge 로 놓고 수신 방향과 송신 방향을 각각 그렸으며, 실제 환경이 Bridge · NAT · Routed Network · macvtap · SR-IOV · VFIO passthrough · Open vSwitch · Kubernetes CNI 에 따라 달라질 수 있다고 밝혔다.
- §125 는 같은 경로를 vhost-net 을 쓸 때와 QEMU backend 를 쓸 때로 나눠 다시 그렸다. 두 그림은 TAP 다음이 vhost-net 인지 QEMU virtio backend 인지에서 갈리고 나머지 구간은 같다.
- §119 는 경로를 확인하는 방법으로 호스트의 physical NIC · bridge · tap 또는 vnet 세 곳과 게스트의 guest interface 한 곳에 sudo tcpdump -ni 를 걸고, 어디까지 보이는지로 의심 구간을 좁히라고 적었다.
- §119 가 든 판정 예 셋
Physical NIC O · Bridge O · TAP X : Guest 내부보다 먼저 Host Bridge/TAP mapping 을 의심한다
TAP O · Guest NIC X : virtio/vhost/Guest NIC 계층을 의심한다
Guest NIC O · Socket X : Guest routing/firewall/listen 상태를 의심한다
- §122 OQ-6 은 Host NIC · Bridge · TAP · Guest NIC 에서 tcpdump 로 추적한다고만 적었다. 어느 이름의 인터페이스인지는 적혀 있지 않다.
- §116 의 그림은 앞부분과 뒷부분의 근거가 다르다. 호스트 Nginx 아래에 가상 머신 두 대가 있고 그 안에서 K3s 와 Keycloak 노드가 돈다는 앞부분은 §89 가 밝힌 실험 구성이고, 브리지와 라우팅·NAT, TAP, vhost-net 으로 펼친 뒷부분은 OQ-1 과 OQ-2, OQ-3 를 확인하기 전의 가정이다.
- 이 호스트에서 패킷을 잡아 본 기록이 없다. §116 이 펼친 경로는 확인한 결과가 아니라 §94 의 기준 구조를 이 환경에 얹은 그림이다.
## 가정
- 호스트 Nginx 를 통해 Keycloak 요청을 한 번 보낼 수 있고, 그 요청이 두 가상 머신 가운데 어느 쪽으로 갔는지 확인하는 쪽이 알 수 있다고 본다.
- 네 지점에 동시에 캡처를 걸어 둘 수 있다고 전제한다. 지점마다 따로 걸면 같은 요청을 본 것인지 갈리지 않는다.
- §116 이 그린 구조가 지금 실험 환경과 같다고 전제한다. 가상 머신이 둘이고 각각 K3s 노드와 Keycloak 을 돌린다는 것까지가 근거 문서에 적힌 전부다.
- 오프로드 설정이 캡처하는 동안 바뀌지 않는다.
## 미지수
- 호스트 Nginx 를 지난 요청이 어느 브리지를 거치는지, 그 브리지에서 어느 TAP 으로 나가는지.
- 그 TAP 이 두 가상 머신 가운데 어느 쪽의 인터페이스인지.
- 게스트 안의 인터페이스에서 같은 요청이 보이는지.
- 네 지점의 결과가 §116 이 펼친 그림과 같은지, 다르다면 어느 지점부터 다른지.
- 지금 오프로드 설정이 무엇인지. 캡처에서 본 패킷 크기와 체크섬을 wire 값으로 읽어도 되는지가 이것으로 갈린다.
## 제약
- 캡처 지점의 이름을 먼저 확정해야 한다. 어느 브리지와 어느 TAP 인지 모르면 tcpdump 를 걸 곳을 고르지 못한다. 그 이름은 네트워크 구성을 묻는 물음과 인터페이스를 묻는 물음이 댄다.
- K3s 안에서 Keycloak Pod 까지 가는 구간은 이 물음이 닫는 범위 밖이다. §116 이 CNI · Service · Pod network 를 별도 계층으로 미뤄 두었다.
- 요청이 중간 지점에서 끊기면 그것은 이 물음의 답이 아니라 장애다. §119 가 든 세 판정 예를 따라 의심 구간을 적고 계층별 캡처 기준으로 넘긴다.
- 이 호스트에서 잰 값이 없어 §116 의 그림을 확인된 경로로 삼지 않는다. 이 환경이 실제로 그렇다는 주장이라 재기 전에는 Case 도 Concept 도 아니라고 보고 근거가 모자란 후보로 남겨 두었으며, OQ-1 과 OQ-2, OQ-3, OQ-6 이 답하면 다시 판정한다.
## 선택지
### 1. 네 지점에 한 번에 걸고 요청을 한 번 보낸다
§119 가 든 호스트 세 지점과 게스트 한 지점에 동시에 tcpdump 를 걸어 두고, 호스트 Nginx 를 통해 Keycloak 요청을 한 번 보낸다. 같은 요청 하나가 네 지점에서 각각 보이는지로 경로가 확정되기 때문에 실행이 한 번으로 끝난다.
지점 이름을 미리 확정해 두어야 하고 네 곳을 동시에 열어 두어야 한다.
### 2. 바깥에서 안쪽으로 한 지점씩 옮겨 간다
물리 NIC 에서 시작해 브리지, TAP, 게스트 인터페이스 순으로 한 지점씩 옮기며 같은 요청을 반복해서 보낸다. 여러 곳을 동시에 열지 않아도 되고 어디서부터 안 보이는지가 바로 드러난다.
요청을 여러 번 보내야 해서 매번 같은 경로로 갔다고 전제해야 한다. 두 가상 머신에 요청이 번갈아 가면 그 전제가 깨진다.
### 3. 두 가상 머신 쪽 TAP 을 모두 열어 놓고 한 번 보낸다
VM1 과 VM2 의 TAP 을 둘 다 열어 두면 요청이 어느 쪽으로 갔는지까지 같은 실행에서 나온다. §120 이 든 「Node1 요청만 지연」 같은 증상을 나중에 볼 때 어느 경로를 먼저 열어야 하는지도 이 기록에서 나온다.
캡처 지점이 다섯으로 늘어난다.
### 4. 제외 — 호스트 Nginx 설정에서 경로를 읽는다
Nginx 가 어느 주소로 요청을 넘기는지 설정만 읽어도 어느 가상 머신으로 가는지는 알 수 있다. 그러나 이 물음은 설정이 말하는 경로가 아니라 패킷이 실제로 지나는 계층을 묻는다. §116 이 그린 그림이 확인된 것이 아니라는 데서 물음이 시작했으므로, 설정을 읽으면 같은 종류의 근거가 하나 더 늘어난다.
## 다음 검증
1. 캡처를 걸 지점의 이름을 먼저 확정한다. 어느 브리지와 어느 TAP 인지는 네트워크 구성을 묻는 물음과 인터페이스를 묻는 물음이 답한다.
2. 호스트에서 sudo tcpdump -ni 뒤에 physical NIC 이름 · bridge 이름 · tap 또는 vnet 이름을 넣어 세 곳에 걸고, 게스트에서 같은 명령을 guest interface 이름에 건다 (§122 OQ-6 · §119).
3. 호스트 Nginx 를 통해 Keycloak 요청을 한 번 보낸다.
4. 네 지점에서 그 요청이 보였는지를 §119 처럼 O 와 X 로 적는다.
5. 캡처하는 동안의 오프로드 상태를 함께 적는다. §113 은 오프로드가 켜져 있으면 tcpdump 에서 보이는 패킷 크기나 체크섬이 실제 wire 에서 보이는 것과 다르게 보일 수 있다고 적었다.
닫는 조건 : 네 지점의 캡처 결과로 실제 경로를 한 줄로 적으면 닫는다. §116 이 펼친 그림과 같으면 그 그림을 이 호스트에서 확인된 경로로 올리고, §116 을 근거로 삼는 후보를 다시 판정한다. 다르면 어느 지점부터 다른지를 적고 §119 의 판정 예대로 그 구간을 의심 구간으로 넘긴다. 요청이 중간 지점에서 끊기면 그것은 이 물음의 답이 아니므로 계층별 캡처 기준으로 넘긴다.
@@ -0,0 +1,101 @@
---
id: 484fd832-c7d3-413e-897a-97892e4e10ac
kind: QUESTION
slug: is-vhost-net-actually-in-use
title: 이 호스트에서 vhost-net 이 실제로 packet datapath 를 맡고 있는가
topic: network-virtualization
topicName: 네트워크 가상화
project: virtualization
status: 초안
questionStatus: OPEN
studio: "https://hyeonworks.com/studio/documents/484fd832-c7d3-413e-897a-97892e4e10ac/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#122-open-question-oq-3
- final/document.md#103-qemu-virtio-device-model의-역할
- final/document.md#104-왜-tap-→-vhost-net-→-qemu-→-virtqueue-라고-일반화하면-안-되는가
- final/document.md#106-qemu가-userspace인데-packet이-qemu를-안-거칠-수-있는-이유
- final/document.md#107-vhost-net-최적화
- final/document.md#108-vhost-net은-qemu를-제거하지-않는다
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-4
---
# 이 호스트에서 vhost-net 이 실제로 packet datapath 를 맡고 있는가
§103 은 패킷이 TAP 에서 게스트로 올라오는 길을 두 가지로 갈라 적었다. QEMU 백엔드를 직접 쓰면 QEMU virtio backend 가 그 사이에 들어가고, vhost-net 을 쓰면 호스트 커널의 vhost-net 이 들어간다. 둘 중 어느 쪽이 이 호스트에서 돌고 있는지는 SSOT 에 없어서, 이 물음은 가상 머신 두 대 각각의 데이터 경로 백엔드를 확정한다.
## 관계
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
그 경로 그림이 vhost-net 을 쓰는 구성을 기준으로 그려져 있어서, 이 호스트가 그 기준에 해당하는지를 이 물음이 정한다.
- **QEMU backend 와 vhost-net 의 차이가 이 호스트에서 실제로 보이는가**
비교하려면 지금 어느 백엔드로 돌고 있는지가 먼저 정해져야 한다.
- **부하 중 network 가상화가 지연을 바꿀 만큼 호스트 CPU 를 쓰는가**
§117.4 가 든 CPU 오버헤드는 QEMU 사용자 공간이 데이터 경로를 직접 처리할 때의 이야기다.
- **Host Nginx 에서 Keycloak 까지 packet 은 실제로 어디를 지나는가**
TAP 과 virtqueue 사이에 무엇이 있는지가 그 경로 서술의 한 칸을 채운다.
## 사실
- §103 은 QEMU 의 virtio Device Model 이 호스트 사용자 공간의 QEMU 프로세스 안에 있다고 적고, 그 역할을 둘로 나눴다. 하나는 장치 생성/설정/관리이고 다른 하나는 실제로 패킷을 나르는 처리다.
- §103 이 적은 데이터 경로 두 가지는 이렇게 갈린다.
QEMU backend 를 직접 쓰는 경우 : TAP → QEMU virtio backend → virtqueue → Guest
vhost-net 을 쓰는 경우 : TAP → vhost-net → virtqueue → Guest
- §104 는 TAP → vhost-net → QEMU → virtqueue 를 일반적인 경로로 그리면 안 된다고 못 박았다. vhost-net 의 목적 하나가 데이터 경로에서 QEMU 사용자 공간을 우회하는 것이기 때문이다.
- §106 은 장치를 만들고 관리하는 주체와 실제로 패킷을 나르는 주체가 다르다는 것을 CPU 가상화에 견주어 적었다. QEMU 가 vCPU 를 만들어도 게스트의 ADD · MOV · SUB 를 전부 QEMU 가 실행하지는 않는다.
- §107 은 QEMU 사용자 공간이 패킷마다 I/O 를 처리하면 호스트 커널과 사용자 공간 사이의 전환이 쌓이고, 초당 지나는 패킷이 많아질수록 userspace/kernel transition · scheduling · copy · notification 비용이 커질 수 있다고 적었다.
- §108 은 vhost-net 을 써도 QEMU 가 남아서 맡는 일을 열거했다.
VM lifecycle · Virtual hardware model · virtio device 생성
Feature negotiation · Queue configuration · Backend 연결
Device reset · Control/configuration handling
- §117.4 는 초당 지나는 패킷이 많은데 QEMU 사용자 공간이 데이터 경로를 직접 처리하면 CPU 오버헤드가 커질 수 있다고 밝히고, 관찰 대상으로 QEMU CPU usage · vhost thread · packet rate · latency · context switch 를 들었다.
- 확인 명령으로 §122 OQ-3 과 §118 이 든 것은 lsmod | grep vhost 하나다. §122 OQ-3 은 그 뒤에 QEMU arguments 와 libvirt domain XML 을 추가로 확인한다고 적었지만, 그 둘을 읽는 명령은 제3부 어디에도 적혀 있지 않다.
- §123 은 개념에서 열린 질문을 거쳐 Case 로 가는 순서를 설명하면서 이 물음을 예로 들었다. 개념 자리에 「vhost-net은 QEMU userspace를 우회해 packet datapath를 처리할 수 있다」를, 물음 자리에 「현재 테스트 Host에서 vhost-net이 실제 활성화되어 있는가?」를 놓고, 답이 나오면 「libvirt/QEMU virtio-net backend 구성 확인 및 vhost-net 사용 검증」이 Case 가 된다고 적었다.
- 이 호스트의 가상 머신 두 대가 어느 백엔드로 도는지, vhost 모듈이 올라와 있는지를 확인한 기록은 SSOT 에 없다.
## 가정
- 호스트에 붙어 lsmod 를 실행하고 libvirt 설정을 읽을 수 있다고 본다.
- 모듈이 올라와 있다는 것과 그 가상 머신이 그 백엔드를 쓴다는 것을 다른 사실로 놓고 물음을 세웠다. §103 은 백엔드 선택을 가상 머신의 구성으로 적었지 모듈 적재로 적지 않았다.
- 두 가상 머신이 같은 백엔드를 쓴다고 전제하지 않는다. 가상 머신마다 따로 읽어 각각 적는다.
- 읽는 동안 가상 머신을 재시작하지 않는다고 전제한다. 백엔드는 §105 가 적은 설정 경로에서 정해지므로 재시작하면 달라질 수 있다.
## 미지수
- 이 호스트에 vhost 커널 모듈이 올라와 있는지.
- 두 가상 머신의 libvirt domain 설정과 QEMU arguments 가 vhost 백엔드를 쓰도록 되어 있는지.
- 그래서 지금 흐르는 패킷이 §103 이 그린 두 경로 가운데 어느 쪽으로 가는지.
- QEMU arguments 와 libvirt domain XML 을 이 환경에서 어떤 명령으로 읽는지. 제3부가 그 명령을 적지 않아 실행하는 쪽이 정한다.
## 제약
- 백엔드가 무엇인지를 확정하는 데서 끊는다. 두 백엔드의 성능 차이는 그것을 비교하는 물음이 받는다.
- 이 호스트에서 읽은 설정이 없어 §94 와 §125 가 기준으로 삼은 vhost-net 구성을 이 호스트의 값으로 쓰지 않는다.
- 명령을 SSOT 가 하나만 주었으므로, 나머지 둘을 무엇으로 읽었는지 실행한 명령과 출력을 함께 남긴다.
## 선택지
### 1. 모듈 적재부터 보고 가상 머신 설정으로 좁힌다
lsmod | grep vhost 로 호스트에 그 기능이 있는지 먼저 보고, 나오면 가상 머신마다 설정을 읽어 실제로 쓰는지 좁힌다. 모듈이 아예 없으면 두 대 모두 QEMU 백엔드라는 답이 한 번에 나온다.
모듈이 있을 때는 이 명령만으로 아무것도 정해지지 않아서 결국 가상 머신 설정을 읽어야 한다.
### 2. 가상 머신 설정부터 읽고 모듈 적재로 뒷받침한다
두 가상 머신의 libvirt domain 설정과 QEMU arguments 에서 백엔드 지정을 먼저 읽고, lsmod 출력으로 그 설정이 실제로 성립할 수 있는지 뒷받침한다. 가상 머신마다 다른 답이 나오는 경우까지 한 번에 잡힌다.
설정을 읽는 명령을 SSOT 가 주지 않아서 무엇으로 읽었는지 따로 적어야 한다.
### 3. §117.4 의 관찰 대상으로 되짚는다 — 제외
호스트에 vhost 스레드가 보이는지, 부하 중 QEMU CPU 가 어떻게 움직이는지로 백엔드를 역추정하자는 방법이다. 그 관찰은 부하를 걸어야 값이 생기고, §117.4 는 그 목록을 성능 문제를 진단할 때 볼 것으로 적었다. 지금 필요한 것은 설정이 무엇으로 되어 있는가 하나다. 그것은 부하 없이 읽힌다.
## 다음 검증
1. lsmod | grep vhost 로 vhost 커널 모듈이 올라와 있는지 본다.
2. 두 가상 머신의 libvirt domain 설정에서 인터페이스의 driver 지정을 읽는다. 무엇으로 읽었는지 명령을 함께 적는다.
3. 같은 가상 머신의 QEMU 실행 인자에서 백엔드 지정을 읽는다. 이것도 명령을 함께 적는다.
4. 세 출력을 가상 머신별로 짝지어 적고 실행한 명령과 함께 증거로 남긴다.
닫는 조건 : 가상 머신 두 대 각각의 데이터 경로 백엔드가 vhost-net 인지 QEMU 사용자 공간인지 설정으로 확정되면 닫는다. vhost-net 이면 §125 의 「Data Path - vhost-net 사용」 이 이 호스트의 경로라고 적고, QEMU 백엔드면 「Data Path - QEMU backend 사용」 쪽이라고 적는다. 어느 쪽이든 그 결과가 QEMU backend 와 vhost-net 을 견주는 물음의 비교 대상 하나를 고정한다. 두 대가 서로 다르게 나오면 그 사실을 적고, 이후 측정에서 둘을 같은 조건으로 묶지 않는다.
@@ -0,0 +1,115 @@
---
id: 79ac61d6-cbe6-483b-9961-02ca8c02b5c2
kind: QUESTION
slug: network-virtualization-cpu-cost-under-load
title: 부하 중 network 가상화가 지연을 바꿀 만큼 호스트 CPU 를 쓰는가
topic: network-virtualization
topicName: 네트워크 가상화
project: virtualization
status: 초안
questionStatus: OPEN
studio: "https://hyeonworks.com/studio/documents/79ac61d6-cbe6-483b-9961-02ca8c02b5c2/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#122-open-question-oq-7
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-7
- final/document.md#107-vhost-net-최적화
- final/document.md#111-interrupt-notification-최적화
- final/document.md#120-keycloak-refresh-token-실험과의-관계
---
# 부하 중 network 가상화가 지연을 바꿀 만큼 호스트 CPU 를 쓰는가
§117.7 은 vhost-net 과 QEMU 스레드와 softirq 도 호스트 CPU 를 쓰므로, 네트워크 문제처럼 보이는 것이 CPU 스케줄링 문제일 수 있다고 적었다. 그 셋 가운데 QEMU 스레드와 게스트 쪽 지표는 CPU 가상화를 다루는 물음 둘이 이미 같은 실험 구간에서 재기로 해 두었다. 그래서 여기서는 그 둘이 보지 않는 vhost 커널 스레드와 softirq 만 본다. Keycloak 부하 시험 구간에서 이 둘이 호스트 CPU 를 얼마나 쓰는지, 그 사용량이 네트워크 지연과 같이 움직이는지로 물음을 좁힌다.
## 관계
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
vhost-net 이 데이터 경로의 어느 구간을 맡는지를 이 개념이 설명한다. 여기서 재려는 커널 스레드가 그 구간을 돌린다.
- **Keycloak 실험 결과를 애플리케이션 원인으로 읽기 전에 network 계층을 따로 검증한다**
§120 이 요구한 별도 검증을 규칙으로 편 기준이다. 이 측정이 없으면 그 검증을 마쳤다고 적을 수 없다.
- **QEMU backend 와 vhost-net 의 차이가 이 호스트에서 실제로 보이는가**
백엔드를 바꿔 견주는 물음이라 §122 가 든 관찰 대상을 상당 부분 함께 쓴다. 여기서 찍은 기준값을 그쪽이 그대로 받는다.
- **이 가상 머신들의 virtio-net 에 multi-queue 가 켜져 있는가**
큐가 하나로 나오면 §117.5 가 든 쏠림이 이 측정의 vCPU 별 사용량에서 드러난다.
- **Keycloak 동시 refresh 실험 중 CPU 가상화 계층이 결과를 흔들 만큼 포화되는가**
Guest steal time 과 QEMU vCPU 스레드는 그쪽이 잰다. 같은 실험 구간에서 두 기록을 함께 남기고 여기서는 vhost 커널 스레드와 softirq 만 새로 잰다.
## 사실
- §117.7 은 vhost-net · QEMU thread · softirq 도 호스트 CPU 를 쓰고, 따라서 네트워크 문제처럼 보여도 CPU 스케줄링 문제일 수 있다고 적었다.
- §107 은 QEMU 사용자 공간이 패킷마다 I/O 를 처리하면 호스트 커널과 사용자 공간을 오가는 전환 비용이 쌓이고, 초당 지나는 패킷이 많아질수록 userspace/kernel transition · scheduling · copy · notification 비용이 커질 수 있다고 밝혔다.
- §111 은 게스트와 호스트가 큐에 새 패킷이나 버퍼가 있음을 서로 알려야 한다고 적고, 패킷마다 인터럽트나 알림이 지나치게 많이 발생하면 오버헤드가 커지므로 batching · interrupt moderation · queueing 이 중요하다고 밝혔다.
- §120 은 Refresh Token 경쟁 자체가 virtio-net 문제는 아니지만 클라이언트부터 PostgreSQL/Redis 까지 같은 경로를 공유하므로 네트워크 경로를 별도로 검증한다고 적었다.
- §120 이 Refresh Token 경쟁이나 DB lock 으로 오해할 수 있다고 든 다섯
Node1 요청만 지연
VM2 packet loss
Host bridge misconfiguration
NAT/conntrack issue
Host CPU contention으로 vhost 처리 지연
- §122 OQ-7 이 든 관찰 대상 여섯
QEMU CPU
vhost thread
softirq
Host CPU
Guest CPU
network latency
- 이 여섯 가운데 QEMU CPU 와 Guest CPU 는 CPU 가상화 쪽 물음이 같은 실험 구간에서 이미 잰다. §14.7 이 호스트 스레드를 보는 명령으로 적은 ps -eLo pid,tid,psr,pcpu,comm | grep qemu 는 이름이 qemu 인 스레드만 걸러 내므로 vhost 커널 스레드는 그 출력에 나오지 않는다.
- softirq 시간을 읽는 명령이 근거 문서에 없다. §118 의 네트워크 확인 명령 목록에도, CPU 관측 명령을 모아 둔 §14 에도 softirq 항목이 없고 이 낱말은 §117.7 과 §122 OQ-7 두 곳에만 나온다.
- §108 은 vhost-net 을 써도 QEMU 가 VM lifecycle · virtio device 생성 · feature negotiation · queue configuration · backend 연결 · device reset 을 계속 맡는다고 적었다.
- 이 호스트에서 부하 구간의 vhost 스레드 CPU 사용량이나 softirq 시간을 잰 기록이 없다.
## 가정
- Keycloak 부하 시험을 돌릴 수 있고, 부하 구간과 그 직전 구간을 나눠 기록할 수 있다고 본다.
- 호스트에서 vhost 커널 스레드를 스레드 단위로 구분해 볼 수 있다고 전제한다. §108 이 vhost-net 을 써도 QEMU 가 설정과 수명 주기를 계속 맡는다고 적었으므로, QEMU 프로세스의 CPU 사용량과 vhost 커널 스레드의 CPU 사용량을 따로 세야 한다.
- 부하 도구가 네트워크 지연을 이미 내고 있다고 전제한다. 그 도구가 어떤 값을 어떤 주기로 내는지는 근거 문서에 적혀 있지 않다.
- 호스트와 게스트 둘의 시각을 맞춰 읽을 수 있다. 시계가 어긋나면 세 값을 같은 부하 구간에 겹쳐 놓지 못한다.
- 호스트 쪽 Nginx 도 같은 물리 CPU 를 쓴다. 부하 구간의 호스트 CPU 상승을 전부 네트워크 가상화 몫으로 읽으면 이 전제가 깨진다.
## 미지수
- 부하를 걸기 전 vhost 커널 스레드의 CPU 사용량과 softirq 시간.
- 부하 구간에서 그 둘이 얼마나 오르는지.
- 오른 몫이 QEMU vCPU 스레드 사용량과 어떻게 나뉘는지.
- vhost 커널 스레드와 softirq 의 움직임이 네트워크 지연과 같은 시간축에서 함께 움직이는지.
- 두 지표가 얼마나 움직여야 실험 결과를 다르게 읽어야 하는지. 그 문턱을 아직 정하지 않았다.
- softirq 시간을 이 환경에서 어떤 도구로 읽는지. 근거 문서가 명령을 적지 않아 실행하는 쪽이 정한다.
## 제약
- 실험 조건을 바꾸지 않고 관찰만 덧붙인다. CPU 가상화 쪽 물음이 같은 실험 구간을 쓰기로 되어 있어 부하 수준을 바꾸면 두 기록을 겹쳐 읽지 못한다.
- 기준값을 먼저 찍는다. 부하 구간의 값만 있으면 그것이 평소 값인지 부하 때문에 오른 값인지 판정할 수 없다.
- 이 물음이 새로 만드는 값은 vhost 커널 스레드 사용량과 softirq 시간 둘이다. QEMU CPU 와 Guest CPU 와 steal time 은 CPU 가상화 쪽 기록을 그대로 쓰고, 네트워크 지연은 부하 도구가 낸 값을 쓴다.
- 두 지표가 움직이지 않았다는 결과가 나와도 Refresh Token 실험의 결론이 바뀌지는 않는다. §120 이 Refresh Token 경쟁과 네트워크 가상화를 별개 문제로 놓았기 때문에, 여기서 갈리는 것은 그 실험 결과를 애플리케이션과 저장소 쪽으로 읽어도 되는지 하나다.
- 이 호스트에서 잰 값이 없어 다른 장비의 수치를 근거로 삼지 않는다.
## 선택지
### 1. 실험을 그대로 두고 vhost 스레드와 softirq 만 덧붙여 잰다
CPU 가상화 쪽 물음이 이미 같은 구간에서 Guest steal time 과 QEMU vCPU 스레드를 기록하므로, 여기서는 그 기록에 없는 둘을 같은 시각에 붙인다. 실험 조건을 건드리지 않아서 두 기록을 겹쳐 읽을 수 있다.
이 방법으로는 이번 부하 수준에서 두 지표가 움직였는지 하나만 알 수 있다.
### 2. 부하 직전 구간을 기준값으로 따로 찍는다
부하를 걸기 전에 §122 OQ-7 의 여섯을 한 번 찍어 두면 부하 구간의 값을 견줄 대상이 생긴다. 기준값 없이 부하 구간만 찍으면 vhost 스레드 사용량이 어떤 값으로 나오든 그것이 평소 값인지 부하 때문에 오른 값인지 판정하지 못한다.
### 3. network latency 를 같은 시간축에 올려 함께 본다
vhost 스레드와 softirq 가 올라도 지연이 그대로면 이 부하 수준에서는 지연을 바꾸지 않았다고 적을 수 있다. §122 OQ-7 이 network latency 를 여섯째 관찰 대상으로 둔 이유가 여기에 있다. 대신 호스트와 부하 도구의 시각을 맞춰야 세 값을 겹쳐 놓을 수 있다.
### 4. 제외 — 초당 패킷 수를 올려 vhost 처리를 포화시켜 견준다
§107 은 초당 지나는 패킷이 많아질수록 전환과 복사와 알림 비용이 커질 수 있다고 적었으므로, 그 수를 올리면 vhost 처리 비용이 더 잘 드러난다. 그러나 이 물음은 Keycloak 부하 시험 구간에서 네트워크 가상화가 결과를 흔들었는지를 묻는 것이라, 부하를 다르게 걸고 잰 값은 그 답이 되지 않는다. 포화 구간과 견주는 측정은 별도 부하 Case 로 뺀다.
## 다음 검증
1. 부하를 걸기 전 §122 OQ-7 이 든 여섯을 한 번 찍어 기준값으로 둔다.
2. Keycloak 부하 시험을 돌리면서 같은 여섯을 같은 시각에 기록한다. 호스트에서는 QEMU 프로세스와 vhost 커널 스레드의 CPU 사용량을 스레드 단위로, softirq 시간을, 호스트 전체 CPU 를 적는다.
3. 각 게스트의 CPU 사용량과 부하 도구가 낸 네트워크 지연을 같은 구간에서 적는다.
4. vhost 커널 스레드와 softirq 를 읽는 데 쓴 명령을 함께 남긴다. §14.7 의 ps -eLo pid,tid,psr,pcpu,comm | grep qemu 는 qemu 로 걸러 내 vhost 커널 스레드를 내지 않고, softirq 를 읽는 명령은 근거 문서에 없다.
5. Guest steal time 과 QEMU vCPU 스레드 쪽은 「Keycloak 동시 refresh 실험 중 CPU 가상화 계층이 결과를 흔들 만큼 포화되는가」가 같은 구간에서 재므로, 두 기록을 한 실험에서 함께 남긴다.
닫는 조건 : 부하 구간의 vhost 커널 스레드 사용량과 softirq 시간이 기준값과 다르지 않고 네트워크 지연도 움직이지 않으면, 이 부하 수준에서는 네트워크 가상화가 지연에 영향을 줄 만큼 호스트 CPU 를 쓰지 않는다고 적고 닫는다. 셋 중 하나라도 움직이면 그 부하 전후 표가 Case 가 되고, vhost 스레드를 어느 CPU 에 둘지나 multi-queue 를 켤지는 그 Case 뒤에 Decision 으로 넘긴다. 어느 쪽이든 이 결과가 없으면 §120 이 경고한 오귀속, 곧 네트워크 지연을 Refresh Token 경쟁으로 읽는 것을 배제했다고 적을 근거가 없다.
@@ -0,0 +1,106 @@
---
id: bded6560-dc8d-473d-966c-5b037bb84c4c
kind: QUESTION
slug: qemu-backend-vs-vhost-net-on-this-host
title: QEMU backend 와 vhost-net 의 차이가 이 호스트에서 실제로 보이는가
topic: network-virtualization
topicName: 네트워크 가상화
project: virtualization
status: 초안
questionStatus: OPEN
studio: "https://hyeonworks.com/studio/documents/bded6560-dc8d-473d-966c-5b037bb84c4c/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#122-open-question-oq-4
- final/document.md#107-vhost-net-최적화
- final/document.md#110-data-copy-최적화
- final/document.md#111-interrupt-notification-최적화
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-4
---
# QEMU backend 와 vhost-net 의 차이가 이 호스트에서 실제로 보이는가
backend 는 virtqueue 를 사이에 두고 게스트와 버퍼를 주고받는 호스트 쪽을 가리킨다. §107 은 패킷 처리를 QEMU 사용자 공간(userspace)에서 호스트 커널로 옮기면 컨텍스트 스위치와 사용자 공간 오버헤드가 줄어든다고 적었다. 줄어든다는 방향만 적혀 있을 뿐 이 호스트에서 두 backend 를 나란히 재 본 값은 없다. §110 이 실제로 복사가 일어나는지는 커널 버전과 offload 를 비롯한 여러 조건에 따라 달라질 수 있다고 밝혔으므로, 다른 환경에서 나온 수치를 이 호스트의 값으로 옮겨 쓸 수도 없다. 이 물음은 §122 OQ-4 가 든 여섯 축을 이 호스트에서 나란히 재서 차이가 보이는지 가른다.
## 관계
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
그 경로에서 두 backend 가 갈리는 곳은 TAP 다음 한 칸이고, 이 물음은 거기서 무엇이 달라지는지를 잰다.
- **이 호스트에서 vhost-net 이 실제로 packet datapath 를 맡고 있는가**
지금 어느 쪽으로 돌고 있는지가 정해져야 견줄 두 값 가운데 한쪽이 고정된다.
- **부하 중 network 가상화가 지연을 바꿀 만큼 호스트 CPU 를 쓰는가**
두 물음이 QEMU CPU 와 Host CPU 를 같은 부하에서 읽으므로 측정을 한 번으로 묶을 수 있다.
- **Keycloak 실험 결과를 애플리케이션 원인으로 읽기 전에 network 계층을 따로 검증한다**
backend 차이가 지연에 얼마나 들어오는지를 이 물음이 재면, 그 기준이 그 값을 가져다 쓴다.
## 사실
- §107 은 QEMU 가 사용자 공간에서 패킷마다 I/O 를 처리하면 호스트 커널과 QEMU 사용자 공간 사이를 오가는 전환이 쌓인다고 적었다. 초당 패킷 수(packet rate)가 높아질수록 사용자 공간과 커널 사이의 전환 · 스케줄링 · 복사 · 알림 비용이 커질 수 있다.
- §107 이 적은 두 구성은 TAP 다음에 무엇이 오는지로 갈린다.
QEMU userspace backend : TAP → QEMU → virtqueue
vhost-net kernel backend : TAP → vhost-net → virtqueue
- §125 는 최종 기준 구조에 두 Data Path 를 나란히 그렸다. 열한 칸 가운데 다른 곳은 TAP 다음 한 칸이고, 거기에 vhost-net 이 오느냐 QEMU virtio backend 가 오느냐로 갈린다.
- §107 이 든 최적화 방향은 패킷마다 QEMU 사용자 공간이 끼어들던 처리를 커널 backend 로 옮겨 컨텍스트 스위치와 사용자 공간 오버헤드를 줄이는 것이다.
- §110 은 virtio · virtqueue · vhost 구조가 버퍼 디스크립터(descriptor)로 불필요한 복사와 컨텍스트 스위치를 줄이도록 설계되어 있다고 적고, 그렇다고 이를 항상 zero-copy 라고 일반화하면 안 된다고 못 박았다. 실제로 복사가 일어나는지는 아래에 따라 달라질 수 있다.
Kernel version · QEMU version · vhost configuration
offload · NIC capability · packet path · GSO/GRO/TSO
- §111 은 게스트와 호스트가 큐에 새 패킷이 들어왔음을 서로 알려야 한다고 적고, 패킷마다 인터럽트와 알림이 지나치게 많이 나가면 오버헤드가 커질 수 있다고 덧붙였다. 그래서 batching 과 interrupt moderation 과 queueing 이 중요하다.
- §117.4 는 초당 패킷 수가 높은 구간에서 QEMU 사용자 공간이 처리 경로를 직접 맡으면 CPU 오버헤드가 커질 수 있다고 밝히고, 관찰할 것으로 QEMU CPU usage · vhost thread · packet rate · latency · context switch 를 들었다.
- §122 OQ-4 는 비교할 축 여섯을 적었다.
Latency
Throughput
QEMU CPU
Host CPU
Context Switch
Packet rate
- §122 OQ-4 는 축의 이름만 적어 두었고, 여섯을 무슨 도구로 어떻게 재는지는 제3부에 없다. 같은 §122 에서 OQ-1 과 OQ-2 는 돌릴 명령을 그대로 적었고 OQ-3 은 lsmod | grep vhost 를 확인 후보로 들었다.
- 이 호스트에서 두 backend 를 재 본 값은 SSOT 에 없다.
## 가정
- 같은 가상 머신을 다른 backend 로 다시 구성해 띄울 수 있다고 본다. 그것이 이 실험 환경에서 되는지는 SSOT 에 적혀 있지 않다.
- 두 구성에 같은 부하를 걸 수 있다고 전제한다. 부하 도구와 요청 구성이 같아야 여섯 축을 견줄 수 있기 때문이다.
- 부하를 걸기 전 값과 부하 중 값의 차이가 backend 차이보다 작다고 전제하지 않는다. 그래서 부하 전 값을 먼저 찍어 둔다.
- 두 구성을 같은 시각에 나란히 돌릴 수 없다고 보고 차례로 잰다. 그 사이에 호스트의 다른 부하가 달라지면 값이 흔들릴 수 있다.
## 미지수
- 같은 부하를 두 backend 로 돌렸을 때 여섯 축이 실제로 얼마나 달라지는지.
- 그 차이가 이 실험의 결과를 다르게 읽어야 할 만큼인지, 아니면 측정 흔들림 안인지.
- 이 호스트가 내는 초당 패킷 수가 §107 이 말한 「높아질수록 비용이 커지는」 구간에 들어가는지.
- 여섯 축을 이 환경에서 무엇으로 재는지. 제3부가 도구를 적지 않아 실행하는 쪽이 정한다.
## 제약
- 지금 backend 가 무엇인지는 이 물음이 정하지 않는다. 그것을 확정하는 물음이 닫힌 뒤에 시작한다. §116 은 이 테스트 환경의 경로를 펼치면서 TAP 다음 칸에 vhost-net 을 적어 두었는데, 그 칸이 실제로 그런지를 §122 가 OQ-3 으로 아직 묻고 있으므로 그 그림을 지금 backend 의 근거로 쓰지 않는다.
- §110 이 든 조건들(kernel version · QEMU version · vhost configuration · offload · NIC capability · packet path · GSO/GRO/TSO)을 함께 적지 않으면 이 측정을 다른 환경에 재사용할 수 없다.
- 이 호스트에서 잰 값이 없어 다른 장비의 backend 비교 수치를 근거로 삼지 않는다.
- 여기서 어느 backend 로 실험을 고정할지는 정하지 않는다. 그것은 측정 결과가 나온 뒤의 Decision 이다.
## 선택지
### 1. 지금 backend 만 먼저 찍어 나중에 견줄 값을 만든다
구성을 바꾸지 않고 부하 전과 부하 중의 여섯 축을 한 번씩 기록한다. 가상 머신을 다시 구성하지 않으므로 지금 돌고 있는 Keycloak 실험을 멈추지 않아도 되고, 나중에 다른 backend 를 잴 때 견줄 값이 생긴다.
한 구성의 값만으로는 이 물음이 닫히지 않는다.
### 2. 두 backend 로 바꿔 가며 같은 부하를 건다
§122 OQ-4 가 요구하는 비교가 이것이다. 같은 가상 머신을 다른 backend 로 다시 구성하고 같은 부하를 양쪽에 걸어 여섯 축을 같은 시각에 기록한다. 차이가 나면 그 표가 그대로 Case 가 된다.
가상 머신을 다시 구성하고 재시작해야 하므로 그동안 Keycloak 실험을 멈춰야 하고, 두 측정 사이에 호스트 상태가 달라지지 않도록 관리해야 한다.
### 3. 문헌의 backend 비교 수치를 가져다 쓴다 — 제외
§110 은 실제로 복사가 일어나는지가 여러 조건에 따라 달라질 수 있다고 밝혔다. 그 조건은 kernel version · QEMU version · vhost configuration · offload · NIC capability · packet path · GSO/GRO/TSO 다. 조건이 이만큼 걸려 있으니 다른 환경에서 나온 수치는 이 호스트의 값이 되지 못한다. 이 물음은 이 호스트에서 잰 값으로만 닫힌다.
## 다음 검증
1. 지금 backend 가 무엇인지 먼저 확정한다. 그것을 묻는 물음이 닫히기 전에는 견줄 두 값 가운데 한쪽이 무엇인지 알 수 없다.
2. 부하를 걸기 전에 여섯 축을 한 번 찍어 둔다.
3. 지금 구성에 부하를 걸고 여섯 축을 같은 시각에 기록한다. Latency 와 Throughput 과 Packet rate 는 부하 도구가 내는 값을 쓰고, QEMU CPU 와 Host CPU 와 Context Switch 는 호스트에서 읽는다.
4. 같은 가상 머신을 다른 backend 로 구성하고 같은 부하를 걸어 3 을 되풀이한다.
5. 두 구성의 여섯 축을 나란히 적고, §110 이 든 조건들과 실행한 명령을 함께 증거로 남긴다.
닫는 조건 : 두 구성의 여섯 축을 나란히 놓으면 닫는다. 차이가 부하 전 값의 흔들림 안이면 이 호스트가 내는 초당 패킷 수에서는 backend 선택이 결과를 바꾸지 않는다고 적고 닫는다. 차이가 나면 그 측정이 Case 가 되고, 어느 backend 로 실험을 고정할지는 그 Case 뒤에 Decision 으로 넘긴다.
@@ -0,0 +1,104 @@
---
id: 397c4789-0272-449b-86d6-2c4a3a122401
kind: QUESTION
slug: tap-interface-to-vm-mapping
title: VM1 과 VM2 의 TAP/vnet interface 는 무엇이고 어디에 붙어 있는가
topic: network-virtualization
topicName: 네트워크 가상화
project: virtualization
status: 초안
questionStatus: OPEN
studio: "https://hyeonworks.com/studio/documents/397c4789-0272-449b-86d6-2c4a3a122401/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#122-open-question-oq-2
- final/document.md#99-tap의-역할
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-1
- final/document.md#118-실제-linux에서-확인할-명령어
---
# VM1 과 VM2 의 TAP/vnet interface 는 무엇이고 어디에 붙어 있는가
§99 는 TAP 을 가상 머신의 이더넷 프레임과 호스트 리눅스 네트워크를 잇는 접점으로 놓았다. 그 접점의 이름은 호스트마다 다르게 붙는데, 이 호스트에서 두 가상 머신에 각각 무엇이 붙었는지는 SSOT 에 없다. 이름을 모르면 §119 가 적은 계층별 tcpdump 도 대상을 채우지 못하고, §117.1 이 든 「특정 VM 만 통신 불가」가 어느 가상 머신을 가리키는지도 가릴 수 없다. 이 물음은 두 가상 머신의 호스트 쪽 인터페이스 이름과 그것이 붙어 있는 곳을 확정한다.
## 관계
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
그 경로에서 TAP 이 무엇을 하는지 이미 설명해 두었으므로 이 물음은 거기서 이어진다.
- **packet 이 어디서 끊겼는지는 계층마다 capture 해서 가른다**
그 기준이 요구하는 캡처 지점의 이름을 이 물음이 댄다.
- **이 호스트의 가상 머신 네트워크는 Bridge 인가 NAT 인가 Routing 인가**
ip link 와 bridge link 를 두 물음이 같이 쓰므로 한 번 찍어 둘을 함께 읽는다.
- **Host Nginx 에서 Keycloak 까지 packet 은 실제로 어디를 지나는가**
tcpdump 를 어느 인터페이스에 걸지가 이 물음의 답에서 나온다.
## 사실
- §99 는 TAP 을 호스트 리눅스 커널이 제공하는 가상 이더넷 네트워크 인터페이스로 적었다. 물리 장치가 아니고, 이름의 예로 tap0 과 vnet0 을 들었다.
- §99 가 적은 TAP 의 역할은 가상 머신의 이더넷 프레임과 호스트 리눅스 네트워크를 연결하는 접점이다.
수신 : Linux Bridge → TAP → VM
송신 : VM → TAP → Linux Bridge
- §99 는 확인 명령으로 ip link · ip tuntap show · bridge link · virsh domiflist 넷을 들었고, §118 이 같은 명령을 계층별 목록으로 다시 적었다.
TAP/vnet : ip link · ip tuntap show
libvirt VM NIC : virsh domiflist 에 domain 이름을 넣는다
Linux Bridge : ip link show type bridge · bridge link · bridge fdb show
- §122 OQ-2 는 이 호스트에서 돌릴 명령을 네 줄로 적었다.
virsh domiflist vm1
virsh domiflist vm2
ip link
bridge link
- §99 와 §118 이 TAP 확인 명령으로 든 ip tuntap show 는 OQ-2 의 네 줄에 없다.
- §117.1 은 TAP/Bridge 연결 오류의 증상 셋을 들었다.
VM 외부 통신 불가
Host ↔ VM 통신 불가
특정 VM 만 통신 불가
- §117.1 이 그 증상에서 확인하라고 든 명령은 ip link · bridge link · bridge fdb show · virsh domiflist 다.
- §116 은 이 테스트 환경의 경로를 펼치면서 TAP 칸을 TAP(vm1) 과 TAP(vm2) 로 적었다. 호스트에서 읽은 이름은 그 그림에도 없다.
- 두 가상 머신의 호스트 쪽 인터페이스 이름도, 그 인터페이스가 어느 브리지에 붙어 있는지도 SSOT 에는 없다.
## 가정
- 호스트에 붙어 virsh 와 ip 계열 명령을 실행할 수 있다고 본다.
- §122 OQ-2 가 명령에 적은 vm1 과 vm2 가 이 호스트에 실재하는 domain 이름이라고 본다. 실제 이름이 다르면 그 이름으로 바꿔 돌린다. §99 와 §118 은 같은 명령에 넣을 domain 을 비워 두었고, 가상 머신 이름을 그대로 적은 곳은 §90.1 의 virsh domiflist vm1 과 §122 OQ-2 다.
- virsh domiflist 출력에 인터페이스 이름과 MAC 주소가 함께 나온다고 전제한다. §99 도 §118 도 이 명령의 출력 형식은 적지 않았다.
- 두 가상 머신이 켜져 있는 동안 읽는다고 전제한다. 꺼진 가상 머신의 TAP 이 호스트에 남아 있는지는 SSOT 에 적혀 있지 않다.
## 미지수
- VM1 과 VM2 의 호스트 쪽 인터페이스 이름이 각각 무엇인지.
- 각 인터페이스의 MAC 주소와 NIC model 이 무엇인지.
- 두 인터페이스가 같은 브리지에 붙어 있는지, 서로 다른 곳에 붙어 있는지.
- ip tuntap show 에 나오는 TAP 목록과 virsh domiflist 가 대는 이름이 그대로 맞아떨어지는지.
## 제약
- 이름과 어디에 붙어 있는지를 적는 데서 끊는다. 그 경로로 패킷이 실제로 흘렀는지는 tcpdump 를 쓰는 물음이 받는다.
- 두 가상 머신을 같은 시점에 읽는다. 한쪽을 재시작한 뒤 다른 쪽을 읽으면 이름이 바뀌어도 알 수 없기 때문이다.
- 이 호스트에서 읽은 출력이 없어 tap0 이나 vnet0 같은 §99 의 예시 이름을 이 호스트의 값으로 쓰지 않는다.
## 선택지
### 1. libvirt 가 대는 이름을 먼저 받아 호스트에서 대조한다
virsh domiflist 로 가상 머신마다 붙은 인터페이스를 받고, 그 이름이 ip link 목록에 실재하는지 확인한 뒤 bridge link 로 어느 브리지의 port 인지 잡는다. §122 OQ-2 가 적은 네 줄이 이 순서다. 가상 머신과 인터페이스의 짝이 처음부터 정해져 나오므로 두 대의 것을 헷갈리지 않는다.
libvirt 가 모르는 인터페이스는 이 순서에서 빠진다.
### 2. 호스트의 인터페이스를 전부 세우고 가상 머신으로 되짚는다
ip link 와 ip tuntap show 로 호스트에 있는 TAP 을 모두 적고, bridge link 로 어느 브리지에 붙었는지 잡는다. 그다음 virsh domiflist 로 각각이 어느 가상 머신의 것인지 되짚는다. libvirt 밖에서 만들어진 TAP 이 있어도 목록에 남는다.
가상 머신이 두 대뿐인데 호스트에 TAP 이 여럿이면 짝짓는 데 MAC 주소를 다시 대조해야 한다.
### 3. 통신이 안 될 때 §117.1 의 확인 목록으로 함께 본다 — 제외
§117.1 이 증상과 확인 명령을 이미 묶어 두었으니 장애가 났을 때 같이 보자는 방법이다. 지금 필요한 것은 정상일 때의 이름과 붙어 있는 곳이고, 그것이 있어야 장애 때 무엇이 달라졌는지 견줄 수 있다. 증상이 난 뒤에 처음 읽으면 그 값이 원래 그랬는지 그때 바뀐 것인지 가릴 수 없다.
## 다음 검증
1. virsh domiflist vm1 과 virsh domiflist vm2 로 각 가상 머신에 붙은 인터페이스를 읽는다.
2. ip link 로 그 이름이 호스트에 실재하는지 대조한다.
3. bridge link 로 각 인터페이스가 어느 브리지의 port 인지 적는다.
4. 두 가상 머신의 결과를 인터페이스 이름 · MAC 주소 · 붙어 있는 브리지로 나란히 적고, 실행한 명령과 출력을 함께 증거로 남긴다.
닫는 조건 : 가상 머신마다 인터페이스 이름과 MAC 주소와 붙어 있는 브리지를 적어 두 대를 나란히 놓으면 닫는다. 이 목록이 계층별 캡처 기준이 요구하는 지점의 이름이 되고, 이것이 없으면 실제 패킷 경로를 묻는 물음의 tcpdump 를 어느 인터페이스에 걸지 정할 수 없다. 두 가상 머신이 서로 다른 브리지에 붙어 있으면 §117.1 의 「특정 VM 만 통신 불가」를 진단할 때 그 사실을 먼저 본다고 적는다.
@@ -0,0 +1,106 @@
---
id: 1a996f8d-b04e-49d2-88ef-4877e21f7c0e
kind: QUESTION
slug: virtio-net-multi-queue-enabled
title: 이 가상 머신들의 virtio-net 에 multi-queue 가 켜져 있는가
topic: network-virtualization
topicName: 네트워크 가상화
project: virtualization
status: 초안
questionStatus: OPEN
studio: "https://hyeonworks.com/studio/documents/1a996f8d-b04e-49d2-88ef-4877e21f7c0e/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#122-open-question-oq-5
- final/document.md#112-multi-queue-최적화
- final/document.md#100-virtqueue의-역할
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-5
---
# 이 가상 머신들의 virtio-net 에 multi-queue 가 켜져 있는가
multi-queue 는 virtio-net 이 송수신 큐를 여러 개 두고 쓰는 구성이다. §112 는 큐를 하나만 쓰면 패킷 처리가 한 vCPU 나 한 처리 경로에 몰릴 수 있어서 이것을 최적화 방향으로 들었다. 이 물음은 그 쏠림이 실제로 일어나는지를 재지 않고, 두 가상 머신이 애초에 큐를 몇 개 쓰도록 구성되어 있는지를 읽는다. 근거 문서는 multi-queue 를 쓸 수 있다고만 적었을 뿐 이 가상 머신들의 큐 수는 적지 않았다.
## 관계
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
게스트와 호스트 backend 가 virtqueue 로 무엇을 주고받는지를 이 개념이 설명한다. 큐를 몇 개 두느냐는 그 구조 위의 설정이다.
- **이 호스트에서 vhost-net 이 실제로 packet datapath 를 맡고 있는가**
큐를 실제로 돌리는 backend 가 어느 쪽이냐에 따라 큐 수를 읽을 곳이 달라진다.
- **부하 중 network 가상화가 지연을 바꿀 만큼 호스트 CPU 를 쓰는가**
큐가 하나로 나왔을 때 그것이 실제 병목인지는 부하 구간의 vCPU 별 사용량이 답한다.
## 사실
- §112 는 큐를 하나만 쓰면 특정 vCPU 나 처리 경로에 부하가 몰릴 수 있다고 적었고, virtio-net 이 multi-queue 를 쓸 수 있다고 밝혔다. 든 예는 RX Queue 0 부터 3 까지를 vCPU 0 부터 3 까지에 하나씩 대응시킨 구성이다.
- §112 가 적은 multi-queue 의 목적 셋
Packet processing 병렬화
Single queue bottleneck 완화
Multi-core 활용
- §112 는 그 효과가 workload · CPU affinity · IRQ placement · queue configuration 에 따라 달라진다고 덧붙였다. IRQ(Interrupt Request, 인터럽트 요청)는 장치가 처리할 일이 생겼음을 CPU 에 알리는 신호다. §117.5 가 그 분포를 확인 대상으로 든 것을 보면, 큐를 나눠 두어도 그 신호를 한 vCPU 가 몰아서 받으면 처리는 한 곳에 몰릴 수 있다.
- §100 은 virtqueue 를 게스트와 호스트 backend 가 디스크립터(descriptor)를 써서 I/O 버퍼를 주고받는 공유 큐 구조로 놓고, 네트워크에서는 보통 TX/RX 큐를 쓴다고 적었다. TX virtqueue 는 게스트에서 호스트로, RX virtqueue 는 호스트에서 게스트로 버퍼를 넘긴다.
- §117.5 는 single queue bottleneck 을 큐 하나나 vCPU 하나에 패킷 처리가 몰리는 문제로 놓고, 확인 대상 넷을 들었다.
virtio multi-queue
IRQ distribution
per-vCPU CPU usage
RSS/RPS/XPS
- §122 OQ-5 가 적은 확인 대상 넷
QEMU/libvirt NIC configuration
Guest ethtool
queue count
IRQ distribution
- 같은 §122 에서 OQ-1 과 OQ-2 는 돌릴 명령을 그대로 적었고, OQ-5 는 확인 대상 넷의 이름만 적었다. §117.5 가 든 넷도 이름이다.
- 두 가상 머신에 설정된 큐 수가 근거 문서에 없다. 게스트가 몇 개를 쓰고 있는지도, IRQ 가 어느 vCPU 에 붙어 있는지도 적혀 있지 않다.
- §126 이 적은 실습 순서 열둘 가운데 multi-queue / offload 확인은 마지막 열두째다.
- 네트워크 계층의 확인 명령을 모아 둔 §118 에는 큐 수나 IRQ 분포를 읽는 명령이 없다. Guest NIC 항목에 적힌 것은 ip link · ip addr · ip route · ip neigh 이고, virtio 장치 항목은 lspci 와 lsmod | grep virtio 다. ethtool 은 Physical NIC 항목에서 인터페이스 이름을 받는 형태로만 나온다.
## 가정
- 두 가상 머신의 구성과 게스트 내부를 지금 읽을 수 있다고 본다.
- §112 가 든 RX Queue 넷과 vCPU 넷의 짝은 multi-queue 를 설명하려고 든 예시이지 이 호스트에서 읽은 값이 아니다.
- 설정에 적힌 큐 수와 게스트가 실제로 쓰는 큐 수를 따로 읽어야 한다고 전제한다. 설정에 여럿을 적어 두면 게스트 드라이버가 그만큼 쓴다는 서술이 근거 문서에 없기 때문이다.
- 두 가상 머신의 NIC 구성이 확인하는 동안 바뀌지 않는다.
## 미지수
- 두 가상 머신의 virtio-net 에 설정된 큐 수.
- 게스트가 실제로 쓰고 있는 큐 수, 그리고 그 수가 설정값과 같은지.
- 각 큐의 IRQ 가 여러 vCPU 에 흩어져 있는지 한 vCPU 에 몰려 있는지.
- 두 가상 머신의 vCPU 수. §112 가 큐와 vCPU 를 하나씩 짝지어 든 예는 두 수를 나란히 놓아야 읽히는데, vCPU 수도 네트워크 쪽 근거에는 없다.
- 이 환경의 RSS/RPS/XPS 설정. §117.5 가 확인 대상으로 들었지만 값을 읽는 방법은 적지 않았다.
## 제약
- 이 호스트에서 잰 값이 하나도 없다. 근거는 개념을 정리한 문서 한 편이고 §112 의 큐 넷과 vCPU 넷은 예시 숫자다.
- 이 물음은 큐 구성이 무엇인지까지만 답한다. 쏠림이 실제 병목인지는 부하를 걸어야 갈리고, 그 부하 측정은 다른 물음이 가져간다.
- 확인하는 동안 NIC 구성을 바꾸지 않는다. 큐 수를 늘려 놓고 읽으면 지금 실험이 어떤 구성에서 돌았는지 못 본다.
- §118 이 큐 수와 IRQ 분포를 읽는 명령을 적지 않았으므로, 실행한 명령과 그 출력을 함께 증거로 남겨야 다음 사람이 같은 값을 다시 읽는다.
## 선택지
### 1. 호스트 쪽 설정값만 먼저 읽는다
libvirt 와 QEMU 쪽 NIC 구성에 큐 수가 적혀 있는지부터 본다. §90.2 가 libvirt 의 관리 대상 예로 든 여덟은 vCPU · Memory · Disk · NIC model · MAC address · Virtual network · Bridge · QEMU arguments 이고, 큐 수는 그 목록에 없다. 가상 머신에 들어가지 않고 호스트에서 끝나고, 큐가 하나로 적혀 있으면 그 구성은 single queue 로 확정된다.
설정에 큐를 여럿 적어 두었을 때 게스트가 그만큼 쓰는지는 이 확인으로 알 수 없다.
### 2. 설정값과 게스트 쪽 채널 수를 함께 읽는다
§122 OQ-5 가 든 넷 가운데 앞의 셋에 해당한다. 호스트의 NIC 구성과 게스트 안에서 본 채널 수를 같이 적으면 둘이 어긋나는 경우까지 잡힌다. 가상 머신 두 대에 각각 들어가야 해서 실행 횟수가 늘어난다.
### 3. IRQ 분포까지 한 번에 읽는다
큐가 여럿이어도 IRQ 가 한 vCPU 에 몰려 있으면 §117.5 가 든 쏠림은 그대로 생길 수 있다. §122 OQ-5 가 IRQ distribution 을 넷째 확인 대상으로 둔 이유가 여기에 있다. 세 항목을 한 번에 받아 적으면 큐 수만으로 판정을 끝내지 않게 된다.
### 4. 제외 — 큐 수를 바꿔 가며 견준다
multi-queue 를 켜고 끄면서 재면 이 환경에서 큐 수가 무엇을 바꾸는지 바로 보인다. 그러나 지금 물음은 실험이 어떤 구성에서 돌고 있는지를 읽는 것이라, 구성을 바꾸고 잰 값은 답이 되지 않는다. 켜고 끈 두 구성을 견주는 측정은 별도 Case 로 뺀다.
## 다음 검증
1. 호스트에서 두 가상 머신의 libvirt/QEMU NIC configuration 을 열어 큐 수가 적혀 있는지 읽는다. §122 에서 libvirt domain XML 을 확인하라고 적은 곳은 OQ-3 이고 OQ-5 에는 그 문장이 없다. §118 이 이 확인의 명령을 적지 않았으므로 무엇을 실행했는지 함께 기록한다.
2. 각 게스트에서 ethtool 로 채널 수를 읽고, 실제 queue count 를 그 옆에 적는다. §122 OQ-5 가 Guest ethtool 과 queue count 를 나눠 적었으므로 둘을 따로 남긴다.
3. 각 게스트의 IRQ 분포를 읽어 큐마다 어느 vCPU 에 붙어 있는지 적는다.
4. 두 가상 머신의 결과를 vCPU 수와 나란히 한 표로 정리한다.
닫는 조건 : 가상 머신마다 설정된 큐 수 · 게스트가 쓰는 큐 수 · IRQ 분포를 한 표로 적으면 닫는다. 큐가 하나로 나오면 §117.5 가 든 single queue bottleneck 이 이 환경에서도 일어날 수 있는 구성이라고 적는다. 그것이 실제 병목인지는 부하를 건 뒤 vCPU 별 CPU 사용량이 답하기 때문에 「부하 중 network 가상화가 지연을 바꿀 만큼 호스트 CPU 를 쓰는가」로 넘긴다. 큐가 여럿이고 IRQ 도 흩어져 있으면 이 항목은 지금 실험에서 우선순위를 낮추고 닫는다.
@@ -0,0 +1,104 @@
---
id: 38716d7f-1aa1-48d8-ab4c-3fc505332128
kind: QUESTION
slug: vm-network-mode-bridge-nat-or-routed
title: 이 호스트의 가상 머신 네트워크는 Bridge 인가 NAT 인가 Routing 인가
topic: network-virtualization
topicName: 네트워크 가상화
project: virtualization
status: 초안
questionStatus: OPEN
studio: "https://hyeonworks.com/studio/documents/38716d7f-1aa1-48d8-ab4c-3fc505332128/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#122-open-question-oq-1
- final/document.md#96-linux-bridge의-역할
- final/document.md#97-routing의-역할
- final/document.md#98-nat의-역할
- final/document.md#114-linux-bridge가-항상-host-tcp-ip-stack을-거치는-것은-아니다
- final/document.md#94-전체-네트워크-계층
---
# 이 호스트의 가상 머신 네트워크는 Bridge 인가 NAT 인가 Routing 인가
§98 은 가상 머신 네트워크를 분석하기 전에 Bridge 기반인가 · Routing 기반인가 · NAT(Network Address Translation, 네트워크 주소 변환) 기반인가를 구분하라고 적었다. 셋은 프레임이 지나는 계층이 다르고, 그에 따라 호스트의 L3 경로와 Netfilter 가 끼어드는지도 갈린다. 이 물음은 성능을 재지 않는다. 제3부가 서술한 경로 가운데 어느 절이 이 호스트에 그대로 적용되는지를 먼저 확정한다.
## 관계
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
그 경로 그림에서 TAP 과 Physical NIC 사이에 놓인 계층이 이 물음이 확정하려는 부분이다.
- **packet 이 어디서 끊겼는지는 계층마다 capture 해서 가른다**
구성이 무엇이냐에 따라 캡처를 걸 계층의 이름이 달라진다.
- **VM1 과 VM2 의 TAP/vnet interface 는 무엇이고 어디에 붙어 있는가**
ip link 와 bridge link 를 두 물음이 같이 쓰므로 한 번 찍어 둘을 함께 읽는다.
- **Host Nginx 에서 Keycloak 까지 packet 은 실제로 어디를 지나는가**
이 물음이 닫혀야 그 추적에서 tcpdump 를 걸 대상이 정해진다.
## 사실
- §96 은 Linux Bridge 를 호스트 커널 안의 L2 소프트웨어 스위치로 적었다. 이더넷 프레임의 Destination MAC 을 보고 어느 포트로 보낼지 정하고, MAC learning 을 하며, 여러 가상 포트와 물리 포트를 잇는다.
- §97 은 Routing 을 L3 에서 IP 를 보고 내리는 결정으로 놓았다. Bridge 가 같은 이더넷 네트워크를 잇는 것과 달리 Routing 은 서로 다른 IP 네트워크를 잇고, destination IP 를 보고 어느 인터페이스나 next-hop 으로 보낼지 정한다.
- §98 은 NAT 을 패킷의 IP/Port 정보를 바꾸는 것으로 놓고, 가상 머신이 private subnet 을 쓰면 호스트가 NAT gateway 처럼 동작할 수 있다는 예를 들었다.
VM : 192.168.122.10
Host NAT 를 지난 뒤 : 203.0.113.10
- §96 과 §97 은 절 끝에 확인 명령을 달았다. §96 은 bridge link · bridge fdb show · ip link show type bridge 를, §97 은 ip route 를 든다. 셋을 구분하라고 적은 §98 에는 확인 명령이 없다.
- §114 는 Bridge 가 단순 L2 forwarding 만 하는 구성이면 프레임이 호스트의 일반적인 L3 TCP/IP 스택을 지나지 않고 다른 TAP 으로 나갈 수 있다고 적었다. 호스트가 Routing · NAT · Host-local termination · Firewall 을 맡으면 그때는 L3/Netfilter 경로가 끼어든다.
- 그래서 §114 는 Physical NIC → Host TCP/IP Stack → Bridge 를 고정된 패킷 경로로 보면 안 되고, 실제 경로는 bridge/routing/NAT 구성에 따라 달라진다고 못 박았다.
- §94 는 이 문서가 기준으로 삼은 구조를 virtio-net + vhost-net + TAP + Linux Bridge 로 적고, 실제 환경은 Bridge · NAT · Routed Network · macvtap · SR-IOV · VFIO passthrough · Open vSwitch · Kubernetes CNI 에 따라 달라질 수 있다고 덧붙였다.
- 확인할 명령은 §122 OQ-1 이 다섯 줄로 적어 두었다.
정의된 가상 네트워크 열거 : virsh net-list --all
그 가상 네트워크의 정의 읽기 : virsh net-dumpxml 에 이름을 넣는다
호스트 인터페이스 목록 : ip link
어느 인터페이스가 어느 브리지의 포트인지 : bridge link
라우팅 테이블 : ip route
- §118 이 같은 계층에 든 명령 가운데 virsh net-info 와 ip rule 은 OQ-1 의 다섯 줄에 없다.
- 이 호스트에서 그 명령을 돌린 출력은 SSOT 에 없다. 셋 중 무엇인지도 적혀 있지 않다.
## 가정
- 호스트에 붙어 virsh 와 ip 계열 명령을 실행할 수 있다고 본다.
- libvirt 가상 네트워크로 정의된 구성이면 virsh net-list --all 에 이름이 나온다고 본다. 호스트가 미리 만들어 둔 브리지에 가상 머신을 직접 붙인 구성이면 그 목록에 아무 이름도 나오지 않을 수 있다.
- 확인하는 동안 네트워크 구성이 바뀌지 않는다고 전제한다.
- 셋 가운데 하나로 갈린다고 보고 물음을 세웠다. 다만 §114 가 Bridge 구성에도 Routing 과 NAT 과 Firewall 이 함께 걸릴 수 있다고 적었으므로, 하나로 갈리지 않으면 걸린 것을 모두 적는다.
## 미지수
- 이 호스트의 가상 머신 네트워크가 Bridge 기반인지 NAT 기반인지 Routing 기반인지.
- libvirt 가상 네트워크로 정의되어 있는지, 아니면 호스트의 브리지에 가상 머신이 직접 붙어 있는지.
- 정의되어 있다면 그 가상 네트워크의 forward mode 와 bridge 이름이 무엇인지.
- 가상 머신을 떠난 프레임이 호스트의 L3/Netfilter 경로를 지나는지.
## 제약
- 구성이 셋 중 무엇인지를 확정하는 데서 끊는다. 그 구성이 지연에 얼마나 영향을 주는지는 부하 중 호스트 CPU 사용을 보는 물음이 받는다.
- 이 호스트에서 읽은 출력이 없어 다른 장비의 구성을 근거로 삼지 않는다.
- 돌릴 명령은 §122 OQ-1 이 정해 두었다. 실행한 명령과 출력을 함께 남겨야 다음 사람이 같은 값을 다시 읽는다.
## 선택지
### 1. libvirt 쪽부터 읽는다
virsh net-list --all 로 정의된 가상 네트워크를 열거하고, 나온 이름마다 virsh net-dumpxml 로 forward mode 와 bridge 이름을 읽는다. forward mode 하나로 Bridge 인지 NAT 인지 Routed 인지가 갈리므로 확인이 짧다.
libvirt 로 정의하지 않고 호스트 브리지에 직접 붙인 구성이면 목록이 비어 나오고, 그때는 호스트 인터페이스 쪽을 다시 읽어야 한다.
### 2. 호스트 인터페이스 쪽부터 읽는다
ip link 로 실재하는 인터페이스를 세우고, bridge link 로 어느 인터페이스가 어느 브리지의 포트인지를 잡고, ip route 로 L3 결정을 본다. libvirt 로 정의했든 안 했든 호스트에 실재하는 것을 읽으므로 구성 방식과 무관하게 답이 나온다.
forward mode 라는 이름으로 적힌 의도는 나오지 않으므로, NAT 이 걸려 있는지는 routing 과 방화벽 규칙을 따로 봐야 한다. §117.3 이 NAT/Firewall 오류에 든 것도 nftables · iptables · NAT rules · IP forwarding 이라는 확인 대상 이름이고 돌릴 명령이 아니다.
### 3. tcpdump 로 경로부터 잡는다 — 제외
§119 는 Host NIC · Bridge · TAP · Guest NIC 에서 tcpdump 로 패킷을 추적하는 순서를 적어 두었다. 다만 그 추적은 캡처를 걸 인터페이스 이름을 이미 알고 있을 때 성립한다. 지금은 브리지 이름도 TAP 이름도 모르므로 명령의 대상을 채울 수 없다. §126 의 실습 순서에서도 Bridge/NAT/Route 확인이 셋째이고 Host Nginx → VM packet path tcpdump 는 여덟째다. 이 방법은 이 물음이 닫힌 뒤 실제 패킷 경로를 묻는 물음이 받는다.
## 다음 검증
1. virsh net-list --all 로 정의된 가상 네트워크를 열거한다.
2. 나온 이름마다 virsh net-dumpxml 에 그 이름을 넣어 forward mode 와 bridge 이름을 읽는다.
3. ip link 로 호스트의 인터페이스 목록을 적는다.
4. bridge link 로 어느 인터페이스가 어느 브리지에 붙어 있는지 적는다.
5. ip route 로 라우팅 테이블을 적는다.
6. 다섯 출력을 실행한 명령과 함께 증거로 남긴다.
닫는 조건 : 세 구성 가운데 무엇인지가 출력으로 확정되면 닫는다. Bridge 로 나오면 §96 과 §114 의 L2 forwarding 서술이 이 호스트에 적용된다고 적는다. NAT 으로 나오면 §98 의 주소 변환이 경로에 들어가고, Routing 으로 나오면 §97 의 L3 결정이 들어간다. 어느 쪽이든 그 결과로 실제 패킷 경로를 묻는 물음의 캡처 지점이 정해진다. §94 가 기준으로 삼은 구조와 다르게 나오면 제3부의 서술 가운데 이 호스트에 적용되지 않는 절을 함께 적는다.
@@ -0,0 +1,137 @@
---
id: 68a848c7-eaff-4421-ad25-de21673ca40c
kind: REFERENCE
slug: bisect-the-packet-path-with-capture-points
title: packet 이 어디서 끊겼는지는 계층마다 capture 해서 가른다
topic: network-virtualization
topicName: 네트워크 가상화
project: virtualization
status: 초안
studio: "https://hyeonworks.com/studio/documents/68a848c7-eaff-4421-ad25-de21673ca40c/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#119-실제-packet-path-추적
- final/document.md#118-실제-linux에서-확인할-명령어
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-1
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-2
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-3
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-6
- final/document.md#113-offload-최적화
- final/document.md#114-linux-bridge가-항상-host-tcp-ip-stack을-거치는-것은-아니다
- final/document.md#99-tap의-역할
---
# packet 이 어디서 끊겼는지는 계층마다 capture 해서 가른다
가상 머신이 밖과 통신하지 못할 때 게스트 안에서만 원인을 찾으면 호스트의 브리지와 TAP 연결은 마지막에야 보게 된다. 그 사이의 계층은 애플리케이션 로그에 아무것도 남기지 않는다.
그래서 패킷이 지나야 할 지점마다 캡처를 걸고 어디까지 보였는지로 구간을 좁힌다. 호스트의 물리 NIC(Network Interface Card) 와 브리지, 가상 머신에 붙은 TAP, 게스트 안의 인터페이스 넷이 그 지점이고, 보이지 않은 첫 지점의 앞 구간이 의심 구간이 된다.
## 관계
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
이 절차가 관측 지점으로 쓰는 계층을 세운 글이다. 어느 계층이 무엇을 하는지는 그쪽에 있다.
- **Keycloak 실험 결과를 애플리케이션 원인으로 읽기 전에 network 계층을 따로 검증한다**
그 기준은 문제가 네트워크인지 아닌지를 가르고, 이 절차는 네트워크 안에서 어느 구간인지를 가른다.
- **이 호스트의 가상 머신 네트워크는 Bridge 인가 NAT 인가 Routing 인가**
캡처 지점 이름을 확정하는 첫 규칙이 그대로 이 물음의 확인 절차다.
- **VM1 과 VM2 의 TAP/vnet interface 는 무엇이고 어디에 붙어 있는가**
TAP 이름을 모르면 이 절차의 세 번째 지점에 캡처를 걸 수 없다.
- **Host Nginx 에서 Keycloak 까지 packet 은 실제로 어디를 지나는가**
그 경로를 처음 확인할 때 쓰는 절차다.
## 목적
이 기준은 두 가지를 막는다.
가상 머신이 통신하지 못할 때 게스트 안쪽과 애플리케이션부터 뒤지느라 호스트의 브리지와 TAP 연결을 늦게 보는 일이 하나다.
오프로드 때문에 실제 wire 와 다르게 보이는 정상 패킷을 결함으로 판정하는 일이 다른 하나다.
이 순서는 물음 셋이 함께 쓴다. 이 호스트의 가상 머신 네트워크가 무엇으로 구성돼 있는지, TAP 인터페이스가 무엇인지, 호스트 Nginx 에서 Keycloak 까지 패킷이 어디를 지나는지 셋이 모두 이 순서 위에서 돈다. 그래서 세 곳에 같은 절차를 되풀이하지 않고 한 편에 두었다.
## 규칙
### 1. 캡처할 지점의 이름을 먼저 확정한다
관측 지점은 넷이다. 호스트의 물리 NIC, 호스트의 브리지, 가상 머신에 붙은 tap 또는 vnet, 그리고 게스트 안의 인터페이스.
이름을 모른 채 시작하면 없는 인터페이스에 캡처를 걸어 놓고 패킷이 안 보인다고 판정하게 된다.
인터페이스 목록은 ip link 로 본다.
브리지에 무엇이 붙어 있는지는 bridge link 와 bridge fdb show 가 알려 준다.
tap 목록은 ip tuntap show 로 본다.
어느 가상 머신이 어느 tap 에 붙어 있는지는 virsh domiflist 에 도메인 이름을 붙여 확인한다.
libvirt 가 만든 가상 네트워크가 무엇인지는 virsh net-list --all 과 virsh net-dumpxml 로 확인한다.
라우팅이 개입하는 구성이면 ip route 와 ip rule 도 함께 본다.
### 2. 네 지점에 같은 요청을 흘리면서 캡처를 건다
호스트에서는 물리 NIC 와 브리지, tap 또는 vnet 에 각각 tcpdump -ni 로 캡처를 건다.
게스트에서는 게스트 인터페이스에 같은 방식으로 건다.
### 3. 보이지 않은 첫 지점의 앞 구간을 의심 구간으로 삼는다
판독은 어느 지점까지 보였는가로만 한다.
물리 NIC 에 보이고 브리지에도 보이는데 tap 에 안 보이면, 게스트 내부보다 먼저 호스트의 브리지와 tap 연결을 의심한다.
tap 에는 보이는데 게스트 NIC 에 안 보이면 virtio 와 vhost, 게스트 NIC 계층을 의심한다.
게스트 NIC 에는 보이는데 소켓까지 오지 않으면 게스트의 라우팅과 방화벽, listen 상태를 의심한다.
### 4. 증상마다 다음에 볼 명령을 미리 정해 둔다
의심 구간이 나오면 그 구간의 증상에 맞는 확인으로 넘어간다.
가상 머신이 외부와 통신하지 못하거나 호스트와 통신하지 못하거나 특정 가상 머신만 통신하지 못하면 tap 과 브리지 연결을 본다. ip link, bridge link, bridge fdb show, virsh domiflist 가 그 확인이다.
같은 서브넷은 통신되는데 다른 서브넷이 안 되거나 게이트웨이까지는 가는데 외부 통신이 실패하면 라우팅을 본다. ip route 와 ip rule 이다.
가상 머신에서 인터넷으로 나가지 못하거나 외부에서 가상 머신에 접근하지 못하거나 특정 포트만 실패하면 nftables 와 iptables, NAT(Network Address Translation) 규칙, IP(Internet Protocol) forwarding 설정을 확인 대상으로 둔다.
증상 셋을 각각 다른 기준으로 나누지 않은 것은 셋이 모두 어느 지점에서 끊겼는가로 환원되기 때문이다. 따로 적으면 같은 규칙의 부분 증상이 셋으로 늘어난다.
### 5. 패킷 크기와 체크섬이 예상과 다르다는 이유만으로 결함이라고 읽지 않는다
오프로드가 켜져 있으면 캡처에서 보이는 패킷 크기나 체크섬이 실제 wire 에서 보이는 것과 다르게 보일 수 있다. 원인 후보는 GSO(Generic Segmentation Offload), GRO(Generic Receive Offload), TSO(TCP Segmentation Offload), checksum offload 다.
크기와 체크섬이 예상과 다르면 오프로드 설정을 먼저 확인하고, 그 값만으로 판정하지 않는다.
### 6. 호스트의 L3 쪽에서 안 보이는 것을 곧바로 실패로 읽지 않는다
브리지가 L2 전달만 하는 경우 프레임이 호스트의 일반적인 L3 TCP/IP 스택을 반드시 거치지는 않는다. 한 가상 머신의 tap 에서 브리지를 지나 다른 가상 머신의 tap 으로 가는 경로가 그렇다.
호스트가 라우팅이나 NAT, host-local termination, 방화벽 역할을 할 때 L3 와 Netfilter 경로가 개입한다. 그래서 구성에 따라 호스트의 L3 관측에서 안 보이는 것이 정상이다.
### 7. 네 지점이 모두 보이는데 느리기만 한 경우는 이 절차가 답하지 않는다
이 절차가 가르는 것은 패킷이 어디서 끊겼는가다. 끊기지 않고 느리기만 하면 다른 기준으로 넘긴다.
## 적용 조건
KVM(Kernel-based Virtual Machine)/QEMU/libvirt 로 만든 가상 머신이 외부와 통신하지 못할 때.
특정 구간에서만 통신이 실패할 때.
실제 패킷 경로를 처음 확인할 때.
## 예외
오프로드가 켜진 구성에서는 패킷 크기와 체크섬이 그대로 증거가 되지 않는데, 다섯 번째 규칙이 그 경우다.
브리지가 L2 전달만 하는 구성에서는 호스트의 L3 관측에 안 보이는 것이 정상인데, 여섯 번째 규칙이 그 경우다.
네 지점이 모두 보이는데 느린 경우는 이 기준이 다루지 않는다. Keycloak 실험 결과를 애플리케이션 원인으로 읽기 전에 network 계층을 따로 검증한다 쪽으로 넘긴다.
이 저장소에는 아직 이 절차를 실제로 돌린 출력이 없다. 여기 적은 판독은 원문 문서가 서술한 것이고 이 호스트의 캡처로 확인한 것이 아니다.
## 예시
- 물리 NIC 에 보이고 브리지에도 보이는데 tap 에 안 보임 : 호스트의 브리지와 tap 연결을 먼저 확인한다
- tap 에 보이는데 게스트 NIC 에 안 보임 : virtio 와 vhost, 게스트 NIC 계층을 확인한다
- 게스트 NIC 에 보이는데 소켓까지 안 옴 : 게스트의 라우팅과 방화벽, listen 상태를 확인한다
- 특정 가상 머신만 통신 불가 : ip link 와 bridge link, virsh domiflist 로 그 가상 머신의 tap 이 브리지에 붙어 있는지 본다
- 같은 서브넷은 되고 다른 서브넷만 실패 : ip route 와 ip rule 을 본다
- 캡처에 보이는 패킷이 예상보다 크고 체크섬이 어긋남 : 오프로드 설정을 먼저 확인한다
- 네 지점이 모두 보이는데 응답만 느림 : 이 절차를 여기서 멈춘다
@@ -0,0 +1,108 @@
---
id: ca36f0db-9028-4761-91a8-afb7eb30f2b9
kind: REFERENCE
slug: verify-the-network-path-before-blaming-the-application
title: Keycloak 실험 결과를 애플리케이션 원인으로 읽기 전에 network 계층을 따로 검증한다
topic: network-virtualization
topicName: 네트워크 가상화
project: virtualization
status: 초안
studio: "https://hyeonworks.com/studio/documents/ca36f0db-9028-4761-91a8-afb7eb30f2b9/edit"
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
source:
- final/document.md#120-keycloak-refresh-token-실험과의-관계
- final/document.md#117-이-구조에서-발생할-수-있는-문제-117-7
- final/document.md#124-핵심-claim
- final/document.md#89-문서-목적
- final/document.md#116-현재-keycloak-k3s-테스트-환경과-연결
- final/document.md#102-packet이-keycloak까지-올라오는-과정
---
# Keycloak 실험 결과를 애플리케이션 원인으로 읽기 전에 network 계층을 따로 검증한다
Refresh Token 경쟁 자체는 virtio-net 문제가 아니다. 다만 그 실험은 클라이언트에서 Nginx 와 가상 머신, K3s, Keycloak 을 거쳐 PostgreSQL 또는 Redis 까지 가는 경로를 여러 노드가 함께 쓴다. 그 경로 위의 네트워크 가상화 문제는 애플리케이션 동시성 문제와 비슷한 증상으로 나타난다.
노드 하나만 느리거나 특정 가상 머신에서만 요청이 실패하는 것을 Refresh Token 경쟁이나 데이터베이스 락으로 결론내지 않으려면, 실험 결과를 원인에 귀속하기 전에 네트워크 경로를 따로 검증한다.
## 관계
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
이 기준이 「따로 검증한다」고 말하는 계층을 세운 글이다.
- **packet 이 어디서 끊겼는지는 계층마다 capture 해서 가른다**
이 기준이 문제가 네트워크인지 아닌지를 가른다면, 그 절차는 네트워크 안에서 어느 구간인지를 가른다.
- **Host Nginx 에서 Keycloak 까지 packet 은 실제로 어디를 지나는가**
검증 순서의 첫 항목이다. 경로를 모르면 무엇을 관측할지도 정해지지 않는다.
- **부하 중 network 가상화가 지연을 바꿀 만큼 호스트 CPU 를 쓰는가**
검증 순서의 마지막 항목이고, 네트워크 처리와 CPU 경쟁을 가르는 관측이다.
- **Keycloak 동시 refresh 실험 중 CPU 가상화 계층이 결과를 흔들 만큼 포화되는가**
같은 실험을 CPU 쪽에서 본 물음이다. 두 계층이 같은 지연으로 보이므로 함께 기록한다.
## 목적
이 기준은 실험 결과를 잘못된 원인에 붙이는 일을 막는다.
Keycloak 멀티 노드 실험에서 관측되는 지연과 실패에는 네트워크 가상화 쪽 원인이 섞일 수 있다. 노드 하나만 지연되는 경우, 가상 머신 하나에서만 패킷이 유실되는 경우, 호스트 브리지 구성이 잘못된 경우, NAT(Network Address Translation) 나 conntrack 문제, 호스트 CPU 경쟁 때문에 vhost 처리가 밀리는 경우가 여기 들어간다.
이 원인들을 확인하지 않은 채 결과를 Refresh Token 경쟁이나 데이터베이스 락으로 적으면, 고칠 곳이 아닌 곳을 고치게 된다.
이 분리는 실험 결과를 보고 나서 세운 규칙이 아니다. 원문 문서는 이 실험 기반으로 검증하려는 항목을 일곱 개 적어 두었고, 그 마지막이 「네트워크 계층 문제와 애플리케이션/저장소 문제의 분리」였다.
## 규칙
### 1. 실험이 지나는 경로를 먼저 적고 시작한다
요청은 클라이언트에서 Nginx 로 가고, 거기서 가상 머신 두 대 중 하나로, 그 안의 K3s 를 지나 Keycloak 으로, 다시 PostgreSQL 또는 Redis 로 간다. 이 경로를 노드들이 공유한다.
경로를 적지 않으면 어느 관측이 어느 구간을 덮는지 정해지지 않는다.
### 2. 노드 하나에서만 나는 증상을 애플리케이션 동시성으로 먼저 읽지 않는다
노드 하나만 지연되거나 가상 머신 하나에서만 실패하는 증상은 그 노드로 가는 경로 쪽에서도 나올 수 있다. 호스트 브리지 구성 오류, NAT 와 conntrack 문제, 그 가상 머신 쪽 패킷 유실이 같은 모양으로 관측된다.
증상이 노드별로 갈리면 그 노드까지 가는 경로를 먼저 확인한다.
### 3. 애플리케이션 로그만으로 네트워크 계층을 배제하지 않는다
Keycloak 은 virtqueue 와 vhost-net, TAP, Bridge, 물리 NIC(Network Interface Card) 를 직접 알지 못하고, 게스트가 주는 소켓 위에서만 동작한다. 아래 계층이 어긋나도 애플리케이션 로그에는 느렸다는 것과 실패했다는 것만 남는다.
그래서 애플리케이션 쪽 관측을 아무리 늘려도 이 계층이 원인인지 아닌지는 갈리지 않는다.
### 4. 네트워크 처리가 쓰는 호스트 CPU 를 실험과 같은 시간축에 기록한다
vhost-net 과 QEMU 스레드, softirq 도 호스트 CPU 를 쓴다. 그래서 네트워크 문제처럼 보이는 지연이 CPU 스케줄링 문제일 수도 있다. 앞의 세 규칙이 애플리케이션 증상을 네트워크 쪽으로 되돌린다면 이 규칙은 네트워크 증상을 CPU 쪽으로 되돌린다. 방향이 반대라서 둘을 한 기준에 둔다.
부하 실험을 돌릴 때 QEMU 와 vhost 의 CPU 사용량, 호스트와 게스트의 CPU, 네트워크 지연을 같은 시간축에 남긴다. 나중에 따로 재면 그때의 부하를 다시 만들어야 한다.
### 5. 세 가지 검증을 마친 뒤에 원인을 적는다
경로가 무엇인지, 그 경로가 끊기지 않았는지, 네트워크 처리가 호스트 CPU 를 얼마나 쓰는지 셋을 확인한다.
첫째는 호스트 Nginx 에서 Keycloak 까지의 실제 패킷 경로를 추적해서, 둘째는 계층마다 캡처해서, 셋째는 부하 중 CPU 사용량을 재서 답한다.
## 적용 조건
여러 노드가 같은 네트워크 경로를 공유하는 실험의 결과를 원인에 귀속할 때.
노드별로만 나타나는 지연을 볼 때.
특정 가상 머신에서만 나는 실패를 볼 때.
부하 구간에서만 커지는 지연을 애플리케이션 동시성이나 저장소 lock 으로 결론내려 할 때.
## 예외
이 기준은 애플리케이션 쪽 관측을 늘려서는 적용되지 않는다. Keycloak 이 게스트 소켓 위에서만 동작하므로 애플리케이션 로그로는 이 계층이 원인인지 가릴 수 없다.
네트워크 계층이 깨끗하다고 해서 이 기준이 애플리케이션 결함을 배제해 주지는 않는다. 여기서 나오는 것은 네트워크가 원인이 아니라는 것까지다.
가상 머신 위 K3s 안쪽은 이 기준의 범위 밖이다. CNI(Container Network Interface) 와 Service, Pod 네트워크는 별도 계층으로 두고 따로 분석한다.
원문 문서가 이 테스트 환경에 얹어 그린 경로는 확인된 것이 아니라 기준 구조를 그대로 옮겨 놓은 그림이다. 이 저장소에는 이 기준으로 원인을 실제로 가른 실험이 아직 없다.
## 예시
- Keycloak 노드 하나만 응답이 느림 : 그 노드가 있는 가상 머신까지의 경로부터 확인한다
- 가상 머신 하나에서만 요청이 실패 : 호스트 브리지 구성과 그 가상 머신의 tap 연결을 본다
- 부하를 올릴수록 지연이 커짐 : QEMU 와 vhost 의 CPU 사용량을 같은 시간축에서 함께 본다
- 동시 갱신 실험에서 두 번째 요청이 거부됨 : 네트워크 검증 셋을 마친 뒤에 Refresh Token 경쟁으로 적는다
- 네트워크 검증이 전부 깨끗함 : 네트워크가 원인이 아니라는 것까지만 적고 애플리케이션 쪽을 계속 본다