본문으로 건너뛰기
개발 머꼬
개발 노트인프라·운영
hohyeon.dev23

배포할 때마다 몇 초씩 502가 나는 이유

  • #Deployment
  • #DevOps
  • #Engineering Note

문제 발생

배포 스크립트가 새 컨테이너를 띄우고 기존 컨테이너를 즉시 종료하는 방식이었는데, 배포할 때마다 몇 초 동안 502/503 에러가 관측됐습니다.

원인 분석

무중단 배포가 깨지는 이유는 대부분 순서 문제입니다.

  1. 로드밸런서가 아직 모르는 상태에서 프로세스를 죽임: 새 인스턴스가 떴다고 바로 이전 인스턴스를 종료하면, 로드밸런서/리버스 프록시가 아직 이전 인스턴스로 라우팅 중인 요청이 남아있을 수 있습니다. 그 요청은 연결이 끊긴 대상으로 가서 실패합니다.
  2. SIGTERM을 받고 즉시 죽는 프로세스: 컨테이너 오케스트레이터는 종료 시 먼저 SIGTERM을 보내고 일정 시간(grace period) 뒤 SIGKILL로 강제 종료합니다. 애플리케이션이 SIGTERM을 무시하고 처리 중이던 요청까지 즉시 끊어버리면, 이미 받은 요청이 강제로 실패합니다.
  3. 새 인스턴스가 준비되기 전에 트래픽을 받음: readiness 체크 없이 컨테이너가 시작되자마자 트래픽을 라우팅하면, 아직 초기화 중인 인스턴스가 요청을 실패시킵니다(이 부분은 liveness/readiness 분리와도 연결됩니다).

해결 방안

  1. Rolling update 순서를 지킵니다: 새 인스턴스를 띄우고 → readiness 체크를 통과할 때까지 기다리고 → 로드밸런서에 새 인스턴스를 추가하고 → 그 다음에야 이전 인스턴스를 트래픽 대상에서 제외하고 → 마지막으로 이전 인스턴스를 종료합니다.
  2. Graceful shutdown을 구현합니다: SIGTERM을 받으면 (a) 먼저 readiness를 실패 상태로 바꿔 새 요청 유입을 막고, (b) 이미 받은 요청은 끝까지 처리한 뒤, (c) 프로세스를 종료합니다. 대부분의 웹 프레임워크는 이런 graceful shutdown 훅을 제공합니다.
process.on("SIGTERM", async () => {
  markNotReady(); // 이후 readiness probe 실패
  await server.close(); // 진행 중인 요청은 마저 처리
  process.exit(0);
});
  1. Grace period를 실제 요청 처리 시간보다 충분히 길게 설정합니다 — grace period가 너무 짧으면 graceful shutdown 로직이 채 끝나기 전에 SIGKILL로 강제 종료됩니다.
  2. 로드밸런서 쪽에서도 연결 드레이닝(connection draining) 설정이 있다면 함께 활성화합니다 — 로드밸런서가 이미 특정 인스턴스로 보낸 연결은 끝까지 처리하도록 기다려주는 기능입니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.