WAS를 배포할 때마다 약 1~2초간 502 에러가 발생했다. 로드밸런서 뒤에 WAS가 두 대 있고, 순차적으로 배포하니까 무중단 배포인 줄 알았는데 실제로는 502가 찍히고 있었다.


구조

[클라이언트]
    │
    ▼
[로드밸런서]  ──────────► 헬스체크
    │
    ├── [WAS-01]
    └── [WAS-02]

WAS-02를 배포 중 → WAS-01만 살아있어야 함 → 근데 502가 발생


원인 분석

문제 1: WAS가 종료되는데 로드밸런서가 모른다

로드밸런서 헬스체크 설정이 느슨했다. 헬스체크 주기가 30초, WAS가 종료 신호를 받고 실제로 죽는 데 1초도 안 걸렸다.

배포 시작
  │
  ├── WAS-02 종료 신호 수신
  ├── WAS-02 즉시 종료 (처리 중인 요청 버림)
  │
  └── 로드밸런서: WAS-02가 죽은 줄 모름 (헬스체크까지 30초 남음)
        └── WAS-02로 요청 계속 전달 → 502

문제 2: 처리 중인 요청을 그냥 버렸다

SIGTERM 신호를 받으면 Spring Boot 앱이 그냥 종료됐다. 진행 중이던 HTTP 요청들이 응답을 받지 못하고 연결이 끊겼다.


해결 1: Graceful Shutdown 활성화

Spring Boot 2.3+에서는 Graceful Shutdown을 설정으로 켤 수 있다.

# application.yml
server:
  shutdown: graceful

spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s

graceful로 설정하면 종료 신호를 받았을 때:

  1. 새 요청 수신 거부 (503 반환)
  2. 현재 처리 중인 요청이 완료될 때까지 대기 (최대 30초)
  3. 모두 완료되면 종료
SIGTERM 수신
  │
  ├── 새 요청 → 503 (연결 거부)
  ├── 기존 요청 완료 대기 (최대 30초)
  │
  └── 완료 후 종료

로드밸런서 입장에서는 WAS가 503을 반환하기 시작하면 “비정상”으로 판단하고 해당 서버로의 트래픽을 끊는다.


해결 2: 로드밸런서 헬스체크 튜닝

헬스체크 간격을 줄이고, 서버가 빠르게 제외되도록 설정한다. (클라우드 로드밸런서마다 설정 위치가 다르지만 개념은 같다)

설정 변경 전 변경 후
헬스체크 간격 30초 5초
비정상 판단 기준 3회 연속 실패 2회 연속 실패
서버 제외까지 최대 90초 10초

헬스체크 간격 × 비정상 기준 횟수 = 서버 제외까지 최대 대기 시간이다. 이 시간 동안은 죽어가는 서버로 요청이 계속 날아간다.


Graceful Shutdown만으로는 부족한 이유

Graceful Shutdown을 켜면 WAS는 새 요청을 503으로 거부하기 시작한다. 하지만 로드밸런서가 503을 받고 WAS를 제외하기까지 헬스체크 주기만큼 시간이 걸린다. 그 사이에 날아오는 요청들은 WAS까지 도달해서 503을 받는다.

SIGTERM → WAS 새 요청 거부(503) → 로드밸런서 헬스체크 실패 감지 → WAS 제외
           ↑________________________________↑
                   이 구간이 문제

이 구간을 없애려면 Connection Draining(또는 Deregistration Delay) 설정이 필요하다. WAS가 제외 대상으로 표시되면, 로드밸런서가 기존 진행 중인 연결만 유지한 채 새 연결은 보내지 않는다.


배포 순서 (권장)

1. WAS-02 배포 신호
2. 로드밸런서에서 WAS-02 수동 제외 (또는 헬스체크 실패 대기)
3. WAS-02 Graceful Shutdown 시작 → 기존 요청 완료
4. WAS-02 종료 및 새 버전 기동
5. 헬스체크 통과 확인 후 로드밸런서에 WAS-02 다시 추가
6. WAS-01 동일하게 반복

Graceful Shutdown + 헬스체크 튜닝 + Connection Draining 세 가지가 함께 있어야 진짜 무중단 배포다.


배운 것

  • Graceful Shutdown은 WAS가 천천히 죽게 하는 것이고, 로드밸런서 헬스체크는 빠르게 감지하는 것이다. 둘 다 없으면 502가 난다.
  • server.shutdown: graceful만 켜면 반쪽짜리다. timeout-per-shutdown-phase도 서비스 특성에 맞게 조정해야 한다.
  • 무중단 배포라고 불리려면 WAS, 로드밸런서, Connection Draining 세 레이어가 모두 맞아야 한다.
  • 배포 직후 에러율 모니터링을 반드시 해야 한다. “롤링 배포 하면 무중단”은 설정을 안 잡으면 환상이다.