개발

Loadbalancer로 부터 온 502 Bad gateway

개발자_티모 2025. 10. 19. 22:57
반응형

1. 문제 현상

회원가입 전환율이 약 10% 하락하는 현상이 발생하였다.
로그 분석 결과, 인증률 하락 시점부터 502 Bad Gateway 오류 발생 건수가 급격히 증가하는 상관관계를 확인하였다.
실제 회원가입 절차를 수행하면 일정 확률로 502 응답이 반환되는 현상이 재현되었다.

결과적으로 다음과 같은 인과 관계가 의심되었다.

가입자 하락 → 인증 실패 증가 → 502 오류 다발


2. 원인 탐색 방향

502 오류는 “게이트웨이(중간 서버, 예: ALB)가 백엔드 서버로부터 정상적인 응답을 받지 못했을 때” 발생한다.
AWS 공식 문서(참고 링크)에 따르면,
Application Load Balancer(ALB) 기준에서 502 오류는 다음 세 가지 구간에서 발생할 수 있다.

  1. Pod(백엔드 서버)가 직접 502를 반환하는 경우
  2. ALB → Pod 간 연결 실패
  3. TLS 협상(Handshake) 중 연결 종료

3. Pod이 502를 반환하는 경우

(1) 증상

ALB Access Log에서 elb_status_code=502, target_status_code=502로 동시에 기록된다.

(2) 원인

가장 일반적인 원인으로, 서버의 Keep-Alive 설정 불일치 또는 커넥션 타임아웃 설정 불균형이 있다.

(3) 상세 원리

Load Balancer는 매 요청마다 새로운 TCP 핸드셰이크를 맺는 것은 비효율적이므로,
내부적으로 Connection Pool(커넥션 재사용) 을 유지한다. 이 연결은 다음 조건 중 하나를 만족하면 종료된다.

  • 설정된 Idle Timeout(유휴 연결 유지 시간)이 경과했을 때
  • 백엔드 서버(Pod)로부터 FIN 패킷(연결 종료 신호)을 수신했을 때

현재 설정은 다음과 같다.

  • ALB Idle Timeout: 60초
  • 서버(Pod) Keep-Alive Timeout: 5초

두 설정이 불일치할 경우, 아래와 같은 타이밍 문제가 발생한다.

  1. Pod이 5초 만에 연결 종료(FIN)
  2. ALB는 연결이 유효하다고 판단(60초 유지)
  3. 이후 동일한 연결을 재사용 시 이미 종료된 소켓에 write → 502 반환

(4) 추가 고려 사항

서버에서는 headersTimeout설정이 keepAliveTime보다 커야한다.
이는 클라이언트가 헤더를 모두 전송하기 전에 서버가 커넥션을 조기 종료하는 상황을 방지하기 위함이다.
헤더 전송 중 연결이 끊어질 경우, ALB는 비정상 종료로 인식하고 502로 기록할 수 있다.


아래는 설정을 진행하고 나서 502가 뜬 기록인데 위 설정으로 인해서 약 95% 감소했습니다.

alb 502 응답 빈도


4. Pod까지 도달하지 못해 ALB가 502를 반환하는 경우

이 경우는 클라이언트 요청이 ALB까지는 도달했지만,
ALB가 백엔드(Pod)로 요청을 전달하지 못한 경우를 의미한다.
즉, ALB → Pod 간 TCP 연결 자체가 실패한 상황이다.

(1) 로그 특징

ALB Access Log에서 target_status_code = -로 기록된다.
이는 ALB가 Pod까지 TCP 세션을 맺지 못했다는 것을 의미한다.

(2) 주요 원인

  1. Pod 비정상 상태
    • Pod이 CrashLoopBackOff, OOMKilled 등으로 종료되었거나,
      헬스체크(Health Check)에 실패한 상태에서 트래픽이 라우팅된 경우이다.
    • ALB는 유효한 Target을 찾지 못해 요청을 전달하지 못하고 502를 반환한다.
  2. 라우팅 설정 오류
    • Target Group의 포트 또는 서비스 매핑이 실제 Pod의 포트와 불일치할 경우 발생한다.
    • 예: ALB Target Group은 8080을 바라보지만, Pod이 3000 포트에서만 리스닝 중인 경우
      → ALB는 연결할 수 없어 502 반환.

