feat(pipeline): keycloak-session-store 25편·virtualization 59편을 S3→S5→S6 으로 돌린다

기록 84편을 계약 에이전트로 다시 썼다. 기존 71편(kss 25 · virt 46)과, 계약에만
있고 안 쓰여 있던 새 글감 13편이다. 원장 84개를 열어 단계마다 스킬 영수증과 관문
종료 코드를 적었고 verify-pipeline-run.py 가 error 0 으로 닫는다.

SSOT 결함 둘을 고쳤다.

- kss 의 `약 58일` 이 반입 중 `약 59일` 로 바뀌어 있었다. 원 증거 파일이
  「남은 일수: 88일 … 실제 갱신까지 약 58일」로 산수를 직접 적는다. D-4a 쪽
  `약 59일` 은 강제 갱신 뒤(`VALID: 89 days`)라 맞는 값이라 그대로 뒀다.
- virt §198 의 `11.6GB` 는 §178 의 원 측정 `Mem: 11648`(MiB)과 어긋나는데
  원 가이드의 표기 그대로라 고치지 않고 쓰이는 자리에 대조를 적었다.

기록의 수치 오류 셋을 고쳤다 — CASE 요약의 「게스트 셋에 8240MB」(5120+3120 은
둘이다), k3s 편이 같은 것을 여섯·일곱·여덟로 세던 것, no-docker 편의 「셋을 더
든다」(§281 의 표는 네 행이고 디스크 행이 빠져 있었다).

계약을 셋 고쳤다.

