--- kind: PROJECT_DECISION slug: put-the-reload-in-a-deploy-hook title: reload 를 사람이 아니라 deploy 훅이 부르게 한다 topic: operations-that-report-success topicName: 운영 절차의 완료 판정 — 백업 · 판올림 · Secret · 인증서 갱신 project: keycloak-session-store status: 게시 전 decisionStatus: ADOPTED source: - final/document.md#선택이-코드와-흐름에-반영되는-방식-d4a sourceRevision: cdac9b8178391311d8eca1ebc6cac15bb62d79af evidence: - ../../../final/evidence/raw/d4-certificate-renewal__13-verdict.txt - ../../../final/evidence/raw/d4a-deploy-hook__01-hook-verified.txt - ../../../final/evidence/raw/d4a-deploy-hook__02-certbot-with-hook.txt - ../../../final/evidence/raw/d4a-deploy-hook__03-after-state.txt --- # reload 를 사람이 아니라 deploy 훅이 부르게 한다 인증서 갱신 뒤의 nginx reload 를 사람이 아니라 certbot deploy 훅이 부르게 정했다. 사람이 치던 절차로 두었을 때 갱신에서 서빙까지 2305초가 비었고, 훅을 넣은 뒤에는 1~2초였다. ## 근거 - **새 인증서가 디스크에 있고 38분 25초 동안 옛 인증서가 나갔다** 사람이 치는 절차로 뒀을 때의 공백 2305초를 잰 기록이다. 이 결정이 고르지 않은 쪽을 실제로 재 본 값이 거기 있다. - **deploy 훅 하나가 그 공백을 1~2초로 줄였다** 이 결정대로 훅을 설치하고 갱신에서 서빙까지를 다시 잰 기록이다. - **적용됐는지는 로그 문구가 아니라 상태로 판정한다** 훅이 실제로 reload 를 걸었는지를 무엇으로 가릴지, 이 결정이 따르는 기준을 적은 기록이다. - **갱신 타이머가 실제 갱신에서도 도는가** 이 결정을 강제 갱신에서만 확인했다는 것을 열린 질문으로 남긴 기록이다. ## 결정문 인증서를 갱신한 뒤의 nginx reload 는 /etc/letsencrypt/renewal-hooks/deploy/ 에 설치한 훅 파일이 부른다. 사람이 손으로 reload 를 치는 절차로 두지 않는다. 훅의 내용은 두 줄이고 nginx -t && nginx -s reload 다. ## 판단 이유 nginx 는 인증서를 기동 시점에 읽어 메모리에 들고 있는데, certbot 은 인증서 파일의 경로가 아니라 live/ 심볼릭 링크가 가리키는 대상을 갈아끼운다. 그래서 갱신이 끝나도 nginx 설정은 멀쩡해 보이고 옛 인증서가 계속 나간다. 고칠 것은 설정이 아니라 reload 를 부르는 경로인데, 이 실험대에서는 그 경로가 셋 다 비어 있었다. certbot-renew.service 에 ExecStartPost 가 없었고 certbot 에 nginx 플러그인도 설치돼 있지 않았다. renewal-hooks 아래 pre 와 deploy 와 post 세 디렉터리도 모두 비어 있었다. 사람이 치는 절차로 두는 대안은 반사실이 아니라 D-4 에서 실제로 관측했다. 새 인증서가 디스크에 기록되고 서빙이 바뀌기까지 2305초, 38분 25초가 비었고 그 reload 를 부른 것은 자동화가 아니라 사람이었다. 그 38분은 우연히 짧았을 뿐이고, 아무도 치지 않았다면 다음 nginx 재시작까지 옛 인증서가 나갔을 것이다. 훅을 놓을 디렉터리는 셋 중 하나였다. pre 는 갱신을 시도하기 전에 돌아서 새 인증서가 나오기 전이고, post 는 갱신 여부와 무관하게 매번 돈다. 타이머가 하루 두 번 도니 post 에 넣으면 갱신이 없는 날에도 nginx 를 하루 두 번 reload 하게 된다. deploy 는 certbot 이 RENEWED_LINEAGE 를 넘겨줄 때, 즉 실제로 갱신했을 때만 돈다. 두 줄 중 앞의 nginx -t 도 같은 종류의 안전장치다. 설정이 깨진 상태에서 nginx -s reload 를 보내면 마스터가 새 워커를 못 띄우는데, -t 로 먼저 검사해 통과할 때만 reload 하면 실패했을 때 옛 워커가 그대로 서비스를 계속한다. 인증서는 안 바뀌지만 서비스는 죽지 않는다. 이 순서 하나가 「인증서가 안 바뀐다」와 「사이트가 내려간다」를 가른다. 훅을 설치하고 강제 갱신을 다시 돌리자 갱신에서 서빙까지가 1~2초로 줄었다. 워커 PID(process ID, 프로세스 번호)는 사람이 걸었을 때의 28829 에서 37252 로 바뀌었다. ## 영향 - 갱신에서 서빙까지는 훅이 없던 D-4 에서 2305초, 훅을 넣은 D-4a 에서 1~2초였다. 두 값은 회차마다 한 번씩 잰 것이라 같은 조건에서 되풀이해 잰 값이 아니다. - 이 훅은 인증서를 세우는 구축 절차에 들어간다. 원 가이드가 「이 훅은 구축 절차에 들어가야 한다. 사후에 붙이는 것이 아니다」라고 적었다. D-4 가 잰 2305초의 공백은 훅이 없어서 생긴 것이라, 그 훅은 인증서를 처음 세울 때 같이 놓였어야 했다. - 되돌리기는 훅 파일을 지우는 한 줄이지만 지우지 않는다. 훅은 결함을 고치는 파일이라 지우면 D-4 의 상태로 돌아가고, 그 결함은 다음 실제 갱신(약 59일 뒤)에, 증상은 그 뒤 인증서 만료로 나타난다. - certbot 은 훅이 성공해도 Hook 'deploy-hook' ran with error output 이라고 찍는다. 훅의 실제 출력은 test is successful 과 signal process started 이고, 그 문구가 붙은 까닭은 nginx 의 types_hash 경고가 stderr 로 나갔기 때문이다. certbot 은 stderr 에 무엇이든 있으면 이 문구를 붙이고, 그 경고 자체는 types_hash_max_size 기본값에서 오는 것이라 갱신과 무관하다. 로그에서 error 를 grep 하는 감시를 걸면 성공한 훅을 실패로 오독한다. - 확인은 --force-renewal 로 한 강제 갱신에서만 했다. certbot-renew.timer 가 스스로 갱신하는 경로에서도 같은 훅이 도는지는 아직 재지 않았다. - 훅이 거는 reload 가 진행 중이던 요청을 끊지 않는지는 재지 않았다. 무중단을 확인한 측정은 D-4 에서 사람이 손으로 건 reload 를 잰 값이다.