이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- Rolling 배포 중 readiness, deregistration, draining, graceful shutdown 순서를 어떻게 맞추는가?
- 배포 직후 짧은 502/503/connection reset이 왜 생기는가?
- canary/blue-green 배포에서 네트워크 계층의 성공 기준을 어떻게 잡는가?
개요
배포는 새 프로세스를 띄우는 일이 아니라 트래픽 소유권을 안전하게 옮기는 일이다. Load Balancer는 target 등록/해제와 connection draining으로 이 전환을 돕지만, 앱이 readiness와 graceful shutdown을 제대로 제공하지 않으면 배포 중 짧은 장애가 생긴다.
- 새 target은 준비되기 전 traffic을 받으면 안 된다.
- 제거되는 target은 새 traffic을 받지 않되 기존 요청을 마칠 시간이 필요하다.
- app shutdown timeout은 LB draining 시간과 맞아야 한다.
- long request, upload, WebSocket/SSE는 draining 시간을 더 길게 요구한다.
원리
안전한 종료 순서는 다음에 가깝다.
1. SIGTERM received
2. readiness becomes false
3. LB stops sending new requests
4. in-flight requests finish
5. server stops accepting connections
6. process exits이 순서가 어긋나면 SIGTERM 이후에도 새 요청을 받거나, LB가 아직 target으로 보내는데 프로세스가 먼저 죽어 502/connection reset이 난다.
Spring Boot Graceful Shutdown
Spring Boot 설정 예시다.
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 30s
management:
endpoint:
health:
probes:
enabled: true이 값은 Kubernetes terminationGracePeriodSeconds, ALB deregistration delay, gateway timeout과 함께 맞춰야 한다.
배포 중 흔한 장애
| 증상 | 가능 원인 |
|---|---|
| 배포 직후 502 | target 등록 전 app listen 미완료, old target 먼저 종료 |
| 배포 직후 503 | healthy target 없음 |
| connection reset | process가 in-flight 요청보다 먼저 종료 |
| 일부 사용자만 실패 | canary target group 문제 |
| long request만 실패 | draining/shutdown timeout 부족 |
| WebSocket 대량 재연결 | target drain으로 connection 종료 후 reconnect storm |
코드와 명령으로 확인하기
배포 타임라인을 남긴다.
10:00:00 new pod started
10:00:20 readiness UP
10:00:25 LB target healthy
10:02:00 old pod SIGTERM
10:02:01 readiness DOWN
10:02:10 LB target draining
10:02:35 in-flight requests completed
10:02:40 process exited배포 중 smoke test는 전체 endpoint가 아니라 version별 target을 구분해야 한다.
for i in {1..20}; do
curl -sI https://api.example.com/health \
| grep -iE 'x-app-version|x-app-instance|x-request-id'
sleep 1
doneCanary와 Rollback
canary 배포는 네트워크 계층에서도 기준이 필요하다.
- canary target group 5xx
- canary p95/p99 latency
- target health transition count
- connection reset/timeout count
- business metric 또는 synthetic check
rollback은 traffic weight를 되돌리는 것뿐 아니라 DB migration, cache, message consumer, old asset compatibility까지 포함한다.
인프라 협업 포인트
- 앱 팀은 shutdown에 필요한 최대 시간과 long request 종류를 알려야 한다.
- 인프라 팀은 deregistration delay, health check interval/threshold, rolling update max unavailable 값을 공유해야 한다.
- 배포 도구, orchestrator, LB가 각각 언제 target을 빼는지 타임라인으로 맞춘다.
- DB migration은 old/new app이 동시에 동작할 수 있는 expand-contract 방식이 안전하다.
실전 팁
- readiness down을 먼저 만들고 sleep으로 시간을 버는 preStop hook은 임시방편이지만, LB 반영 지연을 이해하는 데 도움이 된다.
- shutdown timeout은 가장 긴 정상 요청보다 길어야 하지만 무한정 길면 배포가 느려진다.
- WebSocket/SSE 서비스는 배포 공지 또는 재연결 UX가 필요할 수 있다.
- canary가 실패하면 자동 rollback 기준을 사전에 정한다. 사람이 그래프를 보고 판단하는 시간도 장애 시간이다.
- 개인 프로젝트에서도 systemd/docker compose 재시작 전에 health check와 graceful shutdown을 설정하면 배포 중 502가 줄어든다.
위험 신호!
- SIGTERM 후에도 readiness가 계속 UP이다.
- LB deregistration delay보다 app shutdown timeout이 짧다.
- 배포 성공 기준이 “프로세스 시작”뿐이고 synthetic request가 없다.
- DB migration이 old version과 호환되지 않는다.
확인 질문
확인 질문
- 배포 중 readiness를 먼저 내려야 하는 이유는 무엇인가?
- LB나 orchestrator가 새 요청을 보내지 않도록 한 뒤 기존 요청을 마무리해야 하기 때문이다.
- deregistration delay와 app shutdown timeout이 어긋나면 어떤 일이 생기는가?
- LB가 아직 보내는 중인데 app이 먼저 종료되거나, app은 기다리는데 LB가 먼저 끊어 connection reset/502가 날 수 있다.
- canary 배포에서 전체 error rate만 보면 위험한 이유는 무엇인가?
- 작은 비율의 canary 장애가 전체 평균에 희석되어 canary 대상 사용자 실패를 놓칠 수 있기 때문이다.