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 네트워크는 이 네트워크 가상화 위에 더해지는 계층이라 따로 분석한다.