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
@@ -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 노드 하나만 응답이 느림 : 그 노드가 있는 가상 머신까지의 경로부터 확인한다