Files
keycloak-pattern/docs/experiment-followup-untested-items.md
T
DongHyeonkaandClaude Opus 5 e0d27d47ce docs: correct the places where documents contradicted their own evidence
An independent audit found ten documents printing values their evidence files do not contain. C-1 printed a session count of 0 where the evidence says 4, C-2 printed a success readback for a command that exited 1, and A-1 credited the conntrack flush with a split that the timestamps attribute to a pod restart four seconds earlier.

Also measured wal_writer_delay, which A-3 had asserted as matching without ever querying it, relabelled the A-6 control that moved 41 percent, noted A-8's nine-sample resolution, corrected D-1's RTO to the 41 seconds its own timeline shows, and added a correction banner to D-2. Every experiment document now links its evidence files with their real collection times, and the duplicate screenshots are documented as duplicates.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 16:35:49 +09:00

265 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 후속 — 미측정으로 남겼던 항목을 채운다
브랜치 `feature/keycloak-followup-untested-items` ·
증거 [`docs/evidence/followup/`](evidence/followup/) ·
2026-09-04 17:3518:20 KST
23개 실험을 마치며 **세 항목을 "못 했다" 로 남겼다.** 그중 둘을 채우고,
셋째(D-4 강제 갱신)는 권한이 필요해 별도로 진행한다.
---
## 0. 결론부터
| 항목 | 결과 |
|---|---|
| **D-2 정방향 업그레이드** | **무중단.** 87회 요청 전부 200 |
| **그리고 D-2 의 결론이 정밀해졌다** | **"롤백 불가" 는 조건부다** — 스키마가 바뀌었을 때만 |
| **B-4 ③ role 변경 반영 시점** | **요청 횟수와 무관하다.** 세션이 새로 만들어져야 한다 |
| **B층에 Grafana 증거가 없는 이유** | **관측 대상에 없다.** 안 찍은 것이 아니다 |
---
## 1. D-2 정방향 업그레이드 — 26.7.0 → 26.7.3
### 개념 — 왜 이 방향을 못 했었나
D-2 를 처음 할 때 26.7.0 보다 새 이미지를 몰라 **역방향(26.0)만 시험**했다.
태그 목록을 조회하니 26.7.1 / 26.7.2 / **26.7.3** 이 있었다.
```bash
curl -s "https://quay.io/api/v1/repository/keycloak/keycloak/tag/?limit=40&onlyActiveTags=true"
```
### 절차 — D-1 의 교훈대로 백업이 먼저다
```bash
kubectl -n keycloak-lab exec deploy/postgres -- pg_dump -U keycloak -d keycloak \
--clean --if-exists > /tmp/pre-2673.sql # 396333 bytes
# 1초 간격으로 외부 진입점을 찍으면서 태그를 바꾼다
( for i in $(seq 1 150); do
printf "%s " "$(curl -s -o /dev/null -w '%{http_code}' --max-time 3 https://auth.hyeonworks.com/realms/master)"
sleep 1
done > /tmp/avail.txt ) &
kubectl -n keycloak-lab set image statefulset/keycloak keycloak=quay.io/keycloak/keycloak:26.7.3
kubectl -n keycloak-lab rollout status statefulset/keycloak --timeout=600s
```
### 결과 — 무중단
```
200 200 200 ... (87회)
200 응답: 87 회
비200 : 0 회
소요: 15:22:59 → 15:24:26 (87초)
```
![업그레이드 구간의 cluster_size 와 up](evidence/d2-version-upgrade/d2-upgrade-window.png)
**파드가 하나씩 교체되며 `cluster_size` 가 2 → 1 → 2 를 두 번 반복한다.**
각 파드의 `up` 시계열이 끝나고 새 시계열이 시작되는 것이 함께 보인다.
```
마이그레이션: 210 → 210 ← 스키마 변경 없음
세션: 3 → 3 ← 유지
Infinispan: 16.0.12 → 16.0.14
restarts=0
```
---
## 2. ★ 그래서 D-2 의 결론을 정밀화한다
**"스키마 변경이 없었다면 롤백이 될 것" 이라는 가설이 생겼고, 시험했다.**
```bash
kubectl -n keycloak-lab set image statefulset/keycloak keycloak=quay.io/keycloak/keycloak:26.7.0
```
```
200 응답: 43 회 / 비200: 1
Keycloak 26.7.0
마이그레이션: 210 · 세션: 3
restarts=0
```
**롤백이 성공했다.**
| 버전 차 | `databasechangelog` | 롤백 |
|---|---|---|
| 26.7.0 → **26.0** | 체크섬 불일치 | **불가** (`ValidationFailedException`) |
| 26.7.0 ↔ **26.7.3** | **210 → 210, 변화 없음** | **가능** |
> **처음 D-2 에서 "롤백은 안 된다" 고 쓴 것은 과했다.**
> 정확히는 **"스키마가 바뀌었으면 안 된다"** 이고,
> **패치 릴리스처럼 스키마가 그대로면 태그를 되돌리는 것으로 충분하다.**
>
> 판단 기준은 버전 번호가 아니라 **`databasechangelog` 의 행 수가 바뀌었는가**다.
```bash
# 업그레이드 전후로 이것만 비교하면 롤백 가능 여부를 안다
kubectl -n keycloak-lab exec deploy/postgres -- psql -U keycloak -d keycloak -tAc \
"select count(*) from databasechangelog"
```
### 전환 순간의 `000` 1회
```
200 ×24 000 200 ×19
```
**서버 오류가 아니라 `--max-time 3` 타임아웃**이다. 파드 전환 순간 요청 하나가
3초를 넘겼다. 정방향에서는 0회였다.
> **"무중단" 은 관측 해상도에 달려 있다.** 1초 간격·3초 타임아웃으로는
> 44회 중 1회가 걸렸다. **더 촘촘히 보면 더 보일 것이다.**
---
## 3. B-4 ③ — role 변경은 언제 반영되는가
### 왜 못 했었나
B-4 를 할 때 **oauth2-proxy 가 아직 배포되지 않아** "proxy session" 이
존재하지 않았다. B-7 에서 배포했으므로 이제 측정할 수 있다.
### 방법
`X-Auth-Request-Roles` 대신 **이미 전달되고 있는 `x-forwarded-email`** 을 썼다.
role 을 헤더로 내보내려면 추가 설정이 필요한데, **"IdP 의 클레임 변경이
언제 반영되는가" 라는 질문은 어느 클레임이든 같다.**
```bash
# 1. 기준선
fetch('/api/echo') → x-forwarded-email = labuser@example.com
# 2. IdP 에서 바꾼다
kcadm.sh update users/<id> -r keycloak-patterns -s email=CHANGED-labuser@example.com
# 3. 반복 요청
for (i=1..12) fetch('/api/echo') # 0.5초 간격
```
### 결과 — 반영되지 않는다
```
1: labuser@example.com
2: labuser@example.com
...
12: labuser@example.com ← 12회 · 6초 동안 옛 값
```
세션을 지우고 재인증시키자
```
재인증 후 email = changed-labuser@example.com
```
### 개념 — 세션은 로그인 시점의 스냅샷이다
```
로그인 → IdP 가 준 클레임을 세션에 담는다
이후 요청 → 세션에서 읽어 헤더로 내보낸다
└─ IdP 를 다시 부르지 않는다
IdP 에서 변경 → 세션은 모른다
```
**oauth2-proxy 에 `--cookie-refresh` 가 설정되어 있지 않다.**
설정하면 그 주기마다 토큰을 갱신하며 클레임을 다시 받는다.
| 설정 | 반영 시점 |
|---|---|
| 지금 (`--cookie-refresh` 없음) | **쿠키 만료(1시간) 또는 재인증까지 안 됨** |
| `--cookie-refresh=5m` | 최대 5분 |
> **Q4 는 "몇 번째 요청부터 반영되는지" 를 물었는데, 답은 "요청으로는 안 된다" 이다.**
> 요청 횟수가 아니라 **세션의 나이**가 정한다.
>
> **이것이 Q4 의 설계 판단 2번(role·tenant 변경이 즉시 반영돼야 하는가)에
> 직접 답한다** — 즉시가 필요하면 헤더 방식은 맞지 않는다.
---
## 4. B층에 Grafana 증거가 없는 이유
문서 감사에서 **B-1·B-3·B-4·B-5 에 스크린샷이 없는 것**이 드러나
소급해서 찍으려다 원인을 확인했다.
```
=== Prometheus 가 실제로 긁는 대상 ===
keycloak 2개
kubelet 2개
node-exporter 2개
prometheus 1개
=== B층 구성 요소의 지표가 있는가 ===
redis_up 시계열 0개
redis_connected_clients 시계열 0개
pg_up 시계열 0개
pg_stat_database_numbackends 시계열 0개
```
**Redis 도 PostgreSQL 도 BFF 도 긁는 대상에 없다.**
> **스크린샷을 안 찍은 것이 아니라 지표가 없다.**
> A층이 Grafana 증거를 남길 수 있었던 것은 Keycloak 이 `/metrics` 를
> 내놓고 그것을 scrape 대상에 넣어뒀기 때문이다.
>
> **관측은 "나중에 붙이는 것" 이 아니라 실험 설계에 포함되어야 한다.**
> A-2 에서 `kube-state-metrics` 가 없다는 것을, A-6 에서 응답 시간
> 히스토그램이 없다는 것을 찾았는데, **B층 전체가 빠져 있던 것은
> 문서 감사를 하고서야 드러났다.**
### 보완하려면
| 대상 | 방법 |
|---|---|
| Redis | `redis_exporter` 사이드카 또는 Deployment |
| PostgreSQL | `postgres_exporter` |
| BFF | 이미 actuator 가 있다 — `/actuator/prometheus` 노출 + scrape 추가 |
| 파드 readiness | `kube-state-metrics` (A-2 에서 이미 찾은 항목) |
---
## 5. 남은 것 — D-4 강제 갱신
```
$ sudo -n -l
sudo: a password is required
```
**호스트 sudo 가 비밀번호를 요구해 `certbot renew --force-renewal`
`systemctl reload nginx` 를 실행할 수 없다.** 사람이 함께 있어야 한다.
측정 계획은 준비되어 있다.
```bash
# 측정 쪽 (내가 실행)
while true; do
printf '%s ' "$(curl -s -o /dev/null -w '%{http_code}' --max-time 2 https://auth.hyeonworks.com/realms/master)"
sleep 0.2
done
# 주입 쪽 (사람이 실행)
sudo certbot renew --force-renewal
```
**0.2초 간격으로 재는 이유** — 2절에서 1초 간격으로는 전환을 거의 못 잡았다.
nginx reload 는 그보다 훨씬 짧을 것이므로 해상도를 올려야 한다.
---
## 증거 파일
**증거 수집 시각: 2026-09-04 15:23 16:17 KST** (파일 mtime 기준. 문서 상단의 시각 표기는 작성 시점이라 다를 수 있다.)
| 파일 | 종류 |
|---|---|
| [`01-d2-forward-upgrade.txt`](evidence/followup/01-d2-forward-upgrade.txt) | 터미널 원문 |
| [`02-d2-rollback-same-schema.txt`](evidence/followup/02-d2-rollback-same-schema.txt) | 터미널 원문 |
| [`03-b4-role-propagation.txt`](evidence/followup/03-b4-role-propagation.txt) | 터미널 원문 |
| [`04-observability-gap.txt`](evidence/followup/04-observability-gap.txt) | 터미널 원문 |
파일별 상세는 [`evidence/followup/README.md`](evidence/followup/README.md).