docs(guides): reproduction guides for all 26 experiments

Written by subagents running under the writing-practitioner-guides skill,
one guide per experiment, 22,566 lines. Each walks a reader from baseline
capture through injection, injection verification, observation and recovery.

Section 3 carries the weight in most of them. Injection failed silently nine
times in this lab, and a failed injection looks exactly like no effect — so
the guides verify the target is actually in the intended state before
reading any result. A-4 makes virsh list the only proof because the node
reads Ready for 40 seconds after the machine is off; A-5 makes the packet
counter the sole go/no-go because a rule on the wrong node produces an empty
result that reads like a finding; A-6 quotes the run where 적용완료 was
printed between four Cannot find device "eth0" lines.

The traps the guides are built around are ones that invert a conclusion
rather than merely annoy:

  A-0   emptying the session table without a restart leaves cache entries
        that get counted as replication arriving
  A-2   dropping -o /dev/null fuses body and status into one string
  A-3   presence of "ready to accept connections" instead of its timestamp
  B-2   row count alone reads an UPDATE as nothing having happened
  B-4   tr ',' '\n' splits ["admin","editor"] so only admin is seen
  B-7   no login screen means the cookie died and SSO re-authenticated
  C-1   counting sessions without joining realm counts your own kcadm one
  D-1   kubectl exec without -i restores nothing and still exits 0
  D-4a  "ran with error output" is what success looks like

Every quoted block is copied from docs/evidence/ and marked 실측; reshaped
commands are marked 미검증 rather than passed off as measured. Where a source
document carries a ★ correction the guides follow the corrected claim — A-7's
REVOKED_TOKEN hypothesis, C-1's session count, B-2's schema attribution.

Two hazards are stated rather than smoothed over: B-6 deletes a key that
cannot be recreated, and D-1/D-4 need host sudo, which asks for a password,
so those steps say a person must type them.

