docs: restructure roadmap into A/B/operations layers and add five operational items
Adds backup rehearsal, version upgrade, observability, secret management and certificate renewal. Corrects the experiment order so the refresh-token contention test runs after the shared store exists. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
df4d3b4345
commit
d6f8b9f8b3
@@ -39,18 +39,34 @@ if [ "$expected_count" -ne 39 ]; then
|
||||
```
|
||||
✅ 환경 구축
|
||||
✅ 2홉 프록시 헤더 계약
|
||||
──────────────────────────────────────────────
|
||||
Keycloak 멀티노드 클러스터 형성
|
||||
persistent vs volatile 세션
|
||||
refresh token 동시 갱신 경쟁
|
||||
Redis 애플리케이션 세션 + SSO
|
||||
장애 주입과 복구
|
||||
──────────────────────────────────────────────────────────
|
||||
A층 — Keycloak 자체 (공개 질문에 없는 영역)
|
||||
1. 멀티노드 클러스터 형성
|
||||
2. persistent vs volatile 세션
|
||||
|
||||
B층 — 애플리케이션 세션 (공개 열린 질문 대응)
|
||||
3. BFF 저장소 결정 → Q3
|
||||
4. 다중 인스턴스 운영 → Q1
|
||||
5. Refresh Token 경쟁 → Q2 ★ 3 이후여야 재현됨
|
||||
6. Edge 인가 범위 → Q4
|
||||
|
||||
공통 — 운영 역량
|
||||
7. 장애 주입과 복구
|
||||
8. 백업과 복구 리허설
|
||||
9. 버전 업그레이드
|
||||
10. 관측성
|
||||
11. 비밀 관리
|
||||
12. 인증서 갱신 실측
|
||||
```
|
||||
|
||||
**순서 근거는 [`open-questions-coverage.md`](open-questions-coverage.md)에 있다.**
|
||||
특히 5번(refresh 경쟁)은 3번(저장소 공유) 이후여야 **재현 자체가 성립한다.**
|
||||
|
||||
### ✅ 완료 — 환경 구축
|
||||
|
||||
2노드 k3s 실험대. 상세는 [`deploy/lab/README.md`](../deploy/lab/README.md),
|
||||
개념은 [`session-lab-concepts.md`](session-lab-concepts.md).
|
||||
개념은 [`session-lab-concepts.md`](session-lab-concepts.md),
|
||||
운영 도구는 [`session-lab-operations.md`](session-lab-operations.md).
|
||||
|
||||
```
|
||||
브라우저 ─https─▶ 호스트 nginx(TLS 종료) ─▶ Traefik ─▶ Pod
|
||||
@@ -80,70 +96,53 @@ if [ "$expected_count" -ne 39 ]; then
|
||||
세 스위치를 다 켜고 나니 새 구멍이 생겼다. Traefik을 거치지 않고 앱에 직접
|
||||
요청하면서 헤더를 붙이자 **그대로 통과했다.**
|
||||
|
||||
```bash
|
||||
kubectl -n header-lab run t --rm -i --restart=Never --image=curlimages/curl -- \
|
||||
curl -s http://echo:8081/api/echo \
|
||||
-H 'X-Forwarded-Proto: https' -H 'X-Forwarded-Host: evil.example.com' -H 'X-Forwarded-For: 1.2.3.4'
|
||||
```
|
||||
|
||||
```
|
||||
serverName evil.example.com ← 위조 성공
|
||||
remoteAddr 1.2.3.4 ← 위조 성공
|
||||
requestUrl https://evil.example.com/api/echo
|
||||
```
|
||||
|
||||
**원인은 두 신뢰 설정이 모두 "대역"을 믿기 때문이다.**
|
||||
|
||||
| 계층 | 신뢰 범위 |
|
||||
|---|---|
|
||||
| Traefik `trustedIPs` | 파드 대역 전체 (`10.42.0.0/16`) |
|
||||
| 앱 Tomcat `internalProxies` | 사설 대역 전체 (기본 정규식) |
|
||||
|
||||
IP로는 Traefik을 특정할 수 없다. **파드 IP가 재시작마다 바뀌기 때문**이다
|
||||
(측정 중 실제로 `10.42.0.8` → `10.42.1.12`로, 노드까지 옮겨갔다).
|
||||
그래서 대역을 믿는 것으로 대신하게 되고, 그 대역 안의 **아무 파드나** 같은
|
||||
신뢰를 얻는다.
|
||||
|
||||
**막은 방법 — NetworkPolicy는 IP가 아니라 라벨로 지정한다.**
|
||||
|
||||
```yaml
|
||||
- from:
|
||||
- namespaceSelector: { matchLabels: { kubernetes.io/metadata.name: kube-system } }
|
||||
podSelector: { matchLabels: { app.kubernetes.io/name: traefik } }
|
||||
```
|
||||
|
||||
차단 후 재측정에서 정상 경로는 그대로 동작하고 우회는 `curl exit 7`
|
||||
(연결 자체 실패)로 막혔다. 파드 재시작은 0회 — kubelet probe 경로를
|
||||
노드 cni0 주소 `/32`로 따로 허용했기 때문이다. 이 규칙을 빠뜨리면
|
||||
probe가 실패해 **파드가 재시작 루프에 빠진다.**
|
||||
두 신뢰 설정이 모두 **"대역"을 믿기 때문**이다. IP로는 Traefik을 특정할 수
|
||||
없다 — 파드 IP가 재시작마다 바뀐다(측정 중 `10.42.0.8` → `10.42.1.12`로,
|
||||
노드까지 옮겨갔다). **NetworkPolicy는 IP가 아니라 라벨로 지정**하므로 이를
|
||||
닫는다.
|
||||
|
||||
**"헤더를 믿는다"와 "앞에 반드시 프록시가 있다"는 한 쌍이다.**
|
||||
앞의 것만 하면 위조 경로가 열린 채로 남는다. 이 교훈이 AP4에서 결정적이
|
||||
된다 — `X-Auth-Request-*`가 위조되면 그것은 쿠키 속성이 아니라
|
||||
**신원 위조**이기 때문이다.
|
||||
이 교훈이 6번(Edge 인가)에서 결정적이 된다 — `X-Auth-Request-*`가 위조되면
|
||||
그것은 쿠키 속성이 아니라 **신원 위조**다.
|
||||
|
||||
**다음 작업에 미치는 영향** — Keycloak을 올릴 때
|
||||
**이 결과가 뒤에 미치는 영향** — Keycloak을 올릴 때
|
||||
`KC_PROXY_HEADERS=xforwarded`와 `KC_HOSTNAME=https://auth.hyeonworks.com`을
|
||||
근거를 갖고 넣을 수 있고, 로그인이 깨져도 프록시 원인은 배제하고 볼 수 있다.
|
||||
|
||||
---
|
||||
|
||||
### 다음 — Keycloak 멀티노드 클러스터 형성
|
||||
## A층 — Keycloak 자체
|
||||
|
||||
**공개 열린 질문 네 개는 전부 애플리케이션 계층(B층)이다.** Keycloak 자체의
|
||||
클러스터링과 세션 저장을 다루는 질문은 아직 등록되어 있지 않다.
|
||||
|
||||
**그러나 이 두 항목이 이 실험대의 존재 이유에 더 가깝다.** B층 실험은 replica
|
||||
2개면 되므로 Docker Compose로도 상당 부분 가능하지만, A층은 **독립된 커널
|
||||
두 개**를 요구한다. 실험 후 결과를 **새 열린 질문으로 등록할 후보**다.
|
||||
|
||||
### 1. Keycloak 멀티노드 클러스터 형성
|
||||
|
||||
브랜치: `feature/keycloak-multinode-cluster-jdbc-ping`
|
||||
|
||||
**확인할 것**
|
||||
|
||||
- Keycloak 2개 파드가 **서로를 발견해 하나의 클러스터를 이루는가**
|
||||
- k3s 기본 디스커버리는 `jdbc-ping` — PostgreSQL의 `JGROUPS_PING` 테이블로
|
||||
서로를 찾는다. 멀티캐스트가 필요 없다
|
||||
- **TCP 7800이 막히면 어떻게 되는가** — 디스커버리는 DB로 하지만 실제 클러스터
|
||||
통신은 7800이다. 막으면 "DB에는 서로 등록되는데 클러스터가 안 붙는" 증상이
|
||||
나온다. 단일 노드에서는 재현 불가능한 고장이며, 이 실험대를 2노드로 만든
|
||||
이유 중 하나다
|
||||
- Keycloak 26의 기본 디스커버리는 `jdbc-ping` — PostgreSQL의 `JGROUPS_PING`
|
||||
테이블로 서로를 찾는다. 멀티캐스트가 필요 없다
|
||||
- **TCP 7800이 막히면 무엇이 먼저 보이는가** — 디스커버리는 DB로 하지만 실제
|
||||
클러스터 통신은 7800이다. 막으면 "DB에는 서로 등록되는데 클러스터가 안 붙는"
|
||||
증상이 나온다. **단일 노드에서는 재현 불가능한 고장**이며, 이 실험대를
|
||||
2노드로 만든 이유 중 하나다
|
||||
|
||||
**주의** — Traefik이 replica 1이라 그 파드가 있는 노드를 죽이면 진입점 자체가
|
||||
사라진다. 노드 상실 실험 전에 Traefik을 2개로 늘릴지 결정해야 한다.
|
||||
**주의** — Traefik이 replica 1이므로 그 파드가 있는 노드를 죽이면 진입점
|
||||
자체가 사라질 수 있다. **훈련 3([`session-lab-operations.md`](session-lab-operations.md))에서
|
||||
먼저 확인**하고 replica 조정 여부를 정한다.
|
||||
|
||||
**설정 근거** — 헤더 계약에서 확정한 값을 그대로 쓴다.
|
||||
|
||||
@@ -154,7 +153,7 @@ KC_PROXY_HEADERS=xforwarded
|
||||
KC_HTTP_ENABLED=true
|
||||
```
|
||||
|
||||
### persistent vs volatile 세션
|
||||
### 2. persistent vs volatile 세션
|
||||
|
||||
브랜치: `feature/keycloak-persistent-vs-volatile-sessions`
|
||||
|
||||
@@ -165,44 +164,104 @@ KC_HTTP_ENABLED=true
|
||||
- `--features-disabled=persistent-user-sessions`로 volatile 전환 시 비교
|
||||
- **PostgreSQL을 죽이면** 각각 어떻게 되는가
|
||||
- **노드 하나를 죽이면** 세션이 살아남는가
|
||||
- **롤링 배포 시** 로그아웃되는가 ← 운영에서 가장 자주 겪는 시나리오
|
||||
- **롤링 배포 시 로그아웃되는가** ← 운영에서 가장 자주 겪는 시나리오이며,
|
||||
사실상 persistent를 켜는 진짜 이유다
|
||||
|
||||
이것이 "세션을 DB에 둘 때 vs 안 둘 때"의 Keycloak 버전이다.
|
||||
|
||||
### refresh token 동시 갱신 경쟁
|
||||
---
|
||||
|
||||
브랜치: `feature/keycloak-refresh-token-concurrency`
|
||||
## B층 — 애플리케이션 세션
|
||||
|
||||
**확인할 것**
|
||||
공개 열린 질문 네 개에 대응한다. 각 질문이 요구하는 검증 단계는
|
||||
[`open-questions-coverage.md`](open-questions-coverage.md)에 항목별로 있다.
|
||||
|
||||
- realm은 이미 revoke refresh token + max reuse 0
|
||||
([`refresh-token-rotation.md`](refresh-token-rotation.md))
|
||||
- **같은 RT로 두 노드에 동시 refresh**하면 몇 개가 성공하는가
|
||||
- 재사용 탐지가 **세션 전체를 무효화하는가**
|
||||
- Infinispan `DIST_SYNC` 복제 지연이 경쟁 윈도우에 미치는 영향
|
||||
**공통 선행 조건** — BFF 2 replica. 구현은 `develop-keycloak-pattern3`에
|
||||
이미 있으므로 가져온다.
|
||||
|
||||
단일 노드에서는 관찰할 수 없는 항목이다.
|
||||
```bash
|
||||
git checkout develop-keycloak-pattern3 -- bff/
|
||||
```
|
||||
|
||||
### Redis 애플리케이션 세션 + SSO
|
||||
### 3. BFF 저장소 결정 → [Q3](https://hyeonworks.com/questions/bff-session-authorized-client-store)
|
||||
|
||||
브랜치: `feature/keycloak-redis-app-session-store`
|
||||
|
||||
**확인할 것**
|
||||
**질문의 핵심은 "Redis 도입"이 아니라 "Redis와 JDBC 중 무엇이 맞는가"다.**
|
||||
PostgreSQL이 이미 있으므로 같은 조건에서 비교할 수 있다.
|
||||
|
||||
- B층 도입 — BFF(Spring Session Data Redis), oauth2-proxy(`--session-store-type=redis`)
|
||||
- **Redis가 죽으면** 앱 세션은 전멸하지만 **Keycloak SSO 세션은 살아 있다**
|
||||
→ 재로그인이 무프롬프트로 끝난다. "SSO를 추가하면 뭐가 달라지나"의 답
|
||||
- Redis `appendonly` 유무에 따른 복구 차이
|
||||
- **백채널 로그아웃** — Keycloak에서 로그아웃했을 때 Redis의 앱 세션도 죽이려면
|
||||
`sid → session` 역인덱스가 필요하다. Redis 스키마 설계의 실질적 지점
|
||||
- 앱 두 개(`app1`, `app2`)로 SSO 체감
|
||||
**Session과 Authorized Client는 조회 키가 다르다.**
|
||||
|
||||
### 장애 주입과 복구
|
||||
| 상태 | 조회 키 |
|
||||
|---|---|
|
||||
| Application Session | **session ID** |
|
||||
| OAuth2AuthorizedClient | **registration 이름 + principal name** |
|
||||
|
||||
`session ID`가 없으므로 **같은 사용자의 여러 브라우저가 동일한 authorized
|
||||
client를 공유**한다. 따라서 **두 저장소를 각각 설계해야 한다.**
|
||||
|
||||
검증 5단계 — 로그인 유지·재시작 복구 / **refresh token 평문 여부** /
|
||||
**session TTL ≠ token 만료** / logout 후 잔여 항목 / 저장소 끊김 시 오류.
|
||||
|
||||
### 4. 다중 인스턴스 운영 → [Q1](https://hyeonworks.com/questions/server-session-pattern-multi-instance)
|
||||
|
||||
브랜치: `feature/keycloak-multi-instance-session-operation` **(생성 필요)**
|
||||
|
||||
검증 5단계 — 다른 인스턴스로 요청 시 200 유지 / 재시작 후 session cookie /
|
||||
**authorized client 덮어쓰기** / logout 전파 / 만료 어긋남.
|
||||
|
||||
**3번(덮어쓰기)이 특히 중요하다.** "Redis만 붙이면 해결"이라는 착각을 깨는
|
||||
항목이다.
|
||||
|
||||
**질문의 제약 하나는 이미 해결법을 안다** — "Resource Server의 8081이 host에도
|
||||
열려 있어 BFF만 거치도록 강제되지 않았다"는 2홉 실험의 **프록시 우회 경로와
|
||||
같은 문제**이며, NetworkPolicy 패턴을 그대로 재사용한다.
|
||||
|
||||
호스트 nginx의 `ip_hash` 주석을 켜고 끄면 **스티키 유무 비교**까지 같은
|
||||
구성에서 된다.
|
||||
|
||||
### 5. Refresh Token 경쟁 → [Q2](https://hyeonworks.com/questions/refresh-rotation-replica-contention)
|
||||
|
||||
브랜치: `feature/keycloak-refresh-token-concurrency`
|
||||
|
||||
**★ 3번 이후여야 한다.** 저장소가 process-local이면 두 replica가 같은 refresh
|
||||
token 항목을 보지 않아 **경쟁 자체가 재현되지 않는다.**
|
||||
|
||||
검증 5단계 — 만료 직후 동시 요청 / 이긴 쪽·지는 쪽 응답 / **지는 쪽이 새
|
||||
token으로 재시도해 성공하는가** / **지는 쪽 사용자 화면** /
|
||||
**lock 유무를 같은 입력으로 비교**.
|
||||
|
||||
**마지막이 결론 기준이다** — *실패가 사용자에게 노출되면 lock, 노출되지 않으면
|
||||
재시도.*
|
||||
|
||||
**제약** — rotation + 재사용 0회는 전제로 고정한다. 그리고 이미 발급된 access
|
||||
token은 만료 전까지 통하므로 **재현은 access token 만료 직후에 맞춰 실행**한다.
|
||||
|
||||
### 6. Edge 인가 범위 → [Q4](https://hyeonworks.com/questions/edge-authorization-scope)
|
||||
|
||||
브랜치: `feature/keycloak-edge-authorization-scope` **(생성 필요)**
|
||||
|
||||
**확인할 것** — role을 헤더에 담고 **다중 값 구분자·escaping** / **헤더 크기
|
||||
상한** 초과 시 자르는가 거부하는가 / role 변경이 **몇 번째 요청부터 반영**되는가
|
||||
/ upstream이 헤더 존재만 보는가 값과 service identity까지 보는가.
|
||||
|
||||
2홉 실험에서 확인한 **nginx가 동명 헤더를 merge하지 않고 덮어쓴다**는 동작이
|
||||
`X-Auth-Request-*`에도 적용되는지 같은 방법으로 검증한다.
|
||||
|
||||
질문의 제약 — internal token 검사가 controller 한 곳에만 있어 **공통 경계로
|
||||
옮겨야** 한다. 코드 변경이므로 `backend/`에서 진행한다.
|
||||
|
||||
---
|
||||
|
||||
## 공통 — 운영 역량
|
||||
|
||||
**여기부터는 "구성했다"가 아니라 "운영해봤다"에 필요한 항목이다.**
|
||||
백업과 업그레이드는 빠지면 티가 난다.
|
||||
|
||||
### 7. 장애 주입과 복구
|
||||
|
||||
브랜치: `feature/keycloak-failure-injection-recovery`
|
||||
|
||||
**확인할 것**
|
||||
|
||||
| 주입 | 방법 |
|
||||
|---|---|
|
||||
| 노드 상실 | `virsh destroy` — 프로세스 kill 이 아닌 진짜 상실 |
|
||||
@@ -212,11 +271,70 @@ KC_HTTP_ENABLED=true
|
||||
| Redis 상실 | Redis 파드 정지 |
|
||||
| 지연 주입 | 게스트 안에서 `tc netem` — 커널이 분리돼 있어 안전 |
|
||||
|
||||
**복구 절차**를 각각 기록한다. 이 실험대가 재현할 수 없는 것(실제 AZ 간 RTT,
|
||||
ALB 고유 동작, 규모)은 스코프에서 제외한다.
|
||||
**복구 절차**를 각각 기록한다. 실제 장애의 대부분은 완전 사망이 아니라
|
||||
**부분 장애**(느려짐, 일부 실패)이므로 netem 지연을 기본값으로 둔다.
|
||||
|
||||
### 8. 백업과 복구 리허설
|
||||
|
||||
**"백업이 있다"와 "복구해봤다"는 완전히 다르다.**
|
||||
|
||||
- `pg_dump`로 realm·세션·JGROUPS_PING 포함 전체 덤프
|
||||
- **일부러 파괴** — PVC 삭제 또는 DB 드롭
|
||||
- 덤프에서 복구하고 **로그인이 되는지, 기존 세션이 살아나는지** 확인
|
||||
- 복구에 걸린 시간을 기록한다 (RTO)
|
||||
- 백업 시점 이후 데이터가 무엇을 잃는지 확인한다 (RPO)
|
||||
|
||||
Redis 쪽은 `appendonly` 유무에 따른 차이를 함께 본다.
|
||||
|
||||
### 9. Keycloak 버전 업그레이드
|
||||
|
||||
**운영에서 가장 무서운 작업 중 하나다.** realm 마이그레이션과 **DB 스키마
|
||||
변경이 자동으로 실행**되며, 실패하면 되돌리기 어렵다.
|
||||
|
||||
- 현재 26.7.0 → 다음 마이너로 이미지 태그 변경
|
||||
- **업그레이드 전 백업**을 먼저 확보한다 (8번의 전제)
|
||||
- 롤링 중 **기존 세션이 유지되는가** (2번의 persistent 설정과 연결된다)
|
||||
- 스키마 변경 로그를 확인한다
|
||||
- **롤백이 되는가** — 스키마가 바뀐 뒤에는 이전 버전이 뜨지 않을 수 있다
|
||||
|
||||
### 10. 관측성
|
||||
|
||||
지금은 `kubectl top`뿐이라 **장애 중 무슨 일이 있었는지 사후 추적이 안 된다.**
|
||||
|
||||
- `KC_METRICS_ENABLED=true` + `KC_HEALTH_ENABLED=true`
|
||||
- Prometheus + Grafana 배포
|
||||
- 볼 지표 — Infinispan 캐시 항목 수·축출, DB 커넥션 풀 사용률,
|
||||
로그인 성공/실패율, **클러스터 멤버 수**
|
||||
- 장애 주입(7번) 중에 **어떤 지표가 먼저 움직이는지** 기록한다
|
||||
|
||||
### 11. 비밀 관리
|
||||
|
||||
지금 방식대로면 client secret과 DB 비밀번호가 **매니페스트에 평문**으로 들어간다.
|
||||
|
||||
- k8s `Secret`으로 분리
|
||||
- 저장소에는 `.example`만 커밋 (기존 `.env.example` 관례 그대로)
|
||||
- 평문 Secret은 etcd에 base64로만 저장되므로 실제로는 감춰지지 않는다는 점을
|
||||
확인한다 — `kubectl get secret -o yaml`로 직접 본다
|
||||
- SealedSecret 또는 외부 저장소가 필요한 지점을 판단한다
|
||||
|
||||
### 12. 인증서 갱신 실측
|
||||
|
||||
90일을 기다리지 않고 강제로 겪는다.
|
||||
|
||||
```bash
|
||||
sudo certbot renew --force-renewal
|
||||
```
|
||||
|
||||
- nginx reload 타이밍에 **무중단인가**
|
||||
- 갱신 중 진행 중이던 요청은 어떻게 되는가
|
||||
- `certbot-renew.timer`가 실제로 동작하는가 (`--dry-run`이 아니라 실제 갱신)
|
||||
|
||||
---
|
||||
|
||||
## 스코프에서 제외한 것
|
||||
|
||||
### 이 실험대가 재현하지 못하는 것
|
||||
|
||||
| 항목 | 이유 |
|
||||
|---|---|
|
||||
| 성능·처리량 측정 | 단일 물리 머신의 숫자는 운영에 대해 아무것도 말해주지 않는다 |
|
||||
@@ -227,10 +345,40 @@ ALB 고유 동작, 규모)은 스코프에서 제외한다.
|
||||
|
||||
**이 실험대가 검증하는 것은 계약(정합성)이지 성능이 아니다.**
|
||||
|
||||
### 구조적으로 줄 수 없는 경험
|
||||
|
||||
실험 설계로는 만들 수 없는 것들. **무엇을 겪지 않았는지 아는 것도 기록의
|
||||
일부다.**
|
||||
|
||||
| 없는 것 | 왜 |
|
||||
|---|---|
|
||||
| **규모** | 수천 세션에서의 커넥션 풀 고갈, Infinispan 캐시 축출 |
|
||||
| **시간** | 몇 달 돌면서 드러나는 디스크 참, 로그 누적, 메모리 누수 |
|
||||
| **다른 사람** | 동시에 만지는 사람, 온콜, 인수인계, "내가 안 바꿨는데 바뀌어 있음" |
|
||||
| **실제 사용자** | 봇, 오래된 클라이언트, 예측 못 한 사용 패턴 |
|
||||
| **클라우드 관리형 컴포넌트** | ALB·RDS·ElastiCache의 고유 동작과 **그것들의 장애 모드** |
|
||||
| **비용** | 운영 판단의 큰 축인데 실험대엔 없다 |
|
||||
| **보안 사고 대응** | 실제 침해, 토큰 유출 후 회수, 감사 로그 추적 |
|
||||
|
||||
**"시간"은 부분적으로 살 수 있다.** 실험이 끝나도 클러스터를 지우지 않고
|
||||
몇 주 켜둔 채로 두면 인증서가 갱신되고, 로그가 쌓이고, 예상 못 한 것이
|
||||
죽는다. 실험 설계로는 만들 수 없는 종류의 관찰이다.
|
||||
|
||||
### 따라서 말할 수 있는 것과 없는 것
|
||||
|
||||
**말할 수 있다** — Keycloak 멀티노드에서 세션과 토큰이 어디에 저장되고 각
|
||||
저장소가 죽으면 무엇이 어떻게 실패하는지 재현하고 복구했다. 프록시 체인의
|
||||
헤더 계약을 측정으로 확정했고, 신뢰 경계의 구멍을 실증하고 막았다.
|
||||
|
||||
**말하면 안 된다** — "운영해봤다", "대규모 트래픽을 다뤄봤다"
|
||||
|
||||
**그 경계를 정확히 구분해 말하는 것 자체가 이 기록의 목적이다.**
|
||||
|
||||
## 관련 문서
|
||||
|
||||
| 문서 | 내용 |
|
||||
|---|---|
|
||||
| [`open-questions-coverage.md`](open-questions-coverage.md) | 공개 열린 질문 4개와의 대조, 순서 근거 |
|
||||
| [`session-lab-concepts.md`](session-lab-concepts.md) | 등장 개념 전체 (가상화·네트워크·k3s·TLS·패키지) |
|
||||
| [`session-lab-operations.md`](session-lab-operations.md) | 관측 도구 · 자주 쓰는 명령 · 훈련 · 자원 예산 |
|
||||
| [`two-hop-proxy-header-contract.md`](two-hop-proxy-header-contract.md) | 첫 실험의 측정·진단·수정 |
|
||||
|
||||
Reference in New Issue
Block a user