5. TLS 협상(Handshake) 오류

본 장애의 근본 원인은 TLS 협상 실패(Client TLS Negotiation Error)였다.
TLS 협상(Handshake)이란 실제 데이터가 암호화되어 전송되기 전에,
클라이언트–게이트웨이(ALB)–서버 간에 암호화 알고리즘, 인증서, 세션 키 등을 교환하여
보안 채널을 수립하는 초기 단계이다.


(1) TLS 협상 단계 요약

  1. ClientHello
    클라이언트(브라우저)가 서버로 지원 가능한 암호화 알고리즘, TLS 버전, 세션 ID 등을 제시한다.
  2. ServerHello + Certificate
    서버(ALB 또는 Pod)가 사용할 암호화 알고리즘을 선택하고, 인증서를 반환한다.
  3. Key Exchange
    양측이 세션 키를 교환하며, 이때 ALB는 실제 서버와의 세션도 병렬로 수립한다.
  4. Finished
    세션 키를 이용해 암호화된 통신이 시작된다.

이 네 단계 중 어느 한 단계라도 실패하면, 연결은 즉시 종료되며
ALB는 “502 Bad Gateway” 또는 “Client TLS Negotiation Error”로 기록할 수 있다.


(2) 발생 시나리오

  • 상황:
    사용자가 모바일 브라우저에서 회원가입 API 호출(A 요청)을 보낸 직후,
    카카오 인증 화면으로 리다이렉트되면서 브라우저가 백그라운드 상태로 전환된다.
  • 모바일 OS 동작:
    iOS 및 Android는 백그라운드 탭의 네트워크 연결을 일정 시간 후 강제로 정리한다.
    이때 브라우저가 ClientHello를 보낸 직후 혹은 ServerHello를 받기 전에 세션을 닫으면,
    ALB 입장에서는 TLS 협상 미완료 상태에서 연결이 끊긴 것처럼 보이게 된다.
  • ALB 관점:
    ALB는 클라이언트 쪽 세션이 먼저 끊기면 백엔드(Pod)로의 세션을 유지하지 않는다.
    이때 ALB Access Log에는 아래와 같은 패턴이 나타난다.

(3) 계층별 타임라인 예시

구간 동작 상태
(1) 클라이언트 → ALB: ClientHello 송신 정상 시작
(2) ALB → 서버(Pod): ServerHello 중계 ALB 세션 생성 중
(3) 클라이언트가 앱 전환으로 네트워크 중단 TCP FIN 또는 RST 송신
(4) ALB는 응답 통로를 잃고 세션 종료 ClientTLSNegotiationError 기록
(5) 서버(Pod)는 이미 세션 완료를 기다리던 중 연결 끊김, 응답 전달 불가

(5) 대응 방안

본 이슈는 TLS 협상 중 네트워크 단절로 인한 일시적 연결 실패로 확인되었다.
이에 따라, 502 오류를 네트워크 에러로 간주하고 재시도 로직을 적용하도록 변경하였다.

✅ 변경된 정책

  1. 502 및 네트워크 오류 재시도 처리
    • 502, ClientTLSNegotiationError, ECONNRESET, ENOTFOUND, ETIMEDOUT
      네트워크 계층의 일시적 실패는 재시도 대상으로 분류.
    • 최대 3회까지 지수 백오프(Exponential Backoff) 간격으로 재요청.
  2. 클라이언트 단에서의 개선
    • iOS Safari, Android Chrome 등 브라우저 환경에서
    • 백그라운드 전환 이벤트(visibilitychange)* 시 pending 요청을 중단하고,
      포그라운드 복귀 시 자동 재요청하도록 처리.
반응형