- kss 의 sourceRepository 리비전이 cdac9b8 이었는데 그 커밋에는 docs/guides/**
  28개가 아예 없다. 9465582b 로 바꾸고, 반입한 바이트가 어느 커밋과도 같지 않다는
  것을 측정값과 함께 적었다 — 반입은 커밋이 아니라 그 시점의 작업 트리에서 떠 온
  것이다(kss 297/306 · virt 12/14 가 작업 트리와 같고, 200 커밋을 거슬러 전수
  대조했을 때 가장 가까운 커밋도 28개가 어긋났다).
- virt 계약이 「2026-09-11 재배분」이라고 적는데 SSOT 는 재배분 날짜를 적지 않고
  재배분 뒤 값은 이미 2026-09-10 측정에 찍혀 있다.
- kss 후보 대장이 지나친 절 아홉에 처분을 적었다(warn 9 → 0). 새 글감은 0건이고
  넷은 앵커가 h3 슬러그의 접두가 아니라 중간 토막이라 검사기가 못 본 것이었다.

style_profile.mjs 의 결함 둘을 고쳤다 — frontmatter 가 문장으로 세어져
(실측 398자짜리 「문장」 하나) 평균 길이를 기준 안으로 밀어 올리고 있었고,
engPerSent 의 분자는 목록을 포함한 글에서, 분모는 목록을 걷어낸 글에서 세고
있었다(Question 기록에서 11.94 → 3.86).

verify-pipeline.py 전 항목 PASS · error 0 · unittest 334건 OK.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
DongHyeonka
2026-09-17 11:01:55 +09:00
co-authored by Claude Opus 5
parent d473609e0a
commit 2109f726fe
574 changed files with 159654 additions and 1551 deletions
@@ -50,7 +50,7 @@ source:
# 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 멀티 노드 실험을 돌리 요청이 느려지거나 아예 닿지 않는 일이 생긴다. 이 경로를 알아 두면 그 증상을 애플리케이션·저장소 쪽 문제와 네트워크 가상화 계층 문제로 갈라 볼 수 있다.
가상 머신 안의 Keycloak 이 HTTP 요청 하나를 받으려면 그 패킷이 호스트의 물리 NIC 에서 Linux Bridge 와 TAP, vhost-net, virtqueue 를 지나 게스트 커널의 TCP/IP 스택까지 올라와야 한다. virtio-net 은 그 경로에 놓인 프로그램 하나가 아니 게스트 쪽 프런트엔드 드라이버와 호스트 쪽 백엔드를 잇는 I/O 계약이고, 그 백엔드 자리는 QEMU 사용자 공간이 맡을 수도 호스트 커널의 vhost-net 이 맡을 수도 있다. 가상 머신 두 대 Keycloak 멀티 노드 실험을 돌리 요청이 느려지거나 아예 닿지 않으면, 이 경로를 알아 그 증상을 애플리케이션·저장소 쪽 문제와 네트워크 가상화 계층 문제로 갈라 볼 수 있다.
## 관계
@@ -59,7 +59,7 @@ source:
- **Keycloak 실험 결과를 애플리케이션 원인으로 읽기 전에 network 계층을 따로 검증한다**
이 경로가 실험의 여러 노드에 공유되기 때문에 생기는 오귀속을 막는 기준이다.
- **이 호스트의 가상 머신 네트워크는 Bridge 인가 NAT 인가 Routing 인가**
이 글은 Bridge 와 라우팅, NAT 가 각각 무엇을 하는지까지만 적었다. 이 호스트가 그중 무엇으로 구성돼 있는지는 확인하지 않았다.
이 글은 Bridge 와 라우팅, NAT 가 각각 무엇을 하는지까지만 적었다. 이 호스트가 NAT 로 돈다는 것은 실험대를 세운 기록이 나중에 적었고, 그 구성에서 프레임이 어느 계층을 지나는지는 그 물음이 받는다.
- **VM1 과 VM2 의 TAP/vnet interface 는 무엇이고 어디에 붙어 있는가**
TAP 이 게스트의 이더넷 프레임과 호스트 네트워크를 잇는 접점이라는 설명이 이 물음의 전제다.
- **이 호스트에서 vhost-net 이 실제로 packet datapath 를 맡고 있는가**
@@ -254,7 +254,7 @@ Intel Physical NIC
네트워크 성능에서 큰 비용 하나가 패킷 데이터를 복사하는 일이다. virtio 와 virtqueue, vhost 구조는 버퍼 디스크립터를 써서 불필요한 복사와 컨텍스트 스위치를 줄이는 방향으로 설계돼 있다. 다만 이것을 항상 zero-copy 라고 일반화하지 않는다. 실제로 복사가 일어나는지는 커널 버전과 QEMU 버전, vhost 설정, 오프로드, NIC 의 기능, 패킷이 지나는 경로, GSO/GRO/TSO 에 따라 달라질 수 있다.
게스트와 호스트는 큐에 새 패킷이나 버퍼가 들어왔다는 것을 서로 알려야 한다. 게스트가 보낼 때는 virtqueue 에 디스크립터를 등록하고 호스트 백엔드에 알리면 백엔드가 처리하고, 받을 때는 호스트가 virtqueue 에 버퍼를 반영하고 게스트에 알리면 게스트 드라이버가 처리한다. 패킷마다 인터럽트와 알림이 지나치게 많이 발생하면 오버헤드가 커지기 때문에 batching 과 interrupt moderation, queueing 을 쓴다.
게스트와 호스트는 큐에 새 패킷이나 버퍼가 들어왔다는 것을 서로 알려야 한다. 게스트가 보낼 때는 virtqueue 에 디스크립터를 등록하고 호스트 백엔드에 알리면 백엔드가 처리한다. 받을 때는 호스트가 virtqueue 에 버퍼를 반영하고 게스트에 알리면 게스트 드라이버가 처리한다. 패킷마다 인터럽트와 알림이 지나치게 많이 발생하면 오버헤드가 커지기 때문에 batching 과 interrupt moderation, queueing 을 쓴다.
큐를 하나만 쓰면 특정 vCPU 나 처리 경로에 부하가 몰릴 수 있다. virtio-net 은 multi-queue 를 쓸 수 있고, RX 큐를 vCPU 마다 하나씩 붙여 패킷 처리를 병렬로 돌리고 큐 하나에 몰리는 병목을 완화한다. 효과는 부하의 성격과 CPU affinity, IRQ(Interrupt Request) 배치, 큐 설정에 따라 달라진다.
@@ -328,11 +328,11 @@ sudo tcpdump -ni <guest-interface>
## 이 문서가 확인하지 않은 것
이 호스트에서 잰 값은 하나도 없다. 위의 명령들은 무엇을 볼 수 있는지 적어 둔 목록이고 아직 실행하지 않았다. 이 가상 머신들의 네트워크가 Bridge 인지 NAT 인지 라우팅인지, VM1 과 VM2 의 TAP 또는 vnet 인터페이스가 무엇인지를 아직 확인하지 않았다. vhost-net 이 실제로 데이터 경로를 맡고 있는지, multi-queue 가 켜져 있는지도 마찬가지다. 그래서 이 글은 「이 구조에서는 이렇게 동작한다」까지만 말하고 「이 서버가 그 구조다」라고는 말하지 않는다. QEMU 백엔드와 vhost-net 의 성능 차이가 이 호스트에서 관찰되는지, Keycloak 부하 시험에서 네트워크 가상화가 지연에 영향을 줄 만큼 호스트 CPU 를 쓰는지도 같은 상태다.
위의 명령을 이 호스트에서 돌린 출력은 대부분 없다. 네트워크 구성만은 나중에 갈렸다. 실험대를 세운 기록이 이 호스트에 이더넷 없이 WiFi 만 있어 브리지를 못 쓰고 libvirt NAT 를 골랐다고 적었고, 게스트는 그 NAT 가 만드는 브리지에 붙는다. 브리지를 막은 것은 무선 링크다. 802.11 데이터 프레임은 기본적으로 주소 필드가 3개라, AP(Access Point, 무선 접속 장치) 는 연결된 단말의 MAC 만 알고 있고 그 단말이 자기 것이 아닌 출발지 MAC 을 단 프레임을 보내면 버린다. 브리지된 가상 머신이 보내는 것이 정확히 그런 프레임이다. 우회로 4-address 모드(WDS) 가 있지만 AP 와 클라이언트 드라이버가 모두 지원해야 하고 실제로는 거의 지원되지 않아, 그 기록은 현실적인 우회로 USB 이더넷 어댑터를 들었다. 같은 기록이 NAT 와 브리지와 macvtap 을 견준 표에서 뒤의 둘은 나란히 WiFi 라 불가다. TAP 또는 vnet 인터페이스의 이름과 vhost-net 이 실제로 데이터 경로를 맡는지, multi-queue 가 켜져 있는지는 아직 읽지 않았다. 그래서 이 글은 「이 구조에서는 이렇게 동작한다」까지만 말하고 「이 서버가 그 구조다」라고는 말하지 않는다. QEMU 백엔드와 vhost-net 의 성능 차이가 이 호스트에서 관찰되는지, Keycloak 부하 시험에서 네트워크 가상화가 지연에 영향을 줄 만큼 호스트 CPU 를 쓰는지도 재지 않았다.
이 문서가 Keycloak 테스트 환경에 얹어 그린 경로도 마찬가지다. 호스트 Nginx 아래에 가상 머신 두 대가 있고 그 안에서 K3s 와 Keycloak 노드가 돈다는 앞부분은 실험 구성으로 밝혀 두었다. 그 아래를 브리지와 라우팅·NAT, TAP, vhost-net, virtqueue 로 펼친 뒷부분은 네트워크 구성과 TAP 이름, vhost-net 사용 여부를 확인하기 전의 가정이다. 이 환경이 실제로 그렇다는 주장이어서, 재기 전에는 이 글의 그림으로 올리지 않고 근거가 모자란 후보로 남겼다.
이 문서가 Keycloak 테스트 환경에 얹어 그린 경로는 그대로 쓰지 못한다. 그 그림은 호스트의 nginx 가상 머신 두 대로 프록시하는 모양인데, 실험대는 그 뒤에 nginx 를 게스트 한 대로 옮기고 호스트에는 커널 DNAT(Destination NAT, 목적지 주소 변환) 만 두었다. 게스트도 둘이 아니라 셋이고, 엣지 한 대와 K3s 노드 두 대다. 브리지와 라우팅·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 의 기능에 따라 달라지므로, 버전이 바뀌어 이 서술이 낡았는지는 위 확인 명령을 실제 호스트에서 돌릴 때 드러난다.
여기 적은 것은 가장 기본적인 조합 안에서만 성립한다. 실제 환경은 Bridge 와 NAT, routed network, macvtap, SR-IOV, VFIO(Virtual Function I/O) passthrough, Open vSwitch, Kubernetes CNI(Container Network Interface) 에 따라 달라질 수 있다. 이 서술 자체는 판 번호를 달고 있지 않다. 실험대를 잰 기록이 그 호스트의 libvirt 12.7.0 과 QEMU 11.1.1, 커널 7.2.2-arch1-1 을 적었다. 여기 적은 동작은 그 판에서 읽은 것이 아니라 구조를 서술한 것이라, 세 값은 확인할 때의 조건으로 쓴다. 복사 동작 하나만 봐도 커널 버전과 QEMU 버전, vhost 설정, 오프로드, NIC 의 기능에 따라 달라지므로, 버전이 바뀌어 이 서술이 낡았는지는 위 확인 명령을 실제 호스트에서 돌릴 때 드러난다.
가상 머신 위에 올린 K3s 안쪽도 이 문서 밖이다. K3s 의 CNI 와 Service, Pod 네트워크는 이 네트워크 가상화 위에 더해지는 계층이라 따로 분석한다.
@@ -20,7 +20,7 @@ source:
# Host Nginx 에서 Keycloak 까지 packet 은 실제로 어디를 지나는가
§116 은 이 테스트 환경의 요청 경로를 클라이언트에서 Keycloak 까지 한 줄로 그렸다. 가상 머신 네트워크까지 펼치면 물리 NIC 에서 브리지와 TAP 을 지나 vhost-net 과 virtqueue 를 거쳐 게스트 안으로 들어간다고 적었다. 그 펼친 그림은 §94 가 기준으로 삼은 구조를 이 환경에 얹은 것이고, 이 호스트에서 패킷을 잡아 본 결과가 아니다. 여기서 묻는 것은 호스트 Nginx 를 지난 요청이 실제로 어느 브리지와 어느 TAP 을 거쳐 어느 가상 머신으로 들어가는가 하나다.
경로가 한 번 바뀌었다. §179 는 nginx 를 물리 호스트에서 엣지 게스트로 옮기고 호스트에는 커널 DNAT(Destination NAT, 목적지 주소 변환) 만 두었다고 적었다. §116 이 그린 그림은 옮기기 전 모양이고, 네 지점에 tcpdump 를 걸어 본 기록은 아직 없다.
## 관계
@@ -42,7 +42,7 @@ source:
- §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 에 따라 달라질 수 있다고 밝혔다.
- §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 가 든 판정 예 셋
@@ -50,20 +50,34 @@ source:
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 의 기준 구조를 이 환경에 얹은 그림이다.
- §116 의 그림은 앞부분과 뒷부분의 근거가 다르다. 호스트 Nginx 아래에 가상 머신 두 대가 있고 그 안에서 K3s 와 Keycloak 노드가 돈다는 앞부분은 §89 가 밝힌 실험 구성이다. 브리지와 라우팅·NAT, TAP, vhost-net 으로 펼친 뒷부분은 OQ-1 과 OQ-2, OQ-3 를 확인하기 전의 가정이다.
- §179 와 §254 는 같은 nginx 를 물리 호스트에서 엣지 게스트로 옮겼다고 적고 전후를 나란히 그렸다.
전 : `tailnet:443` → 호스트 nginx → Traefik(게스트 .11/.12)
후 : `tailnet:443` → 호스트 커널 DNAT → 엣지 nginx(.10) → Traefik(.11/.12)
- §179 는 그렇게 옮겨도 L7 홉 수는 2홉 그대로라고 관측으로 적었다. 늘어난 것은 커널이 하는 L4 전달 한 번이다. 지금 호스트에는 `:443` 을 듣는 리스너가 없다.
- §179 는 옮긴 이유를 성능이 아니라 더러워지는 층의 격리로 적었다. nginx 설정과 인증서, certbot, deploy 훅은 자주 갈아엎는 것들인데 호스트에 있으면 초기화가 불가능하고, 엣지 장애 실험이 SSH 까지 위험하게 만든다. 그 대가로 일곱 가지가 새로 필요해졌고 §179 는 그중 DNAT 와 libvirt 방화벽 구멍 둘을 이 이동의 본질로 꼽았다. 그 둘을 본질로 본 것은 추론이라고 밝혔다.
- §254 는 호스트에서 게스트로 가는 요청이 OUTPUT 경로라 필터를 타지 않는다고 적었다. 밖에서 게스트로 들어오는 요청은 FORWARD 경로라 libvirt 의 guest_input 체인을 지난다.
- §207 은 DNAT 파일에 forward 체인을 두지 않은 이유를 그 파일의 주석으로 적어 두었다. libvirt 의 guest_input 체인은 virbr0 으로 나가는 프레임을 reject 하며 끝난다. nftables 에서는 앞 base 체인의 accept 가 뒤 체인의 reject 를 멈추지 못하므로 구멍을 libvirt 체인 맨 앞에 뚫었고, 그래서 그 파일에 forward 체인이 없는 것은 실수가 아니다.
- §207 은 이 DNAT 가 PREROUTING nat 에 있어 라우팅 결정보다 먼저 돌고 호스트의 로컬 소켓보다 이긴다고 적었다. 그래서 엣지를 띄운 채 전환하고, 되돌릴 때는 테이블 하나를 지운다.
- §207 은 DNAT 만 하고 SNAT 는 하지 않는다고 적었다. 게스트의 기본 경로가 호스트여서 응답이 이 경로를 다시 지나고 conntrack 이 변환을 알아서 되돌린다. masquerade 를 붙이면 엣지가 모든 클라이언트를 192.168.122.1 로 보게 된다.
- §208 은 DNAT 유닛의 ExecStartPost 앞에 붙은 하이픈을 설명했다. libvirt_network 테이블은 가상 네트워크가 떠 있어야 존재하는데, 부팅 순서에 따라 이 유닛이 먼저 돌 수 있다. 하이픈이 없으면 그때 규칙 삽입이 실패하면서 DNAT 까지 같이 안 실린다. 하이픈을 두면 DNAT 는 실리고 구멍만 빠진 상태가 되어 systemctl restart 한 번으로 다시 뚫린다.
- §208 은 그 상태에서 유닛이 active 이고 아무 오류도 없다고 적고, 유닛이 active 라는 것은 DNAT 가 실렸다는 뜻이지 구멍이 뚫렸다는 뜻이 아니라고 추론으로 밝혔다. 그 상태를 실제로 재현해 보지는 않았다고 적혀 있다.
- §180 은 구멍이 빠졌을 때의 출력을 관측으로 남겼다. 호스트에서 게스트 주소로 친 curl 은 404 였고, 밖에서 tailnet 주소로 친 curl 은 connection refused 였다. reject 규칙의 카운터는 packets 4 bytes 240 으로 밖에서 친 횟수와 맞았다.
- §254 는 안쪽과 바깥쪽을 한 번씩 쳐서 같은 값이 나오는지 보는 확인을 적었다. 둘 다 404 면 경로가 이어진 것이고, 안쪽만 404 면 nginx 설치 · DNAT · libvirt 구멍 셋 가운데 하나가 빠진 것이다.
- 이 호스트에서 계층마다 패킷을 잡아 본 기록은 없다. §116 이 펼친 경로는 확인한 결과가 아니라 §94 의 기준 구조를 이 환경에 얹은 그림이다.
## 가정
- 호스트 Nginx 를 통해 Keycloak 요청을 한 번 보낼 수 있고, 그 요청이 두 가상 머신 가운데 어느 쪽으로 갔는지 확인하는 쪽이 알 수 있다고 본다.
- 밖에서 tailnet 주소로 Keycloak 요청을 한 번 보낼 수 있고, 그 요청이 두 k3s 노드 가운데 어느 쪽으로 갔는지 확인하는 쪽이 알 수 있다고 본다.
- 네 지점에 동시에 캡처를 걸어 둘 수 있다고 전제한다. 지점마다 따로 걸면 같은 요청을 본 것인지 갈리지 않는다.
- §116 이 그린 구조가 지금 실험 환경과 같다고 전제한다. 가상 머신이 둘이고 각각 K3s 노드와 Keycloak 을 돌린다는 것까지가 근거 문서에 적힌 전부다.
- §116 이 그린 뒷부분 가운데 TAP 다음 구간은 지금도 같다고 전제한다. 앞부분은 §179 가 바꿔 놓았고, TAP 뒤쪽 서술이 그 이동으로 달라졌는지는 적힌 것이 없다.
- 오프로드 설정이 캡처하는 동안 바뀌지 않는다.
## 미지수
- 호스트 Nginx 를 지난 요청이 어느 브리지를 거치는지, 그 브리지에서 어느 TAP 으로 나가는지.
- 그 TAP 이 두 가상 머신 가운데 어느 쪽의 인터페이스인지.
- 밖에서 온 요청이 호스트 커널 DNAT 를 지난 뒤 virbr0 에서 어느 TAP 으로 나가 엣지 게스트로 들어가는지.
- 엣지 nginx 가 k3s 노드로 넘기는 두 번째 홉이 virbr0 안에서 L2 로만 도는지, 호스트의 L3/Netfilter 를 지나는지. §119 의 네 지점은 밖에서 게스트로 들어오는 한 홉만 본다.
- 그 두 번째 홉의 TAP 이 kc-lab-1 과 kc-lab-2 가운데 어느 쪽의 인터페이스인지.
- 게스트 안의 인터페이스에서 같은 요청이 보이는지.
- 네 지점의 결과가 §116 이 펼친 그림과 같은지, 다르다면 어느 지점부터 다른지.
- 지금 오프로드 설정이 무엇인지. 캡처에서 본 패킷 크기와 체크섬을 wire 값으로 읽어도 되는지가 이것으로 갈린다.
@@ -73,13 +87,14 @@ source:
- 캡처 지점의 이름을 먼저 확정해야 한다. 어느 브리지와 어느 TAP 인지 모르면 tcpdump 를 걸 곳을 고르지 못한다. 그 이름은 네트워크 구성을 묻는 물음과 인터페이스를 묻는 물음이 댄다.
- K3s 안에서 Keycloak Pod 까지 가는 구간은 이 물음이 닫는 범위 밖이다. §116 이 CNI · Service · Pod network 를 별도 계층으로 미뤄 두었다.
- 요청이 중간 지점에서 끊기면 그것은 이 물음의 답이 아니라 장애다. §119 가 든 세 판정 예를 따라 의심 구간을 적고 계층별 캡처 기준으로 넘긴다.
- 이 호스트에서 잰 값이 없어 §116 의 그림을 확인된 경로로 삼지 않는다. 이 환경이 실제로 그렇다는 주장이라 재기 전에는 Case 도 Concept 도 아니라고 보고 근거가 모자란 후보로 남겨 두었으며, OQ-1 과 OQ-2, OQ-3, OQ-6 이 답하면 다시 판정한다.
- §116 의 그림은 엣지를 옮기기 전 모양이다. 지금 구성으로 캡처하면 호스트 nginx 였던 칸에 커널 DNAT 와 엣지 게스트가 들어가므로 캡처 지점이 그만큼 늘어난다.
- 이 호스트에서 잰 값이 없어 §116 의 그림을 확인된 경로로 삼지 않는다. 이 환경이 실제로 그렇다는 주장이라 재기 전에는 Case 도 Concept 도 아니라고 보고 근거가 모자란 후보로 남겨 두었다. OQ-1 과 OQ-2, OQ-3, OQ-6 이 답하면 다시 판정한다.
## 선택지
### 1. 네 지점에 한 번에 걸고 요청을 한 번 보낸다
§119 가 든 호스트 세 지점과 게스트 한 지점에 동시에 tcpdump 를 걸어 두고, 호스트 Nginx 를 통해 Keycloak 요청을 한 번 보낸다. 같은 요청 하나가 네 지점에서 각각 보이는지로 경로가 확정되기 때문에 실행이 한 번으로 끝난다.
§119 가 든 호스트 세 지점과 게스트 한 지점에 동시에 tcpdump 를 걸어 두고, 밖에서 tailnet 주소로 Keycloak 요청을 한 번 보낸다. 같은 요청 하나가 네 지점에서 각각 보이는지로 경로가 확정되기 때문에 실행이 한 번으로 끝난다.
지점 이름을 미리 확정해 두어야 하고 네 곳을 동시에 열어 두어야 한다.
@@ -102,8 +117,8 @@ Nginx 가 어느 주소로 요청을 넘기는지 설정만 읽어도 어느 가
## 다음 검증
1. 캡처를 걸 지점의 이름을 먼저 확정한다. 어느 브리지와 어느 TAP 인지는 네트워크 구성을 묻는 물음과 인터페이스를 묻는 물음이 답한다.
2. 호스트에서 sudo tcpdump -ni 뒤에 physical NIC 이름 · bridge 이름 · tap 또는 vnet 이름을 넣어 세 곳에 걸고, 게스트에서 같은 명령을 guest interface 이름에 건다 (§122 OQ-6 · §119).
3. 호스트 Nginx 를 통해 Keycloak 요청을 한 번 보낸다.
2. 호스트에서 sudo tcpdump -ni 뒤에 physical NIC 이름 · bridge 이름 · tap 또는 vnet 이름을 넣어 세 곳에 걸고, 게스트에서 같은 명령을 guest interface 이름에 건다 (§122 OQ-6 · §119). 엣지 게스트의 TAP 과 k3s 노드의 TAP 을 함께 열어야 §179 가 적은 두 홉이 한 실행에서 잡힌다.
3. 밖에서 tailnet 주소로 Keycloak 요청을 한 번 보낸다. §179 대로라면 그 요청은 호스트 커널 DNAT 를 지나 엣지 nginx 로 가고, 거기서 다시 k3s 노드로 넘어간다.
4. 네 지점에서 그 요청이 보였는지를 §119 처럼 O 와 X 로 적는다.
5. 캡처하는 동안의 오프로드 상태를 함께 적는다. §113 은 오프로드가 켜져 있으면 tcpdump 에서 보이는 패킷 크기나 체크섬이 실제 wire 에서 보이는 것과 다르게 보일 수 있다고 적었다.
@@ -22,7 +22,7 @@ source:
# 이 호스트에서 vhost-net 이 실제로 packet datapath 를 맡고 있는가
§103 은 패킷이 TAP 에서 게스트로 올라오는 길을 두 가지로 갈라 적었다. QEMU 백엔드를 직접 쓰면 QEMU virtio backend 가 그 사이에 들어가고, vhost-net 을 쓰면 호스트 커널의 vhost-net 이 들어간다. 둘 중 어느 쪽이 이 호스트에서 돌고 있는지 SSOT 에 없어서, 이 물음은 가상 머신 두 대 각각의 데이터 경로 백엔드를 확정한다.
§103 은 패킷이 TAP 에서 게스트로 올라오는 길을 로 갈라 적었다. QEMU 백엔드를 쓰면 그 사이에 QEMU virtio backend 가 들어가고, vhost-net 을 쓰면 호스트 커널의 vhost-net 이 들어간다. 어느 쪽으로 도는지 SSOT 에 없어서, 이 물음은 가상 머신 두 대의 데이터 경로 백엔드를 각각 확정한다.
## 관계
@@ -43,15 +43,17 @@ source:
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 비용이 커질 수 있다고 적었다.
- §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 를 들었다.
- §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 에 없다.
- §123 은 개념에서 열린 질문을 거쳐 Case 로 가는 순서를 설명하면서 이 물음을 예로 들었다. 개념 자리에 「vhost-net은 QEMU userspace를 우회해 packet datapath를 처리할 수 있다」를, 물음 자리에 「현재 테스트 Host에서 vhost-net이 실제 활성화되어 있는가?」를 놓았다. 답이 나오면 「libvirt/QEMU virtio-net backend 구성 확인 및 vhost-net 사용 검증」이 Case 가 된다고 적었다.
- §178 은 이 실험대의 게스트를 3대로 적었고, §202 의 철거 출력이 그 domain 이름을 kc-lab-edge · kc-lab-1 · kc-lab-2 로 남겼다. §122 OQ-3 이 vm1 과 vm2 로 적은 두 대가 그중 k3s 노드 쪽이다.
- §197 은 이 호스트의 판 번호를 실측으로 적었다. libvirt 12.7.0 · QEMU emulator version 11.1.1 · 커널 7.2.2-arch1-1 이다. 어느 backend 로 도는지는 그 줄에서 나오지 않는다.
- 실험대를 적은 제5부부터 제9부까지 vhost 라는 낱말이 한 번도 나오지 않는다. 두 가상 머신이 어느 백엔드로 도는지도, vhost 모듈이 올라와 있는지도 확인한 기록이 SSOT 에 없다.
## 가정
@@ -59,6 +61,7 @@ source:
- 모듈이 올라와 있다는 것과 그 가상 머신이 그 백엔드를 쓴다는 것을 다른 사실로 놓고 물음을 세웠다. §103 은 백엔드 선택을 가상 머신의 구성으로 적었지 모듈 적재로 적지 않았다.
- 두 가상 머신이 같은 백엔드를 쓴다고 전제하지 않는다. 가상 머신마다 따로 읽어 각각 적는다.
- 읽는 동안 가상 머신을 재시작하지 않는다고 전제한다. 백엔드는 §105 가 적은 설정 경로에서 정해지므로 재시작하면 달라질 수 있다.
- 게스트가 떠 있어야 QEMU 실행 인자를 읽는다. SSOT 가 마지막으로 적은 게스트 상태는 §202 의 철거이고 그 뒤 기록이 없다.
## 미지수
@@ -94,7 +97,7 @@ lsmod | grep vhost 로 호스트에 그 기능이 있는지 먼저 보고, 나
## 다음 검증
1. lsmod | grep vhost 로 vhost 커널 모듈이 올라와 있는지 본다.
2. 두 가상 머신의 libvirt domain 설정에서 인터페이스의 driver 지정을 읽는다. 무엇으로 읽었는지 명령을 함께 적는다.
2. libvirt domain 설정에서 인터페이스의 driver 지정을 읽는다. domain 이름은 §202 가 적은 kc-lab-1 과 kc-lab-2 를 쓰고, 엣지 게스트 kc-lab-edge 도 같이 읽는다. 무엇으로 읽었는지 명령을 함께 적는다.
3. 같은 가상 머신의 QEMU 실행 인자에서 백엔드 지정을 읽는다. 이것도 명령을 함께 적는다.
4. 세 출력을 가상 머신별로 짝지어 적고 실행한 명령과 함께 증거로 남긴다.
@@ -20,7 +20,7 @@ source:
# 부하 중 network 가상화가 지연을 바꿀 만큼 호스트 CPU 를 쓰는가
§117.7 은 vhost-net 과 QEMU 스레드와 softirq 도 호스트 CPU 를 쓰므로, 네트워크 문제처럼 보이는 것이 CPU 스케줄링 문제일 수 있다고 적었다. 그 셋 가운데 QEMU 스레드와 게스트 쪽 지표는 CPU 가상화를 다루는 물음 둘이 이미 같은 실험 구간에서 재기로 해 두었다. 그래서 여기서는 그 둘이 보지 않는 vhost 커널 스레드와 softirq 만 본다. Keycloak 부하 시험 구간에서 이 둘이 호스트 CPU 를 얼마나 쓰는지, 그 사용량이 네트워크 지연과 같이 움직이는지로 물음을 좁힌다.
§117.7 은 vhost-net 과 QEMU 스레드와 softirq 도 호스트 CPU 를 쓰므로, 네트워크 문제처럼 보이는 것이 CPU 스케줄링 문제일 수 있다고 적었다. 그 셋 가운데 QEMU 스레드와 게스트 쪽 지표는 CPU 가상화를 다루는 물음 둘이 같은 실험 구간에서 이미 재기로 해 두었다. 그래서 여기서는 Keycloak 부하 시험 구간에서 vhost 커널 스레드와 softirq 호스트 CPU 를 얼마나 쓰는지, 그 사용량이 네트워크 지연과 같이 움직이는지만 본다.
## 관계
@@ -38,8 +38,8 @@ source:
## 사실
- §117.7 은 vhost-net · QEMU thread · softirq 도 호스트 CPU 를 쓰고, 따라서 네트워크 문제처럼 보여도 CPU 스케줄링 문제일 수 있다고 적었다.
- §107 은 QEMU 사용자 공간이 패킷마다 I/O 를 처리하면 호스트 커널과 사용자 공간을 오가는 전환 비용이 쌓이고, 초당 지나는 패킷이 많아질수록 userspace/kernel transition · scheduling · copy · notification 비용이 커질 수 있다고 밝혔다.
- §111 은 게스트와 호스트가 큐에 새 패킷이나 버퍼가 있음을 서로 알려야 한다고 적고, 패킷마다 인터럽트나 알림이 지나치게 많이 발생하면 오버헤드가 커지므로 batching · interrupt moderation · queueing 이 중요하다고 밝혔다.
- §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 요청만 지연
@@ -57,6 +57,9 @@ source:
- 이 여섯 가운데 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 을 계속 맡는다고 적었다.
- §179 는 nginx 를 물리 호스트에서 엣지 게스트로 옮기고 호스트에는 커널 DNAT 만 두었다고 관측으로 적었다. 지금 호스트에는 `:443` 을 듣는 리스너가 없다.
- §197 과 §218 은 이 호스트의 논리 코어가 8 이고 게스트 셋에 vCPU 2 · 2 · 1 을 배분했다고 적었다. 부하 구간의 호스트 CPU 사용량은 이 수와 나란히 읽는다.
- §192 가 적은 스크레이프 대상은 keycloak · kubelet · node-exporter · prometheus 넷이고 node-exporter 는 게스트 노드마다 하나씩 붙는다. §193 은 이 실험대가 호스트 쪽 지표를 긁지 않는다고 적었다.
- 이 호스트에서 부하 구간의 vhost 스레드 CPU 사용량이나 softirq 시간을 잰 기록이 없다.
## 가정
@@ -65,13 +68,13 @@ source:
- 호스트에서 vhost 커널 스레드를 스레드 단위로 구분해 볼 수 있다고 전제한다. §108 이 vhost-net 을 써도 QEMU 가 설정과 수명 주기를 계속 맡는다고 적었으므로, QEMU 프로세스의 CPU 사용량과 vhost 커널 스레드의 CPU 사용량을 따로 세야 한다.
- 부하 도구가 네트워크 지연을 이미 내고 있다고 전제한다. 그 도구가 어떤 값을 어떤 주기로 내는지는 근거 문서에 적혀 있지 않다.
- 호스트와 게스트 둘의 시각을 맞춰 읽을 수 있다. 시계가 어긋나면 세 값을 같은 부하 구간에 겹쳐 놓지 못한다.
- 호스트 쪽 Nginx 도 같은 물리 CPU 를 쓴다. 부하 구간의 호스트 CPU 상승을 전부 네트워크 가상화 몫으로 읽으면 이 전제가 깨진다.
- 엣지 게스트의 nginx 가 쓰는 CPU 는 그 게스트를 돌리는 QEMU 프로세스 쪽에 나타난다고 본다. §179 가 nginx 를 게스트로 옮긴 뒤로 호스트에는 그 프로세스가 없으므로, 부하 구간의 호스트 CPU 상승을 전부 네트워크 가상화 몫으로 읽으면 이 전제가 깨진다.
## 미지수
- 부하를 걸기 전 vhost 커널 스레드의 CPU 사용량과 softirq 시간.
- 부하 구간에서 그 둘이 얼마나 오르는지.
- 오른 몫이 QEMU vCPU 스레드 사용량과 어떻게 나뉘는지.
- 오른 몫이 QEMU vCPU 스레드 사용량과 어떻게 나뉘는지. 게스트가 셋이라 엣지 게스트를 돌리는 QEMU 프로세스의 몫도 k3s 노드 두 대와 갈라 세야 한다.
- vhost 커널 스레드와 softirq 의 움직임이 네트워크 지연과 같은 시간축에서 함께 움직이는지.
- 두 지표가 얼마나 움직여야 실험 결과를 다르게 읽어야 하는지. 그 문턱을 아직 정하지 않았다.
- softirq 시간을 이 환경에서 어떤 도구로 읽는지. 근거 문서가 명령을 적지 않아 실행하는 쪽이 정한다.
@@ -81,7 +84,9 @@ source:
- 실험 조건을 바꾸지 않고 관찰만 덧붙인다. CPU 가상화 쪽 물음이 같은 실험 구간을 쓰기로 되어 있어 부하 수준을 바꾸면 두 기록을 겹쳐 읽지 못한다.
- 기준값을 먼저 찍는다. 부하 구간의 값만 있으면 그것이 평소 값인지 부하 때문에 오른 값인지 판정할 수 없다.
- 이 물음이 새로 만드는 값은 vhost 커널 스레드 사용량과 softirq 시간 둘이다. QEMU CPU 와 Guest CPU 와 steal time 은 CPU 가상화 쪽 기록을 그대로 쓰고, 네트워크 지연은 부하 도구가 낸 값을 쓴다.
- 호스트 쪽 값은 손으로 읽어 남긴다. §192 의 관측 스택은 게스트 안에서 돌고 §193 이 적은 대로 호스트 지표를 긁지 않는다. §193 은 node-exporter 가 게스트 커널이 내놓는 값을 읽으므로 그 값이 전부 게스트가 본 것이고 호스트에서 같은 것을 재면 다른 수가 나올 수 있다고 추론으로 덧붙였다.
- 두 지표가 움직이지 않았다는 결과가 나와도 Refresh Token 실험의 결론이 바뀌지는 않는다. §120 이 Refresh Token 경쟁과 네트워크 가상화를 별개 문제로 놓았기 때문에, 여기서 갈리는 것은 그 실험 결과를 애플리케이션과 저장소 쪽으로 읽어도 되는지 하나다.
- 스레드가 어느 논리 CPU 에서 돌았는지는 §14.7 의 psr 로 읽히지만, 그 번호가 어느 물리 코어인지는 SSOT 에서 나오지 않는다. §197 이 남긴 lscpu 출력이 grep 으로 걸러져 Model name 과 CPU(s), Thread(s) per core, Core(s) per socket 네 줄뿐이기 때문이다. vhost 스레드를 어느 CPU 에 둘지를 나중에 정하려면 그 배치를 호스트에서 다시 읽어야 한다.
- 이 호스트에서 잰 값이 없어 다른 장비의 수치를 근거로 삼지 않는다.
## 선택지
@@ -20,18 +20,18 @@ source:
# QEMU backend 와 vhost-net 의 차이가 이 호스트에서 실제로 보이는가
backend 는 virtqueue 를 사이에 두고 게스트와 버퍼를 주고받는 호스트 쪽을 가리킨다. §107 은 패킷 처리를 QEMU 사용자 공간(userspace)에서 호스트 커널로 옮기면 컨텍스트 스위치와 사용자 공간 오버헤드가 줄어든다고 적었다. 줄어든다는 방향만 적혀 있을 뿐 이 호스트에서 두 backend 를 나란히 재 본 값은 없다. §110 실제로 복사가 일어나는지 커널 버전과 offload 를 비롯한 여러 조건에 따라 달라질 수 있다고 밝혔으므로, 다른 환경에서 나온 수치를 이 호스트 값으로 옮겨 쓸 수도 없다. 이 물음은 §122 OQ-4 가 든 여섯 축을 이 호스트에서 나란히 재서 차이가 보이는지 가른다.
백엔드(backend)는 virtqueue 를 사이에 두고 게스트와 버퍼를 주고받는 호스트 쪽을 가리킨다. §107 은 패킷 처리를 QEMU 사용자 공간(userspace)에서 호스트 커널로 옮기면 컨텍스트 스위치와 사용자 공간 오버헤드가 줄어든다고 적었지만, 방향만 있을 뿐 이 호스트에서 두 백엔드를 나란히 재 본 값은 없다. §110 실제로 복사가 일어나는지 커널 버전과 offload 를 비롯한 여러 조건에 따라 달라질 수 있다고 밝혔으, 다른 환경 수치를 이 호스트 값으로 옮겨 쓸 수도 없다. 이 물음은 §122 OQ-4 가 든 여섯 축을 이 호스트에서 나란히 재서 차이가 보이는지 가른다.
## 관계
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
그 경로에서 두 backend 가 갈리는 곳은 TAP 다음 한 칸이고, 이 물음은 거기서 무엇이 달라지는지를 잰다.
그 경로에서 두 백엔드가 갈리는 곳은 TAP 다음 한 칸이고, 이 물음은 거기서 무엇이 달라지는지를 잰다.
- **이 호스트에서 vhost-net 이 실제로 packet datapath 를 맡고 있는가**
지금 어느 쪽으로 돌고 있는지가 정해져야 견줄 두 값 가운데 한쪽이 고정된다.
- **부하 중 network 가상화가 지연을 바꿀 만큼 호스트 CPU 를 쓰는가**
두 물음이 QEMU CPU 와 Host CPU 를 같은 부하에서 읽으므로 측정을 한 번으로 묶을 수 있다.
- **Keycloak 실험 결과를 애플리케이션 원인으로 읽기 전에 network 계층을 따로 검증한다**
backend 차이가 지연에 얼마나 들어오는지를 이 물음이 재면, 그 기준이 그 값을 가져다 쓴다.
백엔드 차이가 지연에 얼마나 들어오는지를 이 물음이 재면, 그 기준이 그 값을 가져다 쓴다.
## 사실
@@ -40,12 +40,12 @@ backend 는 virtqueue 를 사이에 두고 게스트와 버퍼를 주고받는
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 라고 일반화하면 안 된다고 못 박았다. 실제로 복사가 일어나는지는 아래에 따라 달라질 수 있다.
- §107 이 든 최적화 방향은 패킷마다 QEMU 사용자 공간이 끼어들던 처리를 커널 백엔드로 옮겨 컨텍스트 스위치와 사용자 공간 오버헤드를 줄이는 것이다.
- §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 를 들었다.
- §117.4 는 초당 패킷 수가 높은 구간에서 QEMU 사용자 공간이 처리 경로를 직접 맡으면 CPU 오버헤드가 커질 수 있다고 밝혔다. 관찰할 것으로 QEMU CPU usage · vhost thread · packet rate · latency · context switch 를 들었다.
- §122 OQ-4 는 비교할 축 여섯을 적었다.
Latency
Throughput
@@ -54,53 +54,59 @@ backend 는 virtqueue 를 사이에 두고 게스트와 버퍼를 주고받는
Context Switch
Packet rate
- §122 OQ-4 는 축의 이름만 적어 두었고, 여섯을 무슨 도구로 어떻게 재는지는 제3부에 없다. 같은 §122 에서 OQ-1 과 OQ-2 는 돌릴 명령을 그대로 적었고 OQ-3 은 lsmod | grep vhost 를 확인 후보로 들었다.
- 이 호스트에서 두 backend 를 재 본 값은 SSOT 에 없다.
- §197 은 이 호스트의 판 번호를 실측으로 적었다.
libvirt : 12.7.0
QEMU emulator version : 11.1.1
커널 : 7.2.2-arch1-1
- 그래서 §110 이 든 조건 여섯 가운데 kernel version 과 QEMU version 은 이제 적을 수 있다. vhost configuration 과 offload, NIC capability 는 이 호스트에서 읽은 값이 없다.
- §202 와 §204 는 게스트를 지우고 다시 세울 때 무엇이 사라지고 무엇이 남는지 적었다. 게스트 디스크와 시드 ISO 는 사라지고 base.qcow2 와 libvirt 의 default 네트워크 정의는 남으며, 재구축해도 IP 가 같다. 다만 백엔드를 바꿔 다시 세운 기록은 없다.
- 이 호스트에서 두 백엔드를 재 본 값은 SSOT 에 없다.
## 가정
- 같은 가상 머신을 다른 backend 로 다시 구성해 띄울 수 있다고 본다. 그것이 이 실험 환경에서 되는지는 SSOT 에 적혀 있지 않다.
- 같은 가상 머신을 다른 백엔드로 다시 구성해 띄울 수 있다고 본다. 그것이 이 실험 환경에서 되는지는 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 이다.
- 지금 백엔드가 무엇인지는 이 물음이 정하지 않는다. 그것을 확정하는 물음이 닫힌 뒤에 시작한다. §116 은 이 테스트 환경의 경로를 펼치면서 TAP 다음 칸에 vhost-net 을 적어 두었다. 그 칸이 실제로 그런지를 §122 가 OQ-3 으로 아직 묻고 있으므로 그 그림을 지금 백엔드의 근거로 쓰지 않는다.
- §110 이 든 조건들(kernel version · QEMU version · vhost configuration · offload · NIC capability · packet path · GSO/GRO/TSO)을 함께 적지 않으면 이 측정을 다른 환경에 재사용할 수 없다. 앞의 둘은 §197 이 이미 적었으므로 잴 때 나머지를 채운다.
- 이 호스트에서 잰 값이 없어 다른 장비의 백엔드 비교 수치를 근거로 삼지 않는다.
- 여기서 어느 백엔드로 실험을 고정할지는 정하지 않는다. 그것은 측정 결과가 나온 뒤의 Decision 이다.
## 선택지
### 1. 지금 backend 만 먼저 찍어 나중에 견줄 값을 만든다
### 1. 지금 백엔드만 먼저 찍어 나중에 견줄 값을 만든다
구성을 바꾸지 않고 부하 전과 부하 중의 여섯 축을 한 번씩 기록한다. 가상 머신을 다시 구성하지 않으므로 지금 돌고 있는 Keycloak 실험을 멈추지 않아도 되고, 나중에 다른 backend 를 잴 때 견줄 값이 생긴다.
구성을 바꾸지 않고 부하 전과 부하 중의 여섯 축을 한 번씩 기록한다. 가상 머신을 다시 구성하지 않으므로 지금 돌고 있는 Keycloak 실험을 멈추지 않아도 되고, 나중에 다른 백엔드를 잴 때 견줄 값이 생긴다.
한 구성의 값만으로는 이 물음이 닫히지 않는다.
### 2. 두 backend 로 바꿔 가며 같은 부하를 건다
### 2. 두 백엔드로 바꿔 가며 같은 부하를 건다
§122 OQ-4 가 요구하는 비교가 이것이다. 같은 가상 머신을 다른 backend 로 다시 구성하고 같은 부하를 양쪽에 걸어 여섯 축을 같은 시각에 기록한다. 차이가 나면 그 표가 그대로 Case 가 된다.
§122 OQ-4 가 요구하는 비교가 이것이다. 같은 가상 머신을 다른 백엔드로 다시 구성하고 같은 부하를 양쪽에 걸어 여섯 축을 같은 시각에 기록한다. 차이가 나면 그 표가 그대로 Case 가 된다.
가상 머신을 다시 구성하고 재시작해야 하므로 그동안 Keycloak 실험을 멈춰야 하고, 두 측정 사이에 호스트 상태가 달라지지 않도록 관리해야 한다.
### 3. 문헌의 backend 비교 수치를 가져다 쓴다 — 제외
### 3. 문헌의 백엔드 비교 수치를 가져다 쓴다 — 제외
§110 은 실제로 복사가 일어나는지가 여러 조건에 따라 달라질 수 있다고 밝혔다. 그 조건은 kernel version · QEMU version · vhost configuration · offload · NIC capability · packet path · GSO/GRO/TSO 다. 조건이 이만큼 걸려 있으니 다른 환경에서 나온 수치는 이 호스트의 값이 되지 못한다. 이 물음은 이 호스트에서 잰 값으로만 닫힌다.
## 다음 검증
1. 지금 backend 가 무엇인지 먼저 확정한다. 그것을 묻는 물음이 닫히기 전에는 견줄 두 값 가운데 한쪽이 무엇인지 알 수 없다.
1. 지금 백엔드가 무엇인지 먼저 확정한다. 그것을 묻는 물음이 닫히기 전에는 견줄 두 값 가운데 한쪽이 무엇인지 알 수 없다.
2. 부하를 걸기 전에 여섯 축을 한 번 찍어 둔다.
3. 지금 구성에 부하를 걸고 여섯 축을 같은 시각에 기록한다. Latency 와 Throughput 과 Packet rate 는 부하 도구가 내는 값을 쓰고, QEMU CPU 와 Host CPU 와 Context Switch 는 호스트에서 읽는다.
4. 같은 가상 머신을 다른 backend 로 구성하고 같은 부하를 걸어 3 을 되풀이한다.
4. 같은 가상 머신을 다른 백엔드로 구성하고 같은 부하를 걸어 3 을 되풀이한다.
5. 두 구성의 여섯 축을 나란히 적고, §110 이 든 조건들과 실행한 명령을 함께 증거로 남긴다.
닫는 조건 : 두 구성의 여섯 축을 나란히 놓으면 닫는다. 차이가 부하 전 값의 흔들림 안이면 이 호스트가 내는 초당 패킷 수에서는 backend 선택이 결과를 바꾸지 않는다고 적고 닫는다. 차이가 나면 그 측정이 Case 가 되고, 어느 backend 로 실험을 고정할지는 그 Case 뒤에 Decision 으로 넘긴다.
닫는 조건 : 두 구성의 여섯 축을 나란히 놓으면 닫는다. 차이가 부하 전 값의 흔들림 안이면 이 호스트가 내는 초당 패킷 수에서는 백엔드 선택이 결과를 바꾸지 않는다고 적고 닫는다. 차이가 나면 그 측정이 Case 가 되고, 어느 백엔드로 실험을 고정할지는 그 Case 뒤에 Decision 으로 넘긴다.
@@ -19,7 +19,7 @@ source:
# VM1 과 VM2 의 TAP/vnet interface 는 무엇이고 어디에 붙어 있는가
§99 는 TAP 을 가상 머신의 이더넷 프레임과 호스트 리눅스 네트워크를 잇는 접점으로 놓았다. 그 접점의 이름은 호스트마다 다르게 붙는데, 이 호스트에서 두 가상 머신에 각각 무엇이 붙었는지는 SSOT 에 없다. 이름을 모르면 §119 가 적은 계층별 tcpdump 대상을 채우지 못하고, §117.1 이 든 「특정 VM 만 통신 불가」 어느 가상 머신을 가리키는지도 가릴 수 없다. 이 물음은 두 가상 머신의 호스트 쪽 인터페이스 이름과 그것이 붙어 있는 곳을 확정한다.
§99 는 TAP 을 가상 머신의 이더넷 프레임과 호스트 리눅스 네트워크를 잇는 접점으로 놓았다. 그 접점의 이름은 호스트마다 다르게 붙는데, 이 호스트 두 가상 머신에 무엇이 붙었는지는 SSOT 에 없다. 이름을 모르면 §119 계층별 tcpdump 대상을 채우지 못하고, §117.1 이 든 「특정 VM 만 통신 불가」 어느 가상 머신인지 가릴 수 없다. 이 물음은 두 가상 머신의 호스트 쪽 인터페이스 이름과 붙어 있는 곳을 확정한다.
## 관계
@@ -54,20 +54,30 @@ source:
특정 VM 만 통신 불가
- §117.1 이 그 증상에서 확인하라고 든 명령은 ip link · bridge link · bridge fdb show · virsh domiflist 다.
- §116 은 이 테스트 환경의 경로를 펼치면서 TAP 칸을 TAP(vm1) 과 TAP(vm2) 로 적었다. 호스트에서 읽은 이름은 그 그림에도 없다.
- 두 가상 머신스트 쪽 인터페이스 이름도, 그 인터페이스가 어느 브리지에 붙어 있는지도 SSOT 에는 없다.
- §178 은 이 실험대스트를 Debian 12 genericcloud 3대로 적었다. 엣지 1대와 k3s 2노드이고, §122 OQ-2 가 vm1 과 vm2 로 적은 두 대가 그중 k3s 노드 쪽이다.
- §202 는 2026-09-10 에 돌린 철거 명령의 출력을 그대로 남겼고, 거기서 libvirt domain 이름이 kc-lab-edge · kc-lab-1 · kc-lab-2 로 확인된다. 한 대분 출력의 첫 줄은 Domain 'kc-lab-edge' destroyed 다.
- §201 과 §247 이 적은 세 게스트의 MAC 주소와 IP 주소
kc-lab-edge : `52:54:00:aa:bb:10` · 192.168.122.10
kc-lab-1 : `52:54:00:aa:bb:11` · 192.168.122.11
kc-lab-2 : `52:54:00:aa:bb:12` · 192.168.122.12
- §247 은 `52:54:00` 이 QEMU/KVM 에 할당된 OUI(제조사 식별 접두사)라고 적고, 예약의 mac 과 VM 을 만들 때 준 mac 이 정확히 같아야 한다고 밝혔다. 다르면 예약이 조용히 무시되고 게스트가 동적 범위에서 아무 주소나 받는다.
- §201 은 예약과 리스를 다른 것으로 갈라 적었다. virsh net-dumpxml 의 예약은 줄 의도이고 virsh net-dhcp-leases 는 실제로 준 기록이라 둘이 다를 수 있다. 실제로 준 기록에는 `52:54:00:aa:bb:11` 이 192.168.122.11/24 을, `52:54:00:aa:bb:12` 가 192.168.122.12/24 을 각각 kc-lab-1 과 kc-lab-2 이름으로 받은 줄이 남아 있다.
- §178 과 §245 는 게스트가 libvirt 의 default 네트워크에 붙는다고 적었다. §245 는 VM 이 한 대라도 뜨면 그 VM 의 vnetN 인터페이스가 virbr0 에 붙으면서 브리지가 UP 으로 바뀐다고 덧붙였다.
- §201 은 VM 세 대가 돌 때 virbr0 이 UP 이고 전부 철거한 뒤에는 DOWN 이라는 출력을 남겼다. 상태가 갈린 이유를 브리지에 붙은 tap 인터페이스가 하나도 없어서라고 적었다.
- 호스트 쪽 인터페이스의 이름은 SSOT 에 없다. virsh domiflist 를 돌린 출력도 ip link 출력도 없다.
## 가정
- 호스트에 붙어 virsh 와 ip 계열 명령을 실행할 수 있다고 본다.
- §122 OQ-2 명령에 적은 vm1 과 vm2 가 이 호스트에 실재하는 domain 이름이라고 본다. 실제 이름이 다르면 그 이름으로 바꿔 돌린다. §99 와 §118 은 같은 명령에 넣을 domain 을 비워 두었고, 가상 머신 이름을 그대로 적은 곳은 §90.1 의 virsh domiflist vm1 과 §122 OQ-2 다.
- §122 OQ-2 명령에 vm1 과 vm2 를 적었지만 §202 의 철거 출력에 찍힌 domain 이름은 kc-lab-1 과 kc-lab-2 다. 그래서 명령에 넣을 이름은 뒤쪽을 쓴다. §99 와 §118 은 같은 명령에 넣을 domain 을 비워 두었다.
- virsh domiflist 출력에 인터페이스 이름과 MAC 주소가 함께 나온다고 전제한다. §99 도 §118 도 이 명령의 출력 형식은 적지 않았다.
- 두 가상 머신이 켜져 있는 동안 읽는다고 전제한다. 꺼진 가상 머신의 TAP 이 호스트에 남아 있는지는 SSOT 에 적혀 있지 않다.
## 미지수
- VM1 과 VM2 의 호스트 쪽 인터페이스 이름이 각각 무엇인지.
- 각 인터페이스의 MAC 주소와 NIC model 이 무엇인지.
- 인터페이스가 같은 브리지에 붙어 있는지, 서로 다른 곳에 붙어 있는지.
- kc-lab-1 과 kc-lab-2 의 호스트 쪽 인터페이스 이름이 각각 무엇인지. 엣지 게스트 kc-lab-edge 의 것도 같이 모른다.
- 각 인터페이스의 NIC model 이 무엇인지. MAC 주소는 §247 의 예약이 대지만 virsh domiflist 가 같은 값을 내는지는 대조해야 갈린다.
- 인터페이스가 모두 virbr0 에 붙어 있는지. §245 는 default 네트워크의 VM 들이 이 브리지에 연결된다고 적었고, bridge link 로 포트 목록을 읽은 출력은 없다.
- ip tuntap show 에 나오는 TAP 목록과 virsh domiflist 가 대는 이름이 그대로 맞아떨어지는지.
## 제약
@@ -75,12 +85,13 @@ source:
- 이름과 어디에 붙어 있는지를 적는 데서 끊는다. 그 경로로 패킷이 실제로 흘렀는지는 tcpdump 를 쓰는 물음이 받는다.
- 두 가상 머신을 같은 시점에 읽는다. 한쪽을 재시작한 뒤 다른 쪽을 읽으면 이름이 바뀌어도 알 수 없기 때문이다.
- 이 호스트에서 읽은 출력이 없어 tap0 이나 vnet0 같은 §99 의 예시 이름을 이 호스트의 값으로 쓰지 않는다.
- SSOT 가 마지막으로 적은 게스트 상태는 §202 의 철거다. 그 뒤에 다시 세웠는지는 적혀 있지 않으므로 세 게스트가 떠 있는 상태에서 읽는다. 철거된 상태로 읽으면 볼 것이 없다 — §201 은 세 대를 전부 철거한 뒤 virbr0 이 DOWN 인 이유를 브리지에 붙은 tap 인터페이스가 하나도 없어서라고 적었다.
## 선택지
### 1. libvirt 가 대는 이름을 먼저 받아 호스트에서 대조한다
virsh domiflist 로 가상 머신마다 붙은 인터페이스를 받고, 그 이름이 ip link 목록에 실재하는지 확인한 뒤 bridge link 로 어느 브리지의 port 인지 잡는다. §122 OQ-2 가 적은 네 줄이 이 순서다. 가상 머신과 인터페이스의 짝이 처음부터 정해져 나오므로 두 대의 것을 헷갈리지 않는다.
virsh domiflist 로 가상 머신마다 붙은 인터페이스를 받고, 그 이름이 ip link 목록에 실재하는지 확인한 뒤 bridge link 로 어느 브리지의 포트인지 잡는다. §122 OQ-2 가 적은 네 줄이 이 순서다. 가상 머신과 인터페이스의 짝이 처음부터 정해져 나오므로 두 대의 것을 헷갈리지 않는다.
libvirt 가 모르는 인터페이스는 이 순서에서 빠진다.
@@ -96,9 +107,9 @@ ip link 와 ip tuntap show 로 호스트에 있는 TAP 을 모두 적고, bridge
## 다음 검증
1. virsh domiflist vm1 과 virsh domiflist vm2 로 각 가상 머신에 붙은 인터페이스를 읽는다.
1. virsh domiflist 에 §202 가 적은 domain 이름 kc-lab-1 · kc-lab-2 · kc-lab-edge 를 차례로 넣어 각 가상 머신에 붙은 인터페이스를 읽는다.
2. ip link 로 그 이름이 호스트에 실재하는지 대조한다.
3. bridge link 로 각 인터페이스가 어느 브리지의 port 인지 적는다.
3. bridge link 로 각 인터페이스가 어느 브리지의 포트인지 적는다.
4. 두 가상 머신의 결과를 인터페이스 이름 · MAC 주소 · 붙어 있는 브리지로 나란히 적고, 실행한 명령과 출력을 함께 증거로 남긴다.
닫는 조건 : 가상 머신마다 인터페이스 이름과 MAC 주소와 붙어 있는 브리지를 적어 두 대를 나란히 놓으면 닫는다. 이 목록이 계층별 캡처 기준이 요구하는 지점의 이름이 되고, 이것이 없으면 실제 패킷 경로를 묻는 물음의 tcpdump 를 어느 인터페이스에 걸지 정할 수 없다. 두 가상 머신이 서로 다른 브리지에 붙어 있으면 §117.1 의 「특정 VM 만 통신 불가」를 진단할 때 그 사실을 먼저 본다고 적는다.
@@ -19,14 +19,14 @@ source:
# 이 가상 머신들의 virtio-net 에 multi-queue 가 켜져 있는가
multi-queue 는 virtio-net 이 송수신 큐를 여러 개 두고 쓰는 구성이다. §112 는 큐를 하나만 쓰면 패킷 처리가 한 vCPU 나 한 처리 경로에 몰릴 수 있어서 이것을 최적화 방향으로 들었다. 이 물음은 그 쏠림이 실제로 일어나는지를 재지 않고, 두 가상 머신이 애초에 큐를 몇 개 쓰도록 구성되어 있는지를 읽는다. 근거 문서는 multi-queue 를 쓸 수 있다고만 적었을 뿐 이 가상 머신들의 큐 수는 적지 않았다.
multi-queue 는 virtio-net 이 송수신 큐를 여러 개 두고 쓰는 구성이다. §112 는 큐를 하나만 쓰면 패킷 처리가 한 vCPU 나 한 처리 경로에 몰릴 수 있어서 이것을 최적화 방향으로 들었을 뿐, 이 가상 머신들의 큐 수는 적지 않았다. 이 물음은 그 쏠림이 실제로 일어나는지를 재지 않고, 두 가 애초에 큐를 몇 개 쓰도록 구성되어 있는지를 읽는다.
## 관계
- **Guest 의 packet 이 Host Physical NIC 에 닿기까지 — virtio-net · virtqueue · vhost-net · TAP · Bridge**
게스트와 호스트 backend 가 virtqueue 로 무엇을 주고받는지를 이 개념이 설명한다. 큐를 몇 개 두느냐는 그 구조 위의 설정이다.
게스트와 호스트 백엔드가 virtqueue 로 무엇을 주고받는지를 이 개념이 설명한다. 큐를 몇 개 두느냐는 그 구조 위의 설정이다.
- **이 호스트에서 vhost-net 이 실제로 packet datapath 를 맡고 있는가**
큐를 실제로 돌리는 backend 가 어느 쪽이냐에 따라 큐 수를 읽을 곳이 달라진다.
큐를 실제로 돌리는 백엔드가 어느 쪽이냐에 따라 큐 수를 읽을 곳이 달라진다.
- **부하 중 network 가상화가 지연을 바꿀 만큼 호스트 CPU 를 쓰는가**
큐가 하나로 나왔을 때 그것이 실제 병목인지는 부하 구간의 vCPU 별 사용량이 답한다.
@@ -37,8 +37,8 @@ multi-queue 는 virtio-net 이 송수신 큐를 여러 개 두고 쓰는 구성
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 는 호스트에서 게스트로 버퍼를 넘긴다.
- §112 는 그 효과가 workload · CPU affinity · IRQ placement · queue configuration 에 따라 달라진다고 덧붙였다. IRQ(Interrupt Request, 인터럽트 요청)는 장치가 처리할 일이 생겼음을 CPU 에 알리는 신호다.
- §100 은 virtqueue 를 게스트와 호스트 백엔드가 디스크립터(descriptor)를 써서 I/O 버퍼를 주고받는 공유 큐 구조로 놓고, 네트워크에서는 보통 TX/RX 큐를 쓴다고 적었다. TX virtqueue 는 게스트에서 호스트로, RX virtqueue 는 호스트에서 게스트로 버퍼를 넘긴다.
- §117.5 는 single queue bottleneck 을 큐 하나나 vCPU 하나에 패킷 처리가 몰리는 문제로 놓고, 확인 대상 넷을 들었다.
virtio multi-queue
IRQ distribution
@@ -50,7 +50,10 @@ multi-queue 는 virtio-net 이 송수신 큐를 여러 개 두고 쓰는 구성
queue count
IRQ distribution
- 같은 §122 에서 OQ-1 과 OQ-2 는 돌릴 명령을 그대로 적었고, OQ-5 는 확인 대상 넷의 이름만 적었다. §117.5 가 든 넷도 이름이다.
- 두 가상 머신에 설정된 큐 수가 근거 문서에 없다. 게스트가 몇 개를 쓰고 있는지도, IRQ어느 vCPU 에 붙어 있는지도 적혀 있지 않다.
- §197 과 §218 은 이 실험대의 vCPU 배분을 실측으로 적었다. k3s 노드 kc-lab-1 과 kc-lab-2 가 각각 vCPU 2 이고 엣지 게스트가 1 이며, 합 5 를 논리 코어 8 위에 얹었다. §112큐와 vCPU 를 하나씩 짝지어 든 예는 넷씩이라 이 게스트들의 수와 다르다.
- §247 은 VM 이 부팅하면 게스트 커널이 virtio NIC 를 인식하고 DHCP 클라이언트가 DHCPDISCOVER 를 브로드캐스트한다고 적었다. 이 게스트들의 NIC 가 virtio 라는 것은 거기까지 나온다.
- §201 은 게스트 안에서 본 인터페이스 이름을 한 줄 남겼다. 엣지 게스트가 첫 부팅에서 enp1s0 으로 192.168.122.10/24 을 받았다. ethtool 에 넣을 이름이 그 줄에서 나오지만 k3s 노드 두 대의 인터페이스 이름은 적혀 있지 않다.
- 두 가상 머신에 설정된 큐 수가 SSOT 에 없다. 게스트가 몇 개를 쓰고 있는지도, 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 항목에서 인터페이스 이름을 받는 형태로만 나온다.
@@ -59,6 +62,7 @@ multi-queue 는 virtio-net 이 송수신 큐를 여러 개 두고 쓰는 구성
- 두 가상 머신의 구성과 게스트 내부를 지금 읽을 수 있다고 본다.
- §112 가 든 RX Queue 넷과 vCPU 넷의 짝은 multi-queue 를 설명하려고 든 예시이지 이 호스트에서 읽은 값이 아니다.
- 설정에 적힌 큐 수와 게스트가 실제로 쓰는 큐 수를 따로 읽어야 한다고 전제한다. 설정에 여럿을 적어 두면 게스트 드라이버가 그만큼 쓴다는 서술이 근거 문서에 없기 때문이다.
- 큐를 여럿 두어도 IRQ 를 한 vCPU 가 몰아서 받으면 처리가 한쪽에 몰린다고 본다. §117.5 는 IRQ distribution 을 확인 대상으로 들었을 뿐 그렇게 된다고 적지는 않았다.
- 두 가상 머신의 NIC 구성이 확인하는 동안 바뀌지 않는다.
## 미지수
@@ -66,7 +70,7 @@ multi-queue 는 virtio-net 이 송수신 큐를 여러 개 두고 쓰는 구성
- 두 가상 머신의 virtio-net 에 설정된 큐 수.
- 게스트가 실제로 쓰고 있는 큐 수, 그리고 그 수가 설정값과 같은지.
- 각 큐의 IRQ 가 여러 vCPU 에 흩어져 있는지 한 vCPU 에 몰려 있는지.
- 두 가상 머신의 vCPU 수. §112 가 큐와 vCPU 를 하나씩 짝지어 든 예 수를 나란히 놓아야 읽히는데, vCPU 수도 네트워크 쪽 근거에는 없다.
- 설정된 큐 수와 vCPU 수의 관계. vCPU 는 §218 이 두 노드 모두 2 로 적었으므로, 큐가 몇이면 §112 가 든 하나씩 대응이 되는지 수를 읽어야 갈린다.
- 이 환경의 RSS/RPS/XPS 설정. §117.5 가 확인 대상으로 들었지만 값을 읽는 방법은 적지 않았다.
## 제약
@@ -74,6 +78,7 @@ multi-queue 는 virtio-net 이 송수신 큐를 여러 개 두고 쓰는 구성
- 이 호스트에서 잰 값이 하나도 없다. 근거는 개념을 정리한 문서 한 편이고 §112 의 큐 넷과 vCPU 넷은 예시 숫자다.
- 이 물음은 큐 구성이 무엇인지까지만 답한다. 쏠림이 실제 병목인지는 부하를 걸어야 갈리고, 그 부하 측정은 다른 물음이 가져간다.
- 확인하는 동안 NIC 구성을 바꾸지 않는다. 큐 수를 늘려 놓고 읽으면 지금 실험이 어떤 구성에서 돌았는지 못 본다.
- 게스트 안에서 읽는 항목이 둘이라 게스트가 떠 있어야 한다. SSOT 가 마지막으로 적은 게스트 상태는 §202 의 철거이고, 그 뒤 다시 세웠는지는 적혀 있지 않다.
- §118 이 큐 수와 IRQ 분포를 읽는 명령을 적지 않았으므로, 실행한 명령과 그 출력을 함께 증거로 남겨야 다음 사람이 같은 값을 다시 읽는다.
## 선택지
@@ -21,7 +21,7 @@ source:
# 이 호스트의 가상 머신 네트워크는 Bridge 인가 NAT 인가 Routing 인가
§98 은 가상 머신 네트워크를 분석하기 전에 Bridge 기반인가 · Routing 기반인가 · NAT(Network Address Translation, 네트워크 주소 변환) 기반인가를 구분하라고 적었다. 셋은 프레임이 지나는 계층이 다르고, 그에 따라 호스트의 L3 경로와 Netfilter 가 끼어드는지도 갈린다. 이 물음은 성능을 재지 않는다. 제3부가 서술한 경로 가운데 어느 절이 이 호스트에 그대로 적용되는지를 먼저 확정한다.
이 실험대는 NAT(Network Address Translation, 네트워크 주소 변환) 를 쓴다. 이더넷 없이 WiFi 만 있어 브리지를 못 쓴다고 §178 이 관측으로 적었다. §249 가 세 모드를 견준 표에서 브리지와 macvtap 은 둘 다 「WiFi라 불가」이고 채택 표시는 NAT 한 줄에만 붙어 있다. 감수한 것도 같은 표에 적혀 있다 — VM 주소가 사설이라 LAN 에서 VM 으로 바로 들어가지 못하고 포워딩이 필요하다. 엣지를 게스트로 옮긴 뒤 호스트 커널 DNAT(Destination NAT, 목적지 주소 변환) 와 libvirt 체인의 구멍이 새로 필요해진 것이 그 포워딩이다. 다만 §122 OQ-1 이 요구한 다섯 명령의 출력이 없어 virbr0 에 무엇이 붙어 있고 라우팅이 어떻게 걸려 있는지는 아직 적지 못한다.
## 관계
@@ -37,14 +37,14 @@ source:
## 사실
- §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 처럼 동작할 수 있다는 예를 들었다.
- §97 은 Routing 을 L3 에서 IP 를 보고 내리는 결정으로 놓았다. Bridge 가 같은 이더넷 네트워크를 잇는 것과 달리 Routing 은 서로 다른 IP 네트워크를 잇고, 목적지 IP 를 보고 어느 인터페이스나 next-hop 으로 보낼지 정한다.
- §98 은 NAT 을 패킷의 IP 와 포트 정보를 바꾸는 것으로 놓고, 가상 머신이 사설 서브넷을 쓰면 호스트가 NAT 게이트웨이처럼 동작할 수 있다는 예를 들었다.
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 에는 확인 명령이 없다.
- §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 에 따라 달라질 수 있다고 덧붙였다.
- §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 에 이름을 넣는다
@@ -52,26 +52,39 @@ source:
어느 인터페이스가 어느 브리지의 포트인지 : bridge link
라우팅 테이블 : ip route
- §118 이 같은 계층에 든 명령 가운데 virsh net-info 와 ip rule 은 OQ-1 의 다섯 줄에 없다.
- 이 호스트에서 그 명령을 돌린 출력은 SSOT 에 없다. 셋 중 무엇인지도 적혀 있지 않다.
- §178 은 이 실험대의 대상 환경을 관측으로 적었다. test-server 는 Arch Linux 이고 이더넷 없이 WiFi 만 있어 브리지를 못 쓴다. 그래서 libvirt NAT(virbr0) 과 호스트 진입 구조를 택했고, 게스트는 Debian 12 genericcloud 3대로 엣지 1대와 k3s 2노드다.
- §250 은 WiFi 에서 브리지가 안 되는 이유를 적었다. 802.11 데이터 프레임은 기본적으로 주소 필드가 3개다(3-address 모드). AP(Access Point, 무선 접속 장치)는 연결(association)된 단말(station)의 MAC 만 알고 있어서, 그 단말이 자기 것이 아닌 출발지 MAC 을 단 프레임을 보내면 버린다. 브리지된 가상 머신은 자기 MAC 을 출발지로 쓰므로 정확히 그런 프레임을 보낸다.
- §250 이 든 우회 수단은 둘이다.
4-address 모드(WDS) : AP 와 클라이언트 드라이버가 모두 지원해야 하는데 실제로는 거의 지원되지 않는다
USB 이더넷 어댑터를 꽂는 것 : 현실적인 우회
- §250 은 test-server 에 이더넷이 없고 wlo1 만 있어서 「VM 에 LAN IP 를 직접 주자」는 계획이 물리적으로 성립하지 않는다고 적었다. 이 제약 하나가 토폴로지 전체를 NAT + 호스트 nginx 로 확정시켰다고 밝혔다. 이더넷 인터페이스가 있는지는 ip -brief link 로 보고 무선 인터페이스 정보는 iw dev 로 본다고 확인 명령을 달았다.
- §249 는 세 모드를 한 표로 놓고 이 실험대가 무엇을 골랐는지 적었다.
NAT(virbr0) : VM 주소는 192.168.122.x 사설, LAN 에서 VM 접근 불가, 채택
브리지(br0) : LAN 에서 직접 IP, WiFi 라 불가
macvtap : LAN 에서 직접 IP(호스트↔VM 은 제외), WiFi 라 불가
- §245 는 libvirt 의 default 네트워크를 소프트웨어 브리지 virbr0 과 거기 붙은 NAT 규칙으로 적었다. 기본 대역은 192.168.122.0/24 이고 호스트가 .1 을 가지며, VM 들은 이 브리지에 연결되어 서로 직접 통신하고 외부로 나갈 때만 호스트 IP 로 마스커레이딩된다. NAT 가 막는 것은 외부에서 VM 으로 들어오는 방향이다.
- §201 은 이 호스트에서 ip -br addr show virbr0 을 돌린 출력을 남겼다. VM 세 대가 돌 때는 virbr0 이 UP 이고 192.168.122.1/24 을 갖고, 전부 철거한 뒤에는 주소는 그대로인 채 DOWN 이다.
- §180 과 §255 는 밖에서 게스트로 들어오는 방향이 FORWARD 경로라 libvirt 의 guest_input 체인을 지난다고 적었다. 그 체인이 oif "virbr0" reject 로 끝나 게스트 대역으로 새로 들어오는 연결을 거절했고, 밖에서 친 curl 은 connection refused 를 받았다. 호스트 안에서 같은 게스트 주소로 친 curl 은 OUTPUT 경로라 forward 를 타지 않아 404 로 응답했다. 그 reject 줄을 범인으로 확정한 것은 카운터다. 밖에서 curl 을 네 번 쳤을 때 그 줄에 packets 4 bytes 240 이 찍혀 있었다.
- 이 호스트에서 §122 OQ-1 의 다섯 명령을 돌린 출력은 SSOT 에 없다.
## 가정
- 호스트에 붙어 virsh 와 ip 계열 명령을 실행할 수 있다고 본다.
- libvirt 가상 네트워크로 정의된 구성이면 virsh net-list --all 에 이름이 나온다고 본다. 호스트가 미리 만들어 둔 브리지에 가상 머신을 직접 붙인 구성이면 그 목록에 아무 이름도 나오지 않을 수 있다.
- §201 과 §247 이 virsh net-update default 로 DHCP 예약을 넣었으므로 이 호스트의 가상 머신이 libvirt 의 default 네트워크를 쓴다고 본다. 그 네트워크 말고 다른 정의가 더 있는지는 열거해 봐야 갈린다.
- 확인하는 동안 네트워크 구성이 바뀌지 않는다고 전제한다.
- 셋 가운데 하나로 갈린다고 보고 물음을 세웠다. 다만 §114 가 Bridge 구성에도 Routing 과 NAT 과 Firewall 이 함께 걸릴 수 있다고 적었으므로, 하나로 갈리지 않으면 걸린 것을 모두 적는다.
## 미지수
- 이 호스트의 가상 머신 네트워크가 Bridge 기반인지 NAT 기반인지 Routing 기반인지.
- libvirt 가상 네트워크로 정의되어 있는지, 아니면 호스트의 브리지에 가상 머신이 직접 붙어 있는지.
- 정의되어 있다면 그 가상 네트워크의 forward mode 와 bridge 이름이 무엇인지.
- 가상 머신을 떠난 프레임이 호스트의 L3/Netfilter 경로를 지나는지.
- §178 과 §249 가 NAT 이라고 적은 것이 libvirt 네트워크 정의의 forward mode 로도 그렇게 적혀 있는지. virsh net-dumpxml default 를 통째로 찍은 출력이 SSOT 에 없고, §247 이 인용한 것은 그 정의의 dhcp 절뿐이다.
- default 말고 정의된 가상 네트워크가 더 있는지. virsh net-list --all 의 출력이 없다.
- virbr0 에 어느 인터페이스가 포트로 붙어 있는지, 그리고 이 호스트의 라우팅 테이블이 무엇인지. bridge link 와 ip route 의 출력이 없다.
- 게스트끼리 오가는 프레임과 게스트가 밖으로 나가는 프레임이 호스트의 L3/Netfilter 경로를 지나는지. 밖에서 게스트로 들어오는 방향만 §180 이 FORWARD 로 관측했다.
## 제약
- 구성이 셋 중 무엇인지를 확정하는 데서 끊는다. 그 구성이 지연에 얼마나 영향을 주는지는 부하 중 호스트 CPU 사용을 보는 물음이 받는다.
- 이 호스트에서 읽은 출력이 없어 다른 장비의 구성을 근거로 삼지 않는다.
- SSOT 에 남은 출력은 §201 의 virbr0 주소와 상태뿐이다. 나머지 네 명령은 다른 장비에서 읽은 값으로 대신하지 않는다.
- 돌릴 명령은 §122 OQ-1 이 정해 두었다. 실행한 명령과 출력을 함께 남겨야 다음 사람이 같은 값을 다시 읽는다.
## 선택지
@@ -86,11 +99,11 @@ libvirt 로 정의하지 않고 호스트 브리지에 직접 붙인 구성이
ip link 로 실재하는 인터페이스를 세우고, bridge link 로 어느 인터페이스가 어느 브리지의 포트인지를 잡고, ip route 로 L3 결정을 본다. libvirt 로 정의했든 안 했든 호스트에 실재하는 것을 읽으므로 구성 방식과 무관하게 답이 나온다.
forward mode 라는 이름으로 적힌 의도는 나오지 않으므로, NAT 이 걸려 있는지는 routing 과 방화벽 규칙을 따로 봐야 한다. §117.3 이 NAT/Firewall 오류에 든 것 nftables · iptables · NAT rules · IP forwarding 이라는 확인 대상 이름이고 돌릴 명령이 아니다.
forward mode 라는 이름으로 적힌 의도는 나오지 않으므로, NAT 이 걸려 있는지는 routing 과 방화벽 규칙을 따로 봐야 한다. §117.3 이 NAT/Firewall 오류에 든 것 nftables · iptables · NAT rules · IP forwarding 이라는 확인 대상 이름이고 돌릴 명령이 아니다. 방화벽 쪽 명령은 §255 가 대신 적어 두었다 — libvirt 체인 하나를 열어 보는 sudo nft -a list chain ip libvirt_network guest_input 이다. 그 명령이 통하는 것은 이 호스트가 nftables 백엔드이기 때문이고, §180 은 libvirt 의 firewall_backend 가 iptables 일 때도 같은지는 재지 않았다고 적었다.
### 3. tcpdump 로 경로부터 잡는다 — 제외
§119 는 Host NIC · Bridge · TAP · Guest NIC 에서 tcpdump 로 패킷을 추적하는 순서를 적어 두었다. 다만 그 추적은 캡처를 걸 인터페이스 이름을 이미 알고 있을 때 성립한다. 지금은 브리지 이름도 TAP 이름도 모르므로 명령의 대상을 채울 수 없다. §126 의 실습 순서에서도 Bridge/NAT/Route 확인이 셋째이고 Host Nginx → VM packet path tcpdump 는 여덟째다. 이 방법은 이 물음이 닫힌 뒤 실제 패킷 경로를 묻는 물음이 받는다.
§119 는 Host NIC · Bridge · TAP · Guest NIC 에서 tcpdump 로 패킷을 추적하는 순서를 적어 두었다. 다만 그 추적은 캡처를 걸 인터페이스 이름을 이미 알고 있을 때 성립한다. 브리지 이름은 §245 와 §201 이 virbr0 으로 적었지만 TAP 이름은 아직 없으므로 게스트 쪽 대상을 채울 수 없다. §126 의 실습 순서에서도 Bridge/NAT/Route 확인이 셋째이고 Host Nginx → VM packet path tcpdump 는 여덟째다. 이 방법은 이 물음이 닫힌 뒤 실제 패킷 경로를 묻는 물음이 받는다.
## 다음 검증
@@ -23,9 +23,7 @@ source:
# packet 이 어디서 끊겼는지는 계층마다 capture 해서 가른다
가상 머신이 밖과 통신하지 못할 때 게스트 안에서만 원인을 찾으면 호스트의 브리지와 TAP 연결은 마지막에야 보게 된다. 그 사이의 계층은 애플리케이션 로그에 아무것도 남기지 않는다.
그래서 패킷이 지나야 할 지점마다 캡처를 걸고 어디까지 보였는지로 구간을 좁힌다. 호스트의 물리 NIC(Network Interface Card) 와 브리지, 가상 머신에 붙은 TAP, 게스트 안의 인터페이스 넷이 그 지점이고, 보이지 않은 첫 지점의 앞 구간이 의심 구간이 된다.
가상 머신이 밖과 통신하지 못하면 게스트부터 뒤지지 말고 패킷이 지나야 할 네 지점에 캡처를 걸어 어디까지 보였는지로 구간을 좁힌다. 호스트의 물리 NIC(Network Interface Card) 와 브리지, 가상 머신에 붙은 TAP, 게스트 안의 인터페이스가 그 지점이다. 보이지 않은 첫 지점의 앞 구간이 의심 구간이 된다.
## 관계
@@ -44,7 +42,7 @@ source:
이 기준은 두 가지를 막는다.
가상 머신이 통신하지 못할 때 게스트 안쪽과 애플리케이션부터 뒤지느라 호스트의 브리지와 TAP 연결을 늦게 보는 일이 하나다.
가상 머신이 통신하지 못할 때 게스트 안쪽과 애플리케이션부터 뒤지느라 호스트의 브리지와 TAP 연결을 늦게 보는 일이 하나다. 그 사이의 계층은 애플리케이션 로그에 아무것도 남기지 않는다.
오프로드 때문에 실제 wire 와 다르게 보이는 정상 패킷을 결함으로 판정하는 일이 다른 하나다.
@@ -90,7 +88,9 @@ tap 에는 보이는데 게스트 NIC 에 안 보이면 virtio 와 vhost, 게스
가상 머신에서 인터넷으로 나가지 못하거나 외부에서 가상 머신에 접근하지 못하거나 특정 포트만 실패하면 nftables 와 iptables, NAT(Network Address Translation) 규칙, IP(Internet Protocol) forwarding 설정을 확인 대상으로 둔다.
증상 셋을 각각 다른 기준으로 나누지 않은 것은 셋이 모두 어느 지점에서 끊겼는가로 환원되기 때문이다. 따로 적으면 같은 규칙의 부분 증상이 셋으로 늘어난다.
이 실험대가 가장 오래 막힌 지점도 여기였다. 밖에서 온 요청만 엣지 게스트에 닿지 않았고, 거절한 것은 libvirt 가 자기 테이블의 guest_input 체인 끝에 둔 reject 규칙이었다(§180).
증상 셋을 각각 다른 기준으로 나누지 않은 것은 셋이 모두 어느 지점에서 끊겼는가 하나로 모이기 때문이다. 따로 적으면 같은 규칙의 부분 증상이 셋으로 늘어난다.
### 5. 패킷 크기와 체크섬이 예상과 다르다는 이유만으로 결함이라고 읽지 않는다
@@ -122,10 +122,12 @@ KVM(Kernel-based Virtual Machine)/QEMU/libvirt 로 만든 가상 머신이 외
브리지가 L2 전달만 하는 구성에서는 호스트의 L3 관측에 안 보이는 것이 정상인데, 여섯 번째 규칙이 그 경우다.
네 지점이 모두 보이는데 느린 경우는 이 기준이 다루지 않는다. Keycloak 실험 결과를 애플리케이션 원인으로 읽기 전에 network 계층을 따로 검증한다 쪽으로 넘긴다.
네 지점이 모두 보이는데 느린 경우는 이 기준이 다루지 않는다. Keycloak 실험 결과를 애플리케이션 원인으로 읽기 전에 network 계층을 따로 검증한다 쪽으로 넘긴다.
이 저장소에는 아직 이 절차를 실제로 돌린 출력이 없다. 여기 적은 판독은 원문 문서가 서술한 것이고 이 호스트의 캡처로 확인한 것이 아니다.
§180 의 사건에서도 구간을 좁힌 것은 네 지점 캡처가 아니었다. 호스트에서 친 curl 은 404 를 받는데 밖에서 친 curl 만 connection refused 를 받았다는 차이가 단서였고, 범인은 그 reject 규칙의 카운터가 4 패킷 240 바이트로 밖에서 친 횟수와 맞아떨어져서 확정했다.
## 예시
- 물리 NIC 에 보이고 브리지에도 보이는데 tap 에 안 보임 : 호스트의 브리지와 tap 연결을 먼저 확인한다
@@ -20,9 +20,7 @@ source:
# Keycloak 실험 결과를 애플리케이션 원인으로 읽기 전에 network 계층을 따로 검증한다
Refresh Token 경쟁 자체는 virtio-net 문제가 아니다. 다만 그 실험 클라이언트에서 Nginx 와 가상 머신, K3s, Keycloak 을 거쳐 PostgreSQL 또는 Redis 까지 가는 경로를 여러 노드가 함께 쓴다. 그 경로 위의 네트워크 가상화 문제는 애플리케이션 동시성 문제와 비슷한 증상으로 나타난다.
노드 하나만 느리거나 특정 가상 머신에서만 요청이 실패하는 것을 Refresh Token 경쟁이나 데이터베이스 락으로 결론내지 않으려면, 실험 결과를 원인에 귀속하기 전에 네트워크 경로를 따로 검증한다.
Refresh Token 경쟁 자체는 virtio-net 문제가 아니다. 다만 그 실험에서는 클라이언트에서 Nginx 와 가상 머신, K3s, Keycloak 을 거쳐 PostgreSQL 또는 Redis 까지 가는 경로를 여러 노드가 함께 쓴다. 그 경로 위의 네트워크 가상화 문제는 애플리케이션 동시성 문제와 비슷한 증상으로 나타난다.
## 관계
@@ -55,6 +53,8 @@ Keycloak 멀티 노드 실험에서 관측되는 지연과 실패에는 네트
경로를 적지 않으면 어느 관측이 어느 구간을 덮는지 정해지지 않는다.
이 실험대에서 그렇게 어긋난 확인이 한 번 있었다. 04 단계의 확인 명령을 엣지 가상 머신 안에서 tailnet 주소로 쳤더니 connection refused 가 돌아왔는데, 엣지에서 나간 패킷은 호스트의 virbr0 으로 들어가고, 들어온 패킷의 도착지 주소를 바꿔 넘기는 호스트의 DNAT(Destination NAT, 도착지 주소 변환) 규칙은 tailscale0 으로 들어온 것만 매칭해서 안 걸렸기 때문이다(§190 · §182). 「설정 문제가 아니라 친 위치 문제다」가 그 단계가 남긴 한 줄이다.
### 2. 노드 하나에서만 나는 증상을 애플리케이션 동시성으로 먼저 읽지 않는다
노드 하나만 지연되거나 가상 머신 하나에서만 실패하는 증상은 그 노드로 가는 경로 쪽에서도 나올 수 있다. 호스트 브리지 구성 오류, NAT 와 conntrack 문제, 그 가상 머신 쪽 패킷 유실이 같은 모양으로 관측된다.
@@ -91,7 +91,7 @@ vhost-net 과 QEMU 스레드, softirq 도 호스트 CPU 를 쓴다. 그래서
## 예외
이 기준은 애플리케이션 쪽 관측을 늘려서는 적용되지 않는다. Keycloak 이 게스트 소켓 위에서만 동작하므로 애플리케이션 로그로는 이 계층이 원인인지 가릴 수 없다.
애플리케이션 쪽 관측을 늘리는 것으로는 이 기준을 적용할 수 없다. Keycloak 이 게스트 소켓 위에서만 동작하므로 애플리케이션 로그로는 이 계층이 원인인지 가릴 수 없다.
네트워크 계층이 깨끗하다고 해서 이 기준이 애플리케이션 결함을 배제해 주지는 않는다. 여기서 나오는 것은 네트워크가 원인이 아니라는 것까지다.
@@ -99,6 +99,8 @@ vhost-net 과 QEMU 스레드, softirq 도 호스트 CPU 를 쓴다. 그래서
원문 문서가 이 테스트 환경에 얹어 그린 경로는 확인된 것이 아니라 기준 구조를 그대로 옮겨 놓은 그림이다. 이 저장소에는 이 기준으로 원인을 실제로 가른 실험이 아직 없다.
가까운 것은 실험대를 세울 때 한 번 있었다. 밖에서 온 요청만 엣지에 닿지 않았는데 원인은 게스트 안이 아니라 호스트의 libvirt 방화벽 규칙이었고, 그때 엣지 nginx 는 호스트에서 친 요청에 404 로 응답하고 있었다(§180).
## 예시
- Keycloak 노드 하나만 응답이 느림 : 그 노드가 있는 가상 머신까지의 경로부터 확인한다