커넥션 드레이닝(connection draining)은 서버를 내리기 전에 새 요청은 더 보내지 않고, 이미 받은 요청은 끝까지 처리하게 기다려 주는 과정이다. 이게 없으면 배포 때마다 짧은 502 구간이 생긴다.
배포 중 502가 나는 원리
흔한 구성은 이렇다.
브라우저 → 로드밸런서(ALB) → 서버마다 nginx → 앱 서버(gunicorn :8000) → Django배포 스크립트가 앱 서버 프로세스를 그냥 재시작하면, 몇 초 동안 :8000에 아무도 없다. 그 사이에도 그 서버는 로드밸런서의 대상 목록에 남아 있으니 요청이 계속 들어오고, nginx는 업스트림 연결에 실패해 자기가 만든 502 Bad Gateway를 돌려준다. 서버가 여러 대이고 한 대씩 배포해도, 재시작 중인 그 한 대로 간 요청은 실패한다.
이 502는 Django가 만든 응답이 아니라서 앱이 붙이던 CORS 헤더가 없다. 그래서 다른 출처에서 호출한 브라우저 콘솔에는 CORS 오류로 보이기 쉽다(CORS).
고치는 순서
1. 로드밸런서 대상 그룹에서 등록 해제 → 드레이닝 시작(새 요청 중단)
2. 진행 중인 요청이 끝날 때까지 대기
3. 앱 서버 재시작
4. 준비 확인(헬스 체크 엔드포인트가 200을 줄 때까지)
5. 다시 등록 → 헬스 체크를 연속 통과하면 트래픽 재개남은 서버가 트래픽을 받아 주므로 사용자는 실패를 보지 않는다. 대상이 한 대뿐인 환경(QA 등)에서는 빼는 순간 받을 곳이 없으니 이 절차를 건너뛰거나 다른 방법을 써야 한다.
AWS ALB의 등록 해제 지연 (2026 기준)
- 대상을 등록 해제하면 ALB는 그 대상으로 새 요청 보내기를 멈추고, 대상 상태가
draining이 된다 - 진행 중인 요청을 위해 등록 해제 지연(deregistration delay)만큼 기다린다. 기본 300초이고 대상 그룹 속성
deregistration_delay.timeout_seconds로 바꾼다. 진행 중인 요청과 연결이 없으면 바로 끝난다 - 지연이 끝나기 전에 대상이 연결을 끊으면 클라이언트는 5xx 오류를 받는다. 그래서 지연 시간은 가장 긴 정상 요청보다 길게, 재시작은 드레이닝이 끝난 뒤에 한다
앱 서버 쪽: graceful shutdown
로드밸런서에서 빼는 것과 별개로, 프로세스도 받은 요청을 마치고 내려가야 한다.
- gunicorn 마스터는
TERM을 받으면 graceful shutdown을 한다. 워커가 처리 중인 요청을graceful_timeout(기본 30초)까지 기다린 뒤 남은 워커를 강제로 끝낸다.INT·QUIT는 바로 끈다 HUP은 설정을 다시 읽고 새 워커를 띄운 뒤 옛 워커를 차례로 내린다. 마스터 프로세스는 내려가지 않고 워커만 교체하므로, 프로세스를 통째로 재시작할 때처럼 포트가 비는 구간이 생기지 않는다. 다만 앱을 미리 로드(preload_app)했다면 코드가 다시 로드되지 않는다USR2는 새 마스터를 띄우는 바이너리 업그레이드다
WSGI와 gunicorn의 역할
WSGI(Web Server Gateway Interface, PEP 3333)는 파이썬 웹 서버와 앱 사이의 호출 규약이다. 앱은 application(environ, start_response) 형태의 호출 가능한 객체만 내놓으면 되고, 소켓·워커 프로세스·타임아웃 관리는 gunicorn 같은 WSGI 서버가 맡는다. Django의 manage.py runserver는 개발용이고, 운영에서는 이 조합을 nginx 뒤에 둔다. 비동기(WebSocket 등)가 필요하면 후속 규약인 ASGI(Asynchronous Server Gateway Interface)와 uvicorn 같은 서버를 쓴다(Django 미들웨어와 로그인 흐름).
쿠버네티스 핵심 개념에서도 같은 문제가 있다. kubelet이 파드 종료를 시작하는 것과 컨트롤 플레인이 그 파드를 서비스의 EndpointSlice에서 빼는 것이 동시에 진행되므로, 프로세스는 신호를 받은 뒤에도 잠깐 요청을 받아 줄 수 있어야 한다. 서버를 여러 대 두는 이유는 수직 확장과 수평 확장, 헬스 체크 엔드포인트는 Spring Boot Actuator를 참고한다.
출처: AWS — ALB target group attributes: Deregistration delay · Gunicorn — Signal Handling · Gunicorn — Settings: graceful_timeout · PEP 3333 — Python Web Server Gateway Interface · Kubernetes — Pod termination