diff --git a/docs/diagrams/d4-renewal-gap.svg b/docs/diagrams/d4-renewal-gap.svg index 1444444..7806145 100644 --- a/docs/diagrams/d4-renewal-gap.svg +++ b/docs/diagrams/d4-renewal-gap.svg @@ -17,7 +17,7 @@ -D-4 — 「갱신 성공」과 「새 인증서 서빙」 사이의 36분 39초 +D-4 — 「갱신 성공」과 「새 인증서 서빙」 사이의 38분 25초 측정: 2026-09-04 · 일련번호 5초 간격 564표본 · 새 연결 0.2초 간격 8856건 @@ -81,7 +81,7 @@ 06C7CB…EA1D -36분 39초 +38분 25초 사람이 reload 했기에 36분이다. 아무도 안 했다면 다음 재시작까지 — 무기한 diff --git a/docs/evidence/d4-certificate-renewal/13-verdict.txt b/docs/evidence/d4-certificate-renewal/13-verdict.txt index 11ec966..13be2f4 100644 --- a/docs/evidence/d4-certificate-renewal/13-verdict.txt +++ b/docs/evidence/d4-certificate-renewal/13-verdict.txt @@ -1,4 +1,5 @@ -D-4 판정 — 갱신은 성공했고, 36분 39초 동안 반영되지 않았다 +D-4 판정 — 갱신은 성공했고, 38분 25초 동안 반영되지 않았다 + (시계 보정 후. 처음 적은 36분 39초는 서로 다른 시계를 뺀 값이었다) ============================================================= 수집: 2026-09-04 · 감시 08:10:51 ~ 09:02 UTC (KST-9) @@ -30,7 +31,8 @@ D-4 판정 — 갱신은 성공했고, 36분 39초 동안 반영되지 않았다 새 인증서 디스크 기록 : 08:22:13 UTC (= 17:22:13 KST, archive mtime) 실제 서빙 시작 : 08:58:52 UTC (= 17:58:52 KST, nginx -s reload) ──────────────────────────────────────────── - 공백 : 2199초 = 36분 39초 + 공백 : 2305초 = 38분 25초 ← test-server 가 106초 빠른 것을 보정한 값 + (보정 전 2199초로 적었던 것을 정정. D-4a 참조) 그리고 이 36분은 **우연히 짧았을 뿐이다.** reload 를 시킨 것은 사람이지 자동화가 아니다. 아무도 안 했다면 다음 nginx 재시작까지 — 즉 무기한 — @@ -86,7 +88,7 @@ D-4 판정 — 갱신은 성공했고, 36분 39초 동안 반영되지 않았다 ════ 세 줄 요약 ════ -1. **"갱신 성공"과 "새 인증서 서빙"은 다른 사건이다.** 36분 39초 동안 +1. **"갱신 성공"과 "새 인증서 서빙"은 다른 사건이다.** 38분 25초 동안 디스크에는 새 인증서가, 네트워크에는 옛 인증서가 있었다. 428번 관측했다. 2. **reload 는 정말 무중단이었다.** 8856건 전부 200, p95 변화 없음, diff --git a/docs/evidence/d4-certificate-renewal/README.md b/docs/evidence/d4-certificate-renewal/README.md index f882237..092c766 100644 --- a/docs/evidence/d4-certificate-renewal/README.md +++ b/docs/evidence/d4-certificate-renewal/README.md @@ -17,11 +17,11 @@ | `10-reload-poll-window.txt` | reload 전후 60초 새 연결 원문 — **비200 0건**, 최대 373ms | | `11-inflight-full.txt` | in-flight 전체 50건. **08:58:40 시작 요청이 08:58:52 reload 를 관통**해 845361바이트 전량 수신 | | `12-certbot-state.txt` | `cert2.pem` **09-04 17:22:13 기록됨** · `renewal-hooks/{deploy,post,pre}/` **셋 다 비었음** · 플러그인 목록에 nginx 없음 | -| `13-verdict.txt` | **판정 전문** — 36분 39초 공백(428회 관측) + reload 무중단(8856건 0실패) | +| `13-verdict.txt` | **판정 전문** — 38분 25초 공백(428회 관측) + reload 무중단(8856건 0실패) | ## 핵심 다섯 줄 -1. **「갱신 성공」과 「새 인증서 서빙」은 다른 사건이다.** 새 인증서가 디스크에 있는 채로 **36분 39초** 동안 옛 인증서를 서빙했고, 그 구간에서 **428번** 관측했다. +1. **「갱신 성공」과 「새 인증서 서빙」은 다른 사건이다.** 새 인증서가 디스크에 있는 채로 **38분 25초** 동안 옛 인증서를 서빙했고, 그 구간에서 **428번** 관측했다. 2. **그 36분은 우연히 짧았다.** reload 를 시킨 것은 사람이다. 아무도 안 했다면 다음 nginx 재시작까지 무기한이었다. 3. **원인이 셋 겹쳤다.** 유닛에 `ExecStartPost` 없음 · 훅 디렉터리 3개 전부 비었음 · certbot 에 nginx 플러그인 없음. 하나라도 있었으면 자동 반영됐다. 4. **reload 자체는 무중단이었다.** 새 연결 8856건 전부 200, p95 205.7 → 204.3ms, 그리고 **전송 12초째에 reload 를 맞은 42초 요청이 845361바이트를 온전히** 받았다(연결수 1). diff --git a/docs/experiment-d4-certificate-renewal.md b/docs/experiment-d4-certificate-renewal.md index 528f46e..44ca4d0 100644 --- a/docs/experiment-d4-certificate-renewal.md +++ b/docs/experiment-d4-certificate-renewal.md @@ -10,7 +10,7 @@ ![D-4 구조 — 체인과 SAN 제약](diagrams/d4-cert-chain.svg) -**핵심 발견** — 갱신은 성공했는데 36분 39초 동안 반영되지 않았다. +**핵심 발견** — 갱신은 성공했는데 **38분 25초** 동안 반영되지 않았다. ![D-4 — 갱신과 서빙 사이의 공백](diagrams/d4-renewal-gap.svg) @@ -26,7 +26,7 @@ | 인증서 구성 | **SAN 3개** (`auth`/`app1`/`app2`), 와일드카드 아님 | | 체인 완결성 | **정상.** `Verify return code: 0 (ok)`, 4단계 | | 갱신 자동화 | 타이머는 **동작한다.** 오늘 두 번 `status=0/SUCCESS` | -| **★ 갱신 반영** | **안 된다.** 새 인증서가 디스크에 있는 채로 **36분 39초** 동안 옛 인증서를 서빙했다 | +| **★ 갱신 반영** | **안 된다.** 새 인증서가 디스크에 있는 채로 **38분 25초** 동안 옛 인증서를 서빙했다 | | **★ reload 무중단** | **무중단이다.** 8856건 전부 200 · 전송 한가운데였던 42초 요청이 845361바이트 전량 수신 | ### 한 문장으로 @@ -161,7 +161,15 @@ reload 순간에 실제로 전송 중인 요청이 있어야 한다. 845KB 짜 |---|---| | 새 인증서 디스크 기록 | 08:22:13 | | 실제 서빙 시작 (`nginx -s reload`) | 08:58:52 | -| **공백** | **2199초 = 36분 39초** | +| **공백** | **2305초 = 38분 25초** (시계 보정 후 — 아래 참조) | + +> **★ 시계 보정 (D-4a 에서 발견)** — 처음 적은 **2199초(36분 39초)** 는 +> `archive/` 파일 시각(test-server 시계)과 일련번호 관측(dev 시계)을 **그대로 뺀** +> 값이었다. 외부 기준(Google · Let's Encrypt `Date` 헤더)으로 재보니 +> **dev 가 정확하고 test-server 가 106초 빠르다**(3회 측정 전부 +106.1초, +> `NTPSynchronized=no`). 보정하면 **2305초 = 38분 25초**다. +> 보정값은 D-4a 새 인증서의 SCT(`Sep 4 12:27:49.054 GMT`, CT 로그가 서명한 +> 독립 시계)가 보정한 훅 시각의 1초 앞에 놓이는 것으로 교차 검증된다. 그 사이 **428번** 옛 인증서로 관측됐다. 그리고 이 36분은 **우연히 짧았을 뿐이다** — reload 를 시킨 것은 사람이지 자동화가 아니다. 아무도 안 했다면 @@ -280,7 +288,7 @@ in-flight 전체 50건 중 종료코드 ≠ 0 은 **0건**이다. | [`10-reload-poll-window.txt`](evidence/d4-certificate-renewal/10-reload-poll-window.txt) | 터미널 | reload 전후 60초 새 연결 원문 (비200 0건) | | [`11-inflight-full.txt`](evidence/d4-certificate-renewal/11-inflight-full.txt) | 터미널 | in-flight 전체 — reload 를 관통한 08:58:40 요청 포함 | | [`12-certbot-state.txt`](evidence/d4-certificate-renewal/12-certbot-state.txt) | 터미널 | `certbot certificates` · `archive/` mtime · **훅 3개 디렉터리 전부 비었음** | -| [`13-verdict.txt`](evidence/d4-certificate-renewal/13-verdict.txt) | 터미널 | **판정** — 36분 39초 공백 + reload 무중단, 수치 전량 | +| [`13-verdict.txt`](evidence/d4-certificate-renewal/13-verdict.txt) | 터미널 | **판정** — 38분 25초 공백 + reload 무중단, 수치 전량 | 파일별 상세는 [`evidence/d4-certificate-renewal/README.md`](evidence/d4-certificate-renewal/README.md). diff --git a/docs/experiment-followup-untested-items.md b/docs/experiment-followup-untested-items.md index 8134918..bdc5555 100644 --- a/docs/experiment-followup-untested-items.md +++ b/docs/experiment-followup-untested-items.md @@ -18,7 +18,7 @@ | **그리고 D-2 의 결론이 정밀해졌다** | **"롤백 불가" 는 조건부다** — 스키마가 바뀌었을 때만 | | **B-4 ③ role 변경 반영 시점** | **요청 횟수와 무관하다.** 세션이 새로 만들어져야 한다 | | **B층에 Grafana 증거가 없는 이유** | **관측 대상에 없다.** 안 찍은 것이 아니다 | -| **D-4 강제 갱신** | **★ 갱신은 성공했는데 36분 39초 동안 반영되지 않았다** | +| **D-4 강제 갱신** | **★ 갱신은 성공했는데 38분 25초 동안 반영되지 않았다** | | **D-4 reload 무중단** | **무중단이다.** 8856건 전부 200 · 전송 한가운데였던 요청이 전량 수신 | --- @@ -303,7 +303,7 @@ nginx 설정을 읽으려던 시도가 계속 빈 결과였고, 그게 sudo 의 |---|---| | 새 인증서 디스크 기록 | 08:22:13 | | 실제 서빙 시작 (`nginx -s reload`) | 08:58:52 | -| **공백** | **2199초 = 36분 39초** (그 사이 428회 관측) | +| **공백** | **2305초 = 38분 25초** (그 사이 428회 관측 · 시계 보정 후) | 그리고 그 36분은 **우연히 짧았을 뿐이다** — reload 를 시킨 것은 사람이지 자동화가 아니다. 원인이 셋 겹쳤고 **전부 비어 있었다.** diff --git a/docs/experiment-index.md b/docs/experiment-index.md index d01da72..950a068 100644 --- a/docs/experiment-index.md +++ b/docs/experiment-index.md @@ -26,10 +26,10 @@ | **D-1** | 백업·복구 | `...d1-backup-restore` | **빈 데이터베이스가 `200` 을 냈다** | | **D-2** | 버전 업그레이드 | `...d2-version-upgrade` | **이미지를 되돌려도 스키마는 안 돌아온다** | | **D-3** | 비밀 관리 | `...d3-secret-management` | **RBAC 만 실제로 감춘다** | -| **D-4** | 인증서 갱신 | `...d4-certificate-renewal` | **갱신은 됐는데 36분 39초 반영 안 됨** (훅 3경로 전부 비었음). reload 자체는 **무중단**(8856건 0실패) | +| **D-4** | 인증서 갱신 | `...d4-certificate-renewal` | **갱신은 됐는데 38분 25초 반영 안 됨** (훅 3경로 전부 비었음). reload 자체는 **무중단**(8856건 0실패) | | **A-7a** | volatile refresh 500 원인 확정 | `...a7a-volatile-cause` | **가설(`REVOKED_TOKEN`)은 틀렸다** — `CLIENT_SCOPE_CLIENT` 조회다. **A-7 의 표는 캐시 온도에 따라 400/500/200** | | **B-7a** | 고아 세션 정리 | `...b7a-orphan-session` | **지울 수 있다** — 프록시가 못 할 뿐. TTL 역산으로 고아만 **1초 오차**로 골라낸다 | -| **후속** | 미측정 3항목 채우기 | `...followup-untested-items` | **셋 다 완료.** 정방향 업그레이드 무중단 · **롤백 불가는 조건부였다** · role 변경은 요청으로 반영 안 됨 · **D-4 갱신 36분 39초 미반영** | +| **후속** | 미측정 3항목 채우기 | `...followup-untested-items` | **셋 다 완료.** 정방향 업그레이드 무중단 · **롤백 불가는 조건부였다** · role 변경은 요청으로 반영 안 됨 · **D-4 갱신 38분 25초 미반영** | ## 시각 자료