떨어지는 연결 없이 설정을 바꾸는 절차.
$ nginx -s reload (= kill -HUP <master pid>)
- ① 마스터가 새 설정을 읽고 검증한다
- 문법이 틀리면 여기서 실패하고 '아무것도 바꾸지 않는다' ← 원자적
- ② 검증 성공 → 새 설정으로 새 워커를 띄운다
- ③ 기존 워커에게 "새 연결은 그만 받아라" 를 알린다
- ④ 기존 워커는 처리 중인 요청을 끝까지 마치고 스스로 종료한다
배포 전에는 항상 문법 검사
nginx -t # 문법 검사
nginx -T # include 를 전부 펼친 '최종' 설정을 출력
-t 는 '문법' 만 본다 upstream 이 실제로 살아 있는지, 인증서 파일이 읽히는지는 리로드 시점에 드러난다
실무에서는 -T 가 더 유용하다
- include 로 흩어진 파일이 어떻게 합쳐졌는지 보여 준다
- "어디서 이 지시자가 들어왔지" 를 여기서 찾는다
- 배포 전후 diff 를 뜨면 변경이 정확히 무엇인지 드러난다
시그널
- SIGHUP — 설정 리로드 (위 절차)
- SIGQUIT — graceful shutdown — 처리 중인 요청을 끝내고 종료
- SIGTERM — fast shutdown — 즉시 종료. 진행 중 요청이 끊긴다
- SIGUSR1 — 로그 파일 다시 열기 (로그 회전 후)
- SIGUSR2 — 실행 파일 교체 — 바이너리 업그레이드를 무중단으로
컨테이너에서 요청이 잘리는 이유
컨테이너의 기본 정지 시그널은 SIGTERM 이다
- nginx 에게 SIGTERM 은 '즉시 종료' 다
- 배포마다 진행 중 요청이 잘린다 (5xx)
대응
- STOPSIGNAL SIGQUIT — Dockerfile 에 명시 또는 종료 훅에서 nginx -s quit 를 보낸다
같은 문제가 WAS 에도 있다 — 정지 시그널을 어떻게 받느냐가 무중단 배포의 절반이다
리로드로 못 바꾸는 것
-
worker_processes 를 줄이는 것은 반영되지만, 늘리는 것도 리로드로 된다
-
listen 포트 변경은 리로드로 반영된다 (마스터가 bind 를 다시 한다)
-
바이너리 자체(업그레이드·모듈 추가)는 SIGUSR2 경로가 필요하다
-
일부 전역 설정(user 등)은 재시작이 필요하다
-
애매하면 -T 로 최종 설정을 확인하고, 그래도 안 바뀌면 재시작을 고려한다
리로드가 잦으면 워커가 쌓인다
기존 워커는 '처리 중인 요청' 이 끝나야 종료한다
WebSocket·SSE 처럼 오래 붙어 있는 연결이 있으면 그 워커가 몇 시간씩 남는다
$ ps -ef | grep nginx nginx: worker process is shutting down ← 이런 것이 여러 개 쌓인다
메모리를 그만큼 더 쓴다 worker_shutdown_timeout 을 두면 강제로 끊는다
- worker_shutdown_timeout 30s;
- 롱 커넥션 서비스에서는 필수에 가깝다
배포 파이프라인에 넣는 형태
set -euo pipefail
nginx -t # 문법 검증 (실패 시 여기서 중단)
diff <(nginx -T) /tmp/prev-config || true # 무엇이 바뀌는지 기록
nginx -s reload
sleep 3
curl -fsS http://localhost/healthz > /dev/null # 리로드 후 살아 있는지
- -t 는 문법만 본다 — 인증서 파일 권한, upstream DNS 해석은 리로드 시점에 드러난다
- 리로드 직후 헬스체크 한 번이 그 공백을 메운다
면접 함정
- ❌ "리로드하면 잠깐 끊긴다" → 기존 워커가 마무리하고 빠지므로 끊기지 않는다.
- ❌ "설정이 틀리면 서비스가 죽는다" → 검증 실패 시 아무것도 바뀌지 않는다. 그래서 안전하다.