Files
document-haness/docs/keycloak-session-store/source/docs/guides/experiments/README.md
T
DongHyeonkaandClaude Opus 5 2109f726fe 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>
2026-09-17 11:01:55 +09:00

7.3 KiB

실험 재현 가이드 26편

각 편은 직접 쳐서 재현하는 문서다. 무엇을 발견했는지는 docs/experiment-*.md 가 적고, 여기는 그것을 다시 만들려면 무엇을 어떤 순서로 치는가를 적는다.

전제

기반 7단계가 끝나 있어야 한다. 특히 05 까지는 모든 실험의 공통 전제이고, 지표를 보는 실험은 06 도 필요하다.

어디서 치는가 — 기본은 [lab host], sudo 는 붙이지 않는다

모든 kubectl 은 lab host(test-server)에서 sudo 없이 친다. 02 에서 kubeconfig 를 당신 홈(~/.kube/config)에 뒀기 때문이다.

kubectl -n keycloak-lab get pods        # 이렇게
sudo kubectl -n keycloak-lab get pods   # 이렇게 치면 안 된다

sudo 를 붙이면 root 환경으로 돌아 그 kubeconfig 를 못 본다. root 홈에는 ~/.kube/config 가 없으므로 localhost:8080 으로 붙으려다 connection refused 로 끝난다. 클러스터 문제가 아니라 누구의 설정 파일을 읽느냐의 문제다.

어디서 kubectl 형태 이유
[lab host] (기본) kubectl … kubeconfig 가 당신 홈에 있다
[kc-lab-1] 안에서 ssh kc-lab-1 'sudo kubectl …' 게스트의 kubeconfig 는 /etc/rancher/k3s/k3s.yaml 이고 root 만 읽는다

게스트 셸이 필요한 것은 nft·tc·systemctl 같은 노드 자체를 건드리는 명령뿐이다. 클러스터 조회·조작은 전부 lab host 에서 한다 — 「어느 기계에서 치는가」 의 규칙 그대로다.

각 편의 구조

이 가이드가 끝나면 · 전제 · 주의 · 표시 규약
0  왜 이 실험인가
1  기준선          ← 주입 전에 평시를 잡는다
2  주입
3  주입 검증        ← 여기가 대부분의 편에서 가장 중요하다
4  관찰
5  복구
막히면 · 다음

3번이 핵심인 편이 많다. 이 실험대에서 주입은 아홉 번 조용히 실패했고, 실패한 주입은 「아무 일도 없었다」로 보여 「영향이 없다」와 구별되지 않는다. 그래서 결과를 읽기 전에 대상이 실제로 그 상태인지를 따로 확인한다.

표시 규약

표시
실측 증거 파일에 있는 출력 원문. 그대로 나온다
형태 모양만 같고 값은 환경마다 다르다
미검증 손으로 치기 좋게 고쳐 쓴 형태. 원래 실행에서 그대로 쓰이지는 않았다

A층 — Keycloak 자체가 깨질 때

가이드 무엇을 직접 보게 되나
A-0 세션 공유 경로 세션을 나르는 것이 Infinispan 이 아니라 PostgreSQL 이라는 것
A-1 7800 차단 세션 공유는 안 깨지고 로그아웃 전파만 깨진다
A-2 DB 정지 503 이 나는 동안에도 up 이 1 이다
A-3 DB 크래시 200 을 받은 로그인 153건 중 4건이 DB 에 없다
A-4 노드 상실 죽은 노드가 40초 동안 Ready 로 읽힌다
A-5 비대칭 분단 한 방향만 막으면 열린 쪽으로 재연결한다
A-6 지연 주입 200밀리초가 두 단계를 지나 22.2초가 된다
A-7 volatile 비교 설정 하나로 A층 결론 셋이 뒤집힌다
A-7a volatile 원인 확정 같은 설정이 캐시 온도만으로 400·500·200 세 답을 낸다
A-8 롤링 재시작 세션은 남고 캐시만 사라진다

B층 — 애플리케이션 쪽 저장소

★ B-0 은 판정이 아니라 배포다. BFF 와 Redis 를 여기서 처음 띄우고, B-1~B-7 전부가 이것을 전제로 한다. 기반 7단계(00~06)에는 BFF·Redis 가 없다 — 그래서 05 를 끝낸 시점에 kubectl get pods -l app=bff 를 쳐도 아무것도 안 나오는 것이 정상이다. A 층은 BFF 없이 Keycloak 만으로 돈다.

가이드 무엇을 직접 보게 되나
B-0 BFF·Redis 배포B 층의 전제 아무것도 안 주면 Spring 이 무엇을 고르는가
B-1 Redis 세션 저장소 세션은 옮겨지는데 토큰은 안 따라온다
B-2 다중 인스턴스 저장소를 옮겨도 안 고쳐지는 것 — 원인은 기본키다
B-3 refresh 경쟁 이긴 요청의 토큰조차 못 쓴다
B-4 Edge 인가 범위 위조 헤더가 그대로 도착한다
B-5 Redis 상실·영속화 볼륨 없는 영속화 설정은 장식이다
B-6 키 회전 JWKS 캐시에 유예 구간이 없다
B-7 cookie secret 회전 겹침 구간을 만들 수 없고 서버 세션이 고아로 남는다
B-7a 고아 세션 정리 TTL 로 생성 시각을 역산해 골라낸다

C층 — SSO 와 로그아웃

가이드 무엇을 직접 보게 되나
C-1 다중 앱 SSO SSO 는 되는데 로그아웃이 안 퍼진다
C-2 백채널 로그아웃 양쪽 다 없었다 — 한쪽만 고치면 여전히 안 된다

D층 — 운영

가이드 무엇을 직접 보게 되나
D-1 백업·복구 백업이 진짜 백업인지 스키마를 지워서 확인한다
D-2 버전 업그레이드 롤백이 되는 조건은 스키마가 안 움직였을 때다
D-3 비밀 관리 base64 는 인코딩이지 암호화가 아니다
D-4 인증서 갱신 갱신은 성공했는데 38분 25초 동안 옛 인증서가 나갔다
D-4a deploy 훅 훅 하나로 그 공백이 1~2초가 된다

순서

A-0 을 먼저 한다. 나머지 A층 결론이 전부 거기서 확인한 「세션이 어디 있는가」 위에 서 있다.

A-0 ─┬─ A-1 ─┬─ A-5
     │        └─ A-6
     ├─ A-2 ── A-3 ── D-1 ── D-2
     ├─ A-4
     ├─ A-8
     └─ A-7 ── A-7a          ← A층을 다 한 뒤 설정 하나만 바꿔 재실행한다

B-0 ── B-1 ─┬─ B-2 · B-3 · B-4 · B-5 · B-6
            └─ B-7 ── B-7a

C-1 ── C-2                    D-3 · D-4 ── D-4a  (언제든 독립적으로)

A-7 을 A층 마지막에 두는 이유 — 앞의 실험을 다 마친 뒤 설정 하나만 바꿔 재실행하면 같은 주입에 대한 정반대 결과를 한 벌로 얻는다.

안전

각 편의 2번(주입)부터 상태가 바뀐다. 모든 편이 되돌리는 명령을 주입보다 먼저 보여 주고, 5번에서 원상복구를 확인한다.

호스트(test-server)에서 하는 일은 sudo 비밀번호가 필요해 사람이 직접 쳐야 한다. D-1 과 D-4 가 여기 해당하며, 각 편이 어느 단계가 그런지 적는다.