The 2199 seconds reported for D-4 subtracted a test-server timestamp (archive/cert2.pem mtime) from a dev-machine timestamp (the serial change observed by the poll), without noting they are different clocks. Checked against external references: the dev machine matches Google and the Let's Encrypt ACME endpoint to the second, while test-server is 105 seconds fast and reports NTPSynchronized=no. Three round-trip measurements put the offset at +106.1s every time. Corrected: new certificate written to disk 08:20:27 UTC (mtime 17:22:13 KST - 106s) actually served 08:58:52 UTC (dev observation, no correction) gap 2305s = 38m25s The correction validates itself in D-4a, where the new certificate's SCT — signed by CT logs on their own accurate clock at 12:27:49.054 GMT — lands one second before the skew-corrected hook time. Without the correction the hook would appear to have run 104 seconds before the certificate existed. Updated across the experiment doc, the index, the follow-up doc, the verdict evidence file and the SVG. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
102 lines
5.5 KiB
Plaintext
102 lines
5.5 KiB
Plaintext
D-4 판정 — 갱신은 성공했고, 38분 25초 동안 반영되지 않았다
|
|
(시계 보정 후. 처음 적은 36분 39초는 서로 다른 시계를 뺀 값이었다)
|
|
=============================================================
|
|
수집: 2026-09-04 · 감시 08:10:51 ~ 09:02 UTC (KST-9)
|
|
|
|
════ 답 1 — "갱신 실패"가 아니라 "갱신됐는데 안 집었다" ════
|
|
|
|
07-renewal-hook-missing.txt 에서 두 갈래로 남겼던 것이 갈렸다.
|
|
(a) 강제 갱신이 실패했다
|
|
(b) 성공했는데 훅이 없어 reload 가 안 됐다
|
|
→ ★ (b) 였다.
|
|
|
|
디스크 (12-certbot-state.txt)
|
|
/etc/letsencrypt/archive/auth.hyeonworks.com/
|
|
cert1.pem 2026-09-03 10:47:40 KST ← 처음 발급
|
|
cert2.pem 2026-09-04 17:22:13 KST ← ★ 강제 갱신, 새로 써졌다
|
|
fullchain2.pem 2026-09-04 17:22:13 KST
|
|
privkey2.pem 2026-09-04 17:22:13 KST
|
|
|
|
Certificate Name: auth.hyeonworks.com
|
|
Serial Number: 6c7cb6df1da8a6d7995d93c264bb9ecea1d
|
|
Expiry Date: 2026-12-03 07:21:52+00:00 (VALID: 89 days)
|
|
|
|
밖에서 본 것 (09-serial-timeline.txt, 5초 간격 564표본)
|
|
08:10:51 ~ 08:58:47 serial=0520BB...B853 notAfter=Dec 2 ← 옛 것
|
|
08:58:52 serial=06C7CB...EA1D notAfter=Dec 3 ← 바뀐 순간
|
|
|
|
08:22:13 ~ 08:58:52 구간에서 옛 인증서로 관측된 횟수: 428회
|
|
|
|
시간 계산
|
|
새 인증서 디스크 기록 : 08:22:13 UTC (= 17:22:13 KST, archive mtime)
|
|
실제 서빙 시작 : 08:58:52 UTC (= 17:58:52 KST, nginx -s reload)
|
|
────────────────────────────────────────────
|
|
공백 : 2305초 = 38분 25초 ← test-server 가 106초 빠른 것을 보정한 값
|
|
(보정 전 2199초로 적었던 것을 정정. D-4a 참조)
|
|
|
|
그리고 이 36분은 **우연히 짧았을 뿐이다.** reload 를 시킨 것은 사람이지
|
|
자동화가 아니다. 아무도 안 했다면 다음 nginx 재시작까지 — 즉 무기한 —
|
|
옛 인증서를 계속 서빙했을 것이다.
|
|
|
|
왜 아무도 reload 하지 않았나 — 원인 세 가지가 겹쳤다
|
|
1. certbot-renew.service 에 ExecStartPost 가 없다 (07 참조)
|
|
2. /etc/letsencrypt/renewal-hooks/{deploy,post,pre}/ 가 전부 비어 있다
|
|
deploy/ total 8 (. 과 .. 뿐)
|
|
post/ total 8
|
|
pre/ total 8
|
|
3. certbot 에 nginx 플러그인이 없다 — 그래서 certbot 이 스스로 못 고친다
|
|
Discovered plugins: dns-cloudflare, manual, null, standalone, webroot
|
|
(certbot 5.7.0)
|
|
|
|
★ 세 경로 전부가 비어 있다. 하나라도 있었으면 자동으로 반영됐다.
|
|
|
|
왜 이게 88일 동안 안 보이나
|
|
타이머는 정상이고 오늘도 두 번 status=0/SUCCESS 로 끝났다.
|
|
만료 30일 전(= 약 58일 뒤)까지는 certbot 이 갱신 자체를 하지 않으므로
|
|
"반영되지 않는다"는 결함이 발현할 기회가 없다. 발현하는 날 증상은
|
|
**인증서 만료** 이고, 그때 로그는 여전히 SUCCESS 라고 적혀 있을 것이다.
|
|
|
|
════ 답 2 — reload 는 무중단이다 (측정됨) ════
|
|
|
|
계획서의 질문: "nginx reload 중 진행 중이던 요청은 어떻게 되는가"
|
|
|
|
[새 연결] 0.2초 폴링, 08:10:51 ~ 09:02
|
|
전체 표본 8856건 / 비200 **0건**
|
|
|
|
응답시간 n 중앙 p95 최대
|
|
─────────────────────────────────────────────────────────
|
|
장기 평시 08:20~08:50 5398 98.0ms 205.7ms 1942.9ms
|
|
reload 직전 2분56초 489 116.0ms 200.8ms 387.7ms
|
|
reload 직후 2분08초 342 132.5ms 204.3ms 475.0ms
|
|
|
|
p95 가 205.7 → 204.3 으로 사실상 동일하고, 최대값은 오히려 낮다.
|
|
10초 구간 중앙값은 reload 전후 모두 80~190ms 사이를 오간다(WiFi 잡음).
|
|
→ **reload 로 인한 지연 증가는 관측되지 않았다.**
|
|
|
|
[진행 중이던 요청] 845KB 를 20k/s 로 받아 42초간 살려 둔 요청
|
|
08:58:40 시작 · 소요 40.84초 → 08:59:21 종료
|
|
reload 는 08:58:52, 즉 **요청 시작 12초 뒤 전송 한가운데**였다
|
|
|
|
코드=200 바이트=845361(전량) 연결수=1 curl종료=0
|
|
|
|
· 바이트가 전량이다 → 잘리지 않았다
|
|
· 연결수가 1이다 → 중간에 끊겨 재연결한 것이 아니다
|
|
· 옛 인증서로 시작한 연결이 새 워커 전환을 관통해 끝까지 갔다
|
|
|
|
in-flight 전체 50건 중 종료코드≠0 은 0건.
|
|
(앞서 76건 실패는 로컬 아티팩트였다 — 08-inflight-artifact.txt)
|
|
|
|
════ 세 줄 요약 ════
|
|
|
|
1. **"갱신 성공"과 "새 인증서 서빙"은 다른 사건이다.** 38분 25초 동안
|
|
디스크에는 새 인증서가, 네트워크에는 옛 인증서가 있었다. 428번 관측했다.
|
|
|
|
2. **reload 는 정말 무중단이었다.** 8856건 전부 200, p95 변화 없음,
|
|
전송 한가운데였던 42초짜리 요청이 845361바이트를 온전히 받았다.
|
|
D-4 문서가 "확인하지 않았으므로 그렇게 쓰면 안 된다"고 유보했던 명제가
|
|
이제 측정으로 뒷받침된다.
|
|
|
|
3. **위험한 쪽은 reload 가 아니라 reload 를 부르는 사람이 없다는 것이다.**
|
|
훅 디렉터리 3개가 전부 비었고, 유닛에 ExecStartPost 가 없고,
|
|
nginx 플러그인도 없다. 그리고 그 상태로 타이머는 SUCCESS 를 찍는다.
|