- Published on
AWS ALB 502/504 급증? 타임아웃 7곳 점검
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버가 멀쩡해 보이는데 ALB에서만 502/504가 급증하는 상황은, 대개 요청이 어디선가 ‘제시간에’ 끝나지 못해 연결이 끊기거나(504), 타깃이 비정상 응답/연결 실패로 보이는(502) 패턴으로 수렴합니다. 중요한 건 “타임아웃은 한 군데만 존재하지 않는다”는 점입니다.
이 글은 ALB를 기준으로 타임아웃이 걸릴 수 있는 7곳을 위에서 아래로(클라이언트 -> ALB -> 타깃 -> 앱 -> 외부 의존성/DB) 정렬해 점검하는 실전 가이드입니다.
참고: Cloudflare 같은 상위 프록시가 끼어 있다면, 먼저 엣지 오류와 원본(ALB) 로그를 함께 보는 편이 빠릅니다. 진단 흐름은 Cloudflare 520·521, Nginx·ALB 로그로 30분 진단도 같이 보세요.
0) 먼저: 502 vs 504를 ALB 관점에서 재정의
504 (Gateway Timeout): ALB가 타깃으로부터 정해진 시간 내 응답을 못 받음. 대표적으로idle_timeout또는 타깃/앱의 처리 지연.502 (Bad Gateway): ALB가 타깃과 연결 자체에 실패하거나, 연결은 됐지만 비정상 응답(예: 조기 종료, 리셋 등)을 받는 경우.
즉, 504는 “늦음”, 502는 “끊김/불완전”의 성격이 강합니다. 하지만 실제로는 타임아웃 불일치가 502를 유발하는 경우도 흔합니다(예: 앱이 먼저 연결을 끊어버려 ALB가 502로 기록).
1) ALB idle_timeout (가장 흔한 504)
ALB에는 유휴 타임아웃이 있습니다. HTTP 요청을 보냈는데 응답 바이트가 일정 시간 동안 전혀 오지 않으면 ALB가 연결을 끊습니다. 기본값은 보통 60s로 시작하는 경우가 많고, 장기 작업/대용량 응답/스트리밍에서 자주 터집니다.
점검 포인트
- 장시간 처리 요청이 존재하는가(리포트 생성, 대용량 export, 외부 API fan-out 등)
- 서버는 처리 중인데 응답을 늦게 시작하는가(첫 바이트가 늦음)
- SSE/롱폴링/스트리밍 응답에서 주기적 keepalive 바이트가 없는가
조치
- ALB
idle_timeout을 업무 특성에 맞게 증가 - 앱이 처리 중이라도 주기적으로 바이트를 flush(스트리밍)하거나, 비동기 작업으로 분리
예시(원인 재현): 서버가 70s 후에야 응답을 시작하면, ALB idle_timeout=60s에서는 504가 날 수 있습니다.
2) Target Group 헬스체크 타임아웃/임계치 (정상 타깃이 갑자기 “죽은 것처럼”)
헬스체크는 ALB의 눈입니다. 헬스체크가 느려지거나 실패하면, 타깃이 unhealthy로 빠지고 트래픽이 다른 타깃으로 쏠리며 연쇄 지연이 발생합니다.
점검 포인트
- 헬스체크 경로가 DB/외부 API를 건드리는가
- 헬스체크
timeout이 너무 짧거나,unhealthy threshold가 너무 공격적인가 - 배포 직후 워밍업이 필요한데
healthy threshold/deregistration delay가 맞지 않는가
조치
- 헬스체크는 가볍게(프로세스/스레드풀/기본 의존성만)
- 배포 시 드레인(
deregistration delay)과 워밍업 시간을 정렬
3) 타깃(EC2/ECS/EKS)에서의 연결 수/포트 고갈, 커널 레벨 큐 적체
ALB는 타깃에 연결을 맺어야 합니다. 타깃이 과부하이면 다음 현상이 502/504로 튈 수 있습니다.
SYN backlog적체(연결 수락 지연)- ephemeral port 고갈(특히 NAT/프록시/아웃바운드 과다)
conntrack테이블 포화(EKS/노드 레벨)
점검 포인트
- 타깃의
CPU가 낮은데도p99 latency가 튀는가(네트워크/큐 병목 의심) - 노드에서
nf_conntrack경고, 드롭 증가 - 갑자기 특정 AZ/노드만 오류가 집중되는가
조치
- 타깃 스케일아웃, 커넥션 수 제한(클라이언트 풀), 커널 튜닝
- EKS라면 노드/파드 네트워크 상태도 같이 봅니다(예: DNS 지연이 타임아웃으로 번짐)
관련해서 EKS 환경에서 외부 호출이 502로 튀는 케이스는 EKS Pod에서 STS 502 Bad Gateway 원인·해결도 참고할 만합니다.
4) 앱 서버(예: Nginx/Envoy) 상위 proxy_read_timeout/upstream 타임아웃 불일치
ALB 앞단 말고, 타깃 내부에 또 프록시가 있는 구조(예: ALB -> Nginx -> App)에서 타임아웃 불일치가 매우 흔합니다.
- Nginx
proxy_read_timeout이30s - ALB
idle_timeout이60s - 앱은
45s에 응답
이 경우 Nginx가 먼저 끊고, ALB는 타깃이 비정상 종료한 것으로 보고 502를 기록할 수 있습니다.
Nginx 예시 설정
location /api/ {
proxy_connect_timeout 3s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
send_timeout 60s;
proxy_pass http://app_upstream;
}
점검 포인트
- 타깃 내부 프록시의
connect/read/send타임아웃 - 업스트림 keepalive 설정,
max_conns제한 - HTTP
1.1keepalive/커넥션 재사용이 과부하를 유발하는지
5) 애플리케이션 서버 타임아웃(스레드풀/요청 처리 제한 포함)
애플리케이션은 “타임아웃”이 단순히 시간만의 문제가 아니라, 처리 자원 고갈로도 발생합니다.
대표 원인
- 요청 스레드풀 고갈(대기열 증가
->응답 지연->ALB 504) - DB 커넥션 풀 고갈(대기
->지연) - GC 스톱 더 월드로 인한 순간 멈춤
Spring Boot 예시(톰캣)
application.yml에서 서버/스레드 관련 기본값을 확인하고, 관측 지표와 함께 조정합니다.
server:
tomcat:
threads:
max: 200
accept-count: 100
connection-timeout: 5s
비즈니스 로직이 느린 경우, 특히 ORM 사용 시 쿼리 폭발이 타임아웃으로 이어집니다. N+1로 응답이 늦어져 ALB 504가 나는 케이스는 Spring Boot JPA N+1 폭발 - 원인·해결 8가지처럼 DB 왕복 횟수부터 줄이는 게 정공법입니다.
6) DB/외부 의존성 타임아웃(드라이버, 커넥션 풀, 쿼리 타임아웃)
ALB 504의 뿌리가 DB인 경우가 제일 많습니다. 앱은 “DB 응답을 기다리느라” 늦고, ALB는 “응답이 없어서” 끊습니다.
점검 포인트
- DB 쿼리 타임아웃이 설정되어 있는가(무한 대기 방지)
- 커넥션 풀 대기 타임아웃(예: Hikari
connectionTimeout)이 적절한가 - 외부 API 호출에
connectTimeout/readTimeout이 분리되어 있는가
Java 예시(OkHttp 타임아웃)
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(java.time.Duration.ofSeconds(2))
.readTimeout(java.time.Duration.ofSeconds(5))
.writeTimeout(java.time.Duration.ofSeconds(5))
.callTimeout(java.time.Duration.ofSeconds(8))
.build();
핵심은 상위 타임아웃이 하위 타임아웃보다 길어야 “어디서 끊겼는지”가 명확해진다는 점입니다. 예를 들어 외부 API의 readTimeout=5s인데 ALB idle_timeout=60s면, 외부 API가 느려도 앱이 먼저 실패를 결정하고(에러 응답), ALB는 정상 응답을 받게 되어 504를 줄일 수 있습니다.
7) 재시도/서킷브레이커/큐잉으로 인한 ‘증폭’ (타임아웃이 폭발로 바뀌는 지점)
타임아웃 자체보다 무서운 건 재시도 정책이 지연을 폭발시키는 것입니다.
- 타임아웃
5s호출을 동기 재시도3회 - 동시 요청이 몰리면 대기열이 늘고, 전체 요청 시간이
15s+로 늘어남 - 결국 ALB
idle_timeout에 걸리거나, 스레드풀이 고갈
점검 포인트
- 클라이언트/서버/서비스 메시에서 재시도가 중복 적용되는가
- 재시도 간 백오프가 있는가
- 타임아웃이 “짧고 명확하게 실패”하도록 설계되어 있는가
gRPC를 쓴다면
gRPC는 deadline 전파가 핵심입니다. 내부 서비스가 DEADLINE_EXCEEDED를 쌓아 올리면 ALB 504로 보일 수 있습니다. 타임아웃 전파/재시도 설계는 gRPC MSA에서 DEADLINE_EXCEEDED 원인 9가지도 함께 확인하세요.
로그로 10분 안에 방향 잡는 법(ALB 액세스 로그)
ALB 액세스 로그를 켜면, 다음 필드가 특히 유용합니다.
elb_status_code/target_status_coderequest_processing_time(ALB가 요청을 처리한 시간)target_processing_time(타깃이 처리한 시간)response_processing_time(응답을 클라이언트로 보내는 시간)
Athena 쿼리 예시(상위 504 원인 후보 찾기)
아래는 개념 예시입니다. 실제 테이블/컬럼명은 환경에 맞게 조정하세요.
SELECT
elb_status_code,
target_status_code,
approx_percentile(target_processing_time, 0.95) AS p95_target,
approx_percentile(target_processing_time, 0.99) AS p99_target,
count(*) AS cnt
FROM alb_logs
WHERE from_iso8601_timestamp(time) > now() - interval '1' hour
GROUP BY 1, 2
ORDER BY cnt DESC;
해석 팁:
elb_status_code=504인데target_status_code가 비어 있거나-에 가깝다면, 타깃에서 응답을 못 받았거나 연결이 중간에 끊긴 가능성이 큽니다.target_processing_time의p99가 갑자기 치솟으면, 앱/DB 병목을 의심합니다.
실전 체크리스트: 타임아웃 정렬 규칙(권장 순서)
타임아웃은 “누가 먼저 포기할지”의 문제입니다. 보통은 더 안쪽(하위 계층)이 먼저 짧게 실패하고, 바깥(ALB/클라이언트)은 그보다 길게 기다리는 구성이 디버깅과 안정성에 유리합니다.
예시 정렬(업무에 맞게 조정):
- 외부 API
readTimeout:3s - 앱 내부 전체 요청 제한(예: 인터셉터/필터):
5s - Nginx
proxy_read_timeout:10s - ALB
idle_timeout:15s
이렇게 하면, 외부 API가 느릴 때 앱이 먼저 실패를 결정하고(에러를 응답), ALB는 504 대신 의미 있는 5xx/4xx를 받게 되어 “원인 추적 가능한 장애”로 바뀝니다.
마무리: 502/504 급증은 ‘한 곳의 문제’가 아니라 ‘불일치’의 문제
ALB 502/504는 보통 아래 중 하나로 귀결됩니다.
- ALB
idle_timeout보다 응답이 늦다(진짜 지연) - 중간 프록시/앱이 먼저 끊는다(불일치)
- 타깃이 연결을 감당 못 한다(자원/네트워크)
- DB/외부 의존성이 느리고, 재시도가 증폭한다(설계)
위 7곳을 순서대로 점검하면서 타임아웃 값을 계층적으로 정렬하고, ALB 로그의 target_processing_time 같은 지표로 “어디서 시간이 쓰였는지”를 확인하면, 502/504 급증은 생각보다 빠르게 안정화할 수 있습니다.