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
@@ -43,7 +43,7 @@ durability 는 전원이 나가도 데이터가 남아 있는 영속성을 말
이 기준은 빨라진 것을 되돌리라고 하지 않는다. 무엇이 빨라졌는지 옆에 어느 보장이 사라졌는지를 같이 적게 한다.
이것을 완료의 네 단계를 설명하는 개념 기록 안 각주로 두지 않고 따로 뺀 것은 이이 필요해지는 때가 다르기 때문이다. 개념을 읽을 때가 아니라 설정을 고르고 성능 개선을 평가할 때 걸리는데, 각주로 두면 다음에 같은 판단을 하는 사람이 그때 이것을 찾지 못한다.
완료의 네 단계를 설명하는 개념 기록 안 각주로 두지 않고 따로 뺀 것은 이 기준이 필요해지는 때가 다르기 때문이다. 개념을 읽을 때가 아니라 설정을 고르고 성능 개선을 평가할 때 걸리는데, 각주로 두면 다음에 같은 판단을 하는 사람이 그때 이것을 찾지 못한다.
## 규칙
@@ -63,7 +63,7 @@ FLUSH 완료 응답을 받은 쪽은 그 데이터가 살아남는다고 가정
개념 문서는 none 을 캐시 자체가 없음으로, writeback 을 무조건 위험으로 읽는 것이 부정확하다고 적었다. cache=writeback 을 골라도 정상적인 스택이라면 게스트의 fsync 와 FLUSH 는 virtio FLUSH 와 QEMU 와 백엔드를 지나 호스트의 sync 와 flush 로 전달된다. 필요한 완료 확인 뒤에 게스트로 완료가 돌아간다. 개념 문서는 정확한 표현을 이렇게 적었다 — writeback caching 에서는 volatile cache 가 존재할 수 있으므로 Guest 의 flush/fsync semantics 가 전체 backend/storage stack 에서 올바르게 보존되는지가 중요하다.
그래서 이 기준은 어느 캐시 모드를 쓰라고 말하지 않는다. 개념 문서는 두 설정의 뜻과 각각의 오해를 갈라 놓기만 했고 어느 쪽으로 정했다고 적지 않았으며 감수한 비용도 적지 않았다. 이 호스트가 지금 무엇을 쓰고 있는지도 아직 읽지 않았다.
그래서 이 기준은 어느 캐시 모드를 쓰라고 말하지 않는다. 개념 문서는 두 설정의 뜻과 각각의 오해를 갈라 놓기만 했고 어느 쪽으로 정했다고 적지 않았으며 감수한 비용도 적지 않았다. 이 호스트가 지금 무엇을 쓰고 있는지도 아직 읽지 않았다. 이 저장소에 남은 것은 게스트를 만든 virt-install 세 줄에 캐시 옵션이 없다는 것까지다.
### 5. DB 부하에서는 애플리케이션이 쓰는 영속성 규약을 함께 적는다
@@ -81,7 +81,7 @@ PostgreSQL 은 WAL 등의 durability protocol 을 사용하며 필요한 시점
- 영속성을 실제로 포기해도 되는 데이터가 있다. 다시 만들 수 있는 캐시나 파생 파일이 그렇고, 그때는 빨라진 것이 정당한 선택이다. 이 기준이 요구하는 것은 포기를 막는 것이 아니라 무엇을 포기했는지 적게 하는 것이다.
- cache=none 이나 Direct I/O 를 골랐다고 영속성이 확보되지도 않는다. 개념 문서는 Direct I/O 의 핵심이 페이지 캐시 우회이며 호스트 페이지 캐시를 우회하는 것이 즉시 durable media 반영을 뜻하지 않는다고 적었고, 그 뒤에 스토리지 컨트롤러나 장치가 휘발성 쓰기 캐시를 가질 수 있다고 덧붙였다.
- 개념 문서는 실제 ordering 과 durability semantics 가 더 복잡하다고 밝혔다. 그래서 이 기준으로 특정 설정이 안전하다는 결론을 내지 않는다. 실제 운영에서는 장치의 flush 와 FUA semantics, power-loss protection 여부도 함께 본다.
- 개념 문서는 실제 순서 보장과 영속성의 의미가 더 복잡하다고 밝혔다. 그래서 이 기준으로 특정 설정이 안전하다는 결론을 내지 않는다. 실제 운영에서는 장치의 flush 와 FUA semantics, power-loss protection 여부도 함께 본다.
- 이 저장소에는 영속성을 실제로 재 본 실험이 없다. 여기 적은 것은 개념 문서가 서술한 구분이고 이 호스트에서 확인된 동작이 아니다.
## 예시
@@ -92,4 +92,4 @@ PostgreSQL 은 WAL 등의 durability protocol 을 사용하며 필요한 시점
- 전원 장애에도 안전한 상태 : 위 셋과 다른 단계로 센다
- 빨라졌다 : 어느 단계를 건너뛰었는지 옆에 적는다
- 포기해도 되는 데이터 : 다시 만들 수 있는 캐시 · 파생 파일
- 이 호스트에서 잰 영속성 측정 : x
- 이 호스트에서 잰 영속성 : x
@@ -54,20 +54,24 @@ source:
### 2. Target 과 Source 를 이어 붙인 뒤에 판독을 시작한다
게스트에서 lsblk 로 보이는 블록 장치와 파티션을 적고, 호스트에서 virsh domblklist <VM_NAME> 로 Target 과 Source 를 적는다. 개념 문서가 든 예시에서는 Target vda 에 Source /var/lib/libvirt/images/vm1.qcow2 가 붙어 있었다. 이 대응이 있어야 게스트의 /dev/vda 에서 virtio-blk 와 QEMU 를 지나 그 파일까지 이어진다. 그 경로는 개념 문서가 예시로 붙여 둔 이름이고 이 호스트에서 읽은 값이 아니다 — 두 명령을 이 호스트에서 돌린 출력은 아직 없다.
게스트에서 lsblk 로 보이는 블록 장치와 파티션을 적고, 호스트에서 virsh domblklist <VM_NAME> 로 Target 과 Source 를 적는다. 개념 문서가 든 예시에서는 Target vda 에 Source /var/lib/libvirt/images/vm1.qcow2 가 붙어 있었다. 이 대응이 있어야 게스트의 /dev/vda 에서 virtio-blk 와 QEMU 를 지나 그 파일까지 이어진다. 그 경로는 개념 문서가 예시로 붙여 둔 이름이다. 두 명령을 이 호스트에서 돌린 출력은 아직 없다.
### 3. Source 가 파일인지 호스트 블록 장치인지 확정한다
개념 문서는 백엔드가 반드시 파일일 필요는 없다고 적고 게스트의 /dev/vda 아래에 호스트의 /dev/nvme0n1p3 이 오는 구성을 들었다. 파일이면 qemu-img info 에 그 경로를 주어 형식을 읽고, 호스트 블록 장치면 qcow2 쪽 확인 항목은 애초에 걸리지 않는다.
두 명령이 필요로 하는 것도 다르다. qemu-img info 는 이미지 파일만 만지는 도구라 가상 머신이 꺼져 있어도 돌고 도메인이 아예 없어도 돈다. Target 과 Source 를 잇는 virsh domblklist 는 도메인이 있어야 하므로, 이미지 파일만 손에 있는 상태에서 확정되는 것은 형식까지다.
### 4. 용량은 virtual size 와 실제 할당을 갈라 읽는다
qcow2 는 가상 디스크 크기와 실제 호스트 할당량이 다를 수 있다. 개념 문서가 든 그림은 게스트가 보는 공간 100 GB 에 호스트 실제 할당 3GB 였다. 게스트가 데이터를 기록하면서 Actual 이 1GB 에서 10GB 로, 다시 40GB 로 늘 수 있다는 예도 함께 보였다. qemu-img info 를 볼 때 virtual size 와 실제 allocation 을 구분해서 봐야 한다.
qcow2 는 가상 디스크 크기와 실제 호스트 할당량이 다를 수 있다. 개념 문서가 든 그림은 게스트가 보는 공간 100 GB 에 호스트 실제 할당 3GB 였다. 게스트가 데이터를 기록하면서 실제 할당량이 1GB 에서 10GB 로, 다시 40GB 로 늘 수 있다는 예도 함께 보였다. qemu-img info 를 볼 때 virtual size 와 실제 allocation 을 구분해서 봐야 한다.
### 5. 파일이면 그 파일이 놓인 호스트 파일시스템과 블록 장치까지 적는다
백엔드가 qcow2 파일이면 QEMU 는 결국 호스트 리눅스에 파일 입출력을 요청한다. 그 요청은 Host Filesystem 과 Host Block Layer 와 NVMe Driver 를 지나 Physical NVMe 로 내려간다. 게스트의 스토리지 스택 아래에 호스트의 스토리지 스택이 한 번 더 있으므로, 어느 파일시스템과 어느 블록 장치 위에 있는지를 lsblk 로 함께 적는다.
적을 파일이 하나가 아닐 수 있다. qemu-img info 출력에 backing file 줄이 있으면 그 파일은 오버레이이고, 자기가 들고 있지 않은 클러스터를 읽을 때마다 바닥 파일을 읽는다. 바닥까지 같은 항목으로 적어야 그 게스트가 실제로 읽는 파일이 모두 덮인다.
## 적용 조건
- 가상 머신의 디스크 성능이나 용량이나 영속성을 판단하기 전
@@ -78,8 +82,8 @@ qcow2 는 가상 디스크 크기와 실제 호스트 할당량이 다를 수
## 예외
- 백엔드가 호스트 블록 장치면 qcow2 쪽 확인은 걸리지 않는다. 가상 크기와 실제 할당량의 차이도, 매핑과 메타데이터 처리도 그 구성에는 없다.
- 백엔드를 확정해도 성능은 예측되지 않는다. 개념 문서는 RAW 는 무조건 빠르고 qcow2 는 무조건 느리다는 식으로 일반화하지 말라고 못박고, 실제 성능 캐시 모드 스토리지 백엔드 부하 패턴 큐 깊이 스냅숏 체인 그 아래 파일시스템 물리 장치에 영향을 받는다고 었다. 이 기준은 무엇 위에서 재고 있는지까지만 확정한다.
-저장소에는 이 절차를 실제로 돌린 출력이 없다. 여기 적은 것은 개념 문서가 서술한 확인 순서이고 이 호스트에서 나온 값이 아니다.
- 백엔드를 확정해도 성능은 예측되지 않는다. 개념 문서는 RAW 는 무조건 빠르고 qcow2 는 무조건 느리다는 식으로 일반화하지 말라고 못박고, 실제 성능 캐시 모드, 스토리지 백엔드, 부하 패턴, 큐 깊이, 스냅숏 체인, 그 아래 파일시스템, 물리 장치에 영향을 받는다고 었다. 이 기준은 무엇 위에서 재고 있는지까지만 확정한다.
-절차를 처음부터 끝까지 돌린 출력은 이 저장소에 없다. 네 번째 규칙의 qemu-img info 는 바닥 이미지 하나에만 돌렸고, virsh domblklist 와 게스트 lsblk 의 출력은 아직 없다. 나머지는 개념 문서가 서술한 확인 순서이고 이 호스트에서 나온 값이 아니다.
## 예시
@@ -88,4 +92,5 @@ qcow2 는 가상 디스크 크기와 실제 호스트 할당량이 다를 수
- Source 의 세 갈래 : qcow2 파일 · RAW 파일 · 호스트 블록 장치
- 용량 : virtual size 와 실제 allocation 을 따로 적는다
- 파일이면 더 볼 것 : 그 파일이 올라간 파일시스템과 블록 장치
- 이 호스트에서 돌린 출력 : x
- 이 호스트에서 돌린 qemu-img info : o — 바닥 이미지 하나뿐이다
- 이 호스트에서 돌린 virsh domblklist : x
@@ -73,7 +73,7 @@ NVMe
### 5. 무엇을 볼지 정한 뒤에 명령을 고른다
명령 목록 자체는 규칙이 아니다. 무엇을 함께 볼지는 앞의 규칙들이 정하고 명령은 그 도구를 댈 뿐이라, 목록만 옮겨 적으면 무엇을 가르려고 그것을 보는지가 남지 않는다.
명령 목록 자체는 규칙이 아니다. 무엇을 함께 볼지는 앞의 규칙들이 정하고 명령은 그 도구 뿐이라, 목록만 옮겨 적으면 무엇을 가르려고 그것을 보는지가 남지 않는다.
장치 입출력 관측은 iostat -xz 1 로 한다. 읽기와 쓰기의 처리량과 IOPS 와 요청 지연과 큐 상태와 device utilization 성격의 지표가 여기서 나오고, 어떤 프로세스가 입출력을 발생시키는지는 iotop 으로 본다. 개념 문서가 양쪽에 나눠 적은 명령은 이렇다.
@@ -90,7 +90,7 @@ Host : virsh domblklist <VM_NAME> · qemu-img info 에 disk image 경로 · lsbl
## 예외
- 이 기준은 어느 자원인지를 좁힐 뿐 원인을 확정하지 않는다. 스토리지 지표가 깨끗하게 나와도 CPU 쪽을 배제하려면 vCPU 경쟁과 steal time 을 따로 본다. 그것은 CPU 가상화 쪽 기준이 받는다.
- iostat -xz 1 이 보여 주는 device utilization 성격의 지표는 NVMe 처럼 병렬성이 큰 장치에서 포화도로 그대로 읽히지 않는다. 개념 문서가 NVMe 는 높은 병렬성과 큐 깊이를 지원한다고 적었다.
- iostat -xz 1 이 보여 주는 device utilization 성격의 지표는 NVMe 처럼 병렬성이 큰 장치에서 포화도로 그대로 읽히지 않는다. 개념 문서가 NVMe 는 높은 병렬성과 큐 깊이를 지원한다고 적었다. 이 저장소의 실측 기록은 이 호스트의 루트를 /dev/nvme0n1p3 로 적었고 게스트 이미지가 전부 그 아래에 모여 있다고 적었으므로, 이 예외는 이 환경에 그대로 걸린다.
- 호스트에서 잰 값은 어느 가상 머신의 입출력인지 갈라 주지 않는다. 개념 문서는 Host Block Layer 가 그 입출력을 가상 머신 안의 프로세스가 시작했는지 호스트 프로세스가 시작했는지 본질적으로 구분해서 처리하는 계층이 아니라고 밝혔다. 가상 머신별로 가르려면 iotop 이나 게스트 쪽 관측을 같은 시각에 함께 찍는다.
- 이 저장소에는 이 기준으로 원인을 실제로 가른 측정이 없다. 여기 적은 것은 개념 문서가 서술한 판독 순서이고 이 호스트에서 확인된 값이 아니다.