Audit over all 26: 672 interpretation pairs, 486 evidence citations, 117
undo sections, and zero occurrences of the patterns the skill forbids —
no python data processing, no deprecated kubectl get endpoints, no
placeholders, no bare kcadm.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
DongHyeonka
2026-09-07 18:29:00 +09:00
co-authored by Claude Opus 5
parent 8062cc9a19
commit 6f6ab86345
27 changed files with 22678 additions and 0 deletions
+112
View File
@@ -0,0 +1,112 @@
# 실험 재현 가이드 26편
각 편은 **직접 쳐서 재현**하는 문서다. 무엇을 발견했는지는
[`docs/experiment-*.md`](../../) 가 적고, 여기는 **그것을 다시 만들려면 무엇을
어떤 순서로 치는가**를 적는다.
## 전제
[기반 7단계](../)가 끝나 있어야 한다. 특히 [05](../05-keycloak/) 까지는
모든 실험의 공통 전제이고, 지표를 보는 실험은 [06](../06-observability/) 도
필요하다.
## 각 편의 구조
```
이 가이드가 끝나면 · 전제 · 주의 · 표시 규약
0 왜 이 실험인가
1 기준선 ← 주입 전에 평시를 잡는다
2 주입
3 주입 검증 ← 여기가 대부분의 편에서 가장 중요하다
4 관찰
5 복구
막히면 · 다음
```
**3번이 핵심인 편이 많다.** 이 실험대에서 주입은 아홉 번 조용히 실패했고,
실패한 주입은 「아무 일도 없었다」로 보여 「영향이 없다」와 구별되지 않는다.
그래서 결과를 읽기 전에 대상이 실제로 그 상태인지를 따로 확인한다.
## 표시 규약
| 표시 | 뜻 |
|---|---|
| **실측** | 증거 파일에 있는 출력 원문. 그대로 나온다 |
| **형태** | 모양만 같고 값은 환경마다 다르다 |
| **미검증** | 손으로 치기 좋게 고쳐 쓴 형태. 원래 실행에서 그대로 쓰이지는 않았다 |
## A층 — Keycloak 자체가 깨질 때
| | 가이드 | 무엇을 직접 보게 되나 |
|---|---|---|
| A-0 | [세션 공유 경로](a0-session-replication.md) | 세션을 나르는 것이 Infinispan 이 아니라 PostgreSQL 이라는 것 |
| A-1 | [7800 차단](a1-jgroups-transport-block.md) | 세션 공유는 안 깨지고 로그아웃 전파만 깨진다 |
| A-2 | [DB 정지](a2-database-loss.md) | 503 이 나는 동안에도 `up` 이 1 이다 |
| A-3 | [DB 크래시](a3-database-crash.md) | 200 을 받은 로그인 153건 중 4건이 DB 에 없다 |
| A-4 | [노드 상실](a4-node-loss.md) | 죽은 노드가 40초 동안 `Ready` 로 읽힌다 |
| A-5 | [비대칭 분단](a5-asymmetric-partition.md) | 한 방향만 막으면 열린 쪽으로 재연결한다 |
| A-6 | [지연 주입](a6-latency-injection.md) | 200밀리초가 두 단계를 지나 22.2초가 된다 |
| A-7 | [volatile 비교](a7-volatile-comparison.md) | 설정 하나로 A층 결론 셋이 뒤집힌다 |
| A-7a | [volatile 원인 확정](a7a-volatile-cause.md) | 같은 설정이 캐시 온도만으로 400·500·200 세 답을 낸다 |
| A-8 | [롤링 재시작](a8-rolling-restart.md) | 세션은 남고 캐시만 사라진다 |
## B층 — 애플리케이션 쪽 저장소
| | 가이드 | 무엇을 직접 보게 되나 |
|---|---|---|
| B-0 | [자동구성 확인](b0-bff-redis-deploy.md) | 아무것도 안 주면 Spring 이 무엇을 고르는가 |
| B-1 | [Redis 세션 저장소](b1-redis-session-store.md) | 세션은 옮겨지는데 토큰은 안 따라온다 |
| B-2 | [다중 인스턴스](b2-multi-instance-session.md) | 저장소를 옮겨도 안 고쳐지는 것 — 원인은 기본키다 |
| B-3 | [refresh 경쟁](b3-refresh-token-contention.md) | 이긴 요청의 토큰조차 못 쓴다 |
| B-4 | [Edge 인가 범위](b4-edge-authorization-scope.md) | 위조 헤더가 그대로 도착한다 |
| B-5 | [Redis 상실·영속화](b5-redis-loss-persistence.md) | 볼륨 없는 영속화 설정은 장식이다 |
| B-6 | [키 회전](b6-key-rotation.md) | JWKS 캐시에 유예 구간이 없다 |
| B-7 | [cookie secret 회전](b7-cookie-secret-rotation.md) | 겹침 구간을 만들 수 없고 서버 세션이 고아로 남는다 |
| B-7a | [고아 세션 정리](b7a-orphan-session.md) | TTL 로 생성 시각을 역산해 골라낸다 |
## C층 — SSO 와 로그아웃
| | 가이드 | 무엇을 직접 보게 되나 |
|---|---|---|
| C-1 | [다중 앱 SSO](c1-multi-app-sso.md) | SSO 는 되는데 로그아웃이 안 퍼진다 |
| C-2 | [백채널 로그아웃](c2-backchannel-logout.md) | 양쪽 다 없었다 — 한쪽만 고치면 여전히 안 된다 |
## D층 — 운영
| | 가이드 | 무엇을 직접 보게 되나 |
|---|---|---|
| D-1 | [백업·복구](d1-backup-restore.md) | 백업이 진짜 백업인지 스키마를 지워서 확인한다 |
| D-2 | [버전 업그레이드](d2-version-upgrade.md) | 롤백이 되는 조건은 스키마가 안 움직였을 때다 |
| D-3 | [비밀 관리](d3-secret-management.md) | base64 는 인코딩이지 암호화가 아니다 |
| D-4 | [인증서 갱신](d4-certificate-renewal.md) | 갱신은 성공했는데 38분 25초 동안 옛 인증서가 나갔다 |
| D-4a | [deploy 훅](d4a-deploy-hook.md) | 훅 하나로 그 공백이 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 가 여기 해당하며, 각 편이 어느 단계가 그런지 적는다.