502/503/504 장애 런북
Web Server와 WAS 사이에서 자주 만나는 502, 503, 504를 계층별로 좁히는 운영 런북
이 런북의 범위
Client
-> LB / WAF
-> Nginx or Apache HTTP Server
-> Tomcat
-> Application / DB / external API
502, 503, 504는 대부분 앞단 Web Server나 Gateway가 뒤쪽 서버와 통신하는 과정에서 드러난다. 상태 코드는 원인을 확정하지 않고, 어느 계층부터 볼지 알려주는 단서로 사용한다.
상태 코드 감 잡기
| 코드 | 보통의 의미 | 먼저 볼 곳 |
|---|---|---|
| 502 | upstream에서 정상 응답을 받지 못함 | WAS 포트, 프로세스, proxy 설정, protocol mismatch |
| 503 | 서비스가 일시적으로 처리 불가 | backend down, health check, worker 한계, maintenance |
| 504 | upstream 응답을 기다리다 timeout | Tomcat thread, DB query, 외부 API, proxy timeout |
같은 502라도 Nginx, Apache mod_proxy, mod_jk, LB가 만든 오류 페이지가 다를 수 있다.
브라우저 화면보다 access log와 error log를 기준으로 판단한다.
처음 5분
- 장애 시간, URL, Host, client IP, 재현 여부를 적는다.
- Web Server access log에 요청이 남았는지 확인한다.
- 같은 시간대 Web Server error log를 본다.
- Tomcat process와 Connector port가 살아 있는지 확인한다.
- Tomcat access/application/catalina log에 같은 요청 흔적이 있는지 찾는다.
- 직전 배포, 설정 reload, 인증서 교체, 방화벽 변경이 있었는지 확인한다.
공통 명령
date
hostname
ss -lntp
ps -ef | grep tomcat
systemctl status nginx
systemctl status httpd
systemctl status tomcat
앞단에서 직접 호출:
curl -sv -H 'Host: example.com' http://127.0.0.1/
curl -sv http://127.0.0.1:8080/
curl -sv http://127.0.0.1:8080/app/health
로그 확인:
tail -n 200 /var/log/nginx/error.log
tail -n 200 /var/log/httpd/error_log
tail -n 200 $CATALINA_BASE/logs/catalina.out
journalctl -u nginx -n 200
journalctl -u httpd -n 200
journalctl -u tomcat -n 200
502 접근
502는 앞단이 backend로 연결했지만 정상적인 HTTP 응답을 받지 못했을 때 자주 나온다.
| 흔한 단서 | 확인 |
|---|---|
| connection refused | Tomcat 미기동, 포트 불일치, process crash |
| no route / timed out while connecting | 방화벽, 보안 그룹, routing |
| upstream prematurely closed | Tomcat 재기동, JVM crash, 애플리케이션 강제 종료 |
| invalid response | HTTP/AJP protocol mismatch, 잘못된 upstream 포트 |
| bad gateway after deploy | context path, proxy path, 애플리케이션 기동 실패 |
확인 순서:
proxy_pass,ProxyPass,JkMount가 가리키는 host와 port를 확인한다.- 해당 포트가 listen 중인지
ss -lntp로 본다. - Web Server에서 backend URL을 직접
curl로 호출한다. - Tomcat이 재기동되었는지 catalina log와 system journal을 본다.
- AJP 구성이라면 HTTP 포트에 AJP로 붙거나, AJP 포트에 HTTP로 붙은 것은 아닌지 확인한다.
503 접근
503은 backend가 없거나, health check에서 제외되었거나, 현재 요청을 받을 수 없는 상태일 때 나타난다.
| 흔한 단서 | 확인 |
|---|---|
| no live upstreams | Nginx upstream health, backend 전체 down |
| service unavailable | Tomcat 미기동, maintenance, application startup 실패 |
| worker down | mod_jk worker 상태, AJP 연결 실패 |
| queue full / overload | Apache worker, Tomcat thread, DB pool |
확인 순서:
- backend 전체가 죽었는지 일부 node만 죽었는지 나눈다.
- health check URL이 실제로 200을 반환하는지 직접 호출한다.
mod_jk라면workers.properties,jk.log, worker 상태를 확인한다.- Apache MPM worker 여유와 Tomcat thread 여유를 같이 본다.
- 점검 페이지나 maintenance 설정이 켜져 있는지 확인한다.
504 접근
504는 앞단이 backend 응답을 기다리다 timeout된 상태다. 앞단에서는 실패로 끝났어도 Tomcat이나 애플리케이션에서는 요청 처리가 계속 진행 중일 수 있다.
| 흔한 단서 | 확인 |
|---|---|
| upstream timed out | proxy_read_timeout, ProxyTimeout, backend 지연 |
| 특정 API만 504 | 해당 API의 DB query, 외부 API 호출, lock |
| 전체적으로 느림 | Tomcat thread 고갈, GC pause, DB 장애 |
| 재시도 후 성공 | backend 일부 node 지연, connection pool 대기 |
확인 순서:
- access log의 request time과 upstream time을 본다.
- Tomcat thread dump를 10초 간격으로 여러 번 남긴다.
- application log에서 같은 요청 ID나 시간대의 지연 지점을 찾는다.
- DB slow query, lock, connection pool 대기를 확인한다.
- timeout을 늘리기 전에 실제 처리 시간이 왜 길어졌는지 확인한다.
Tomcat thread dump:
jcmd <PID> Thread.print > /tmp/tomcat-thread-1.txt
jcmd <PID> Thread.print > /tmp/tomcat-thread-2.txt
jcmd <PID> Thread.print > /tmp/tomcat-thread-3.txt
로그 문구별 빠른 매핑
| 로그 문구 | 우선 의심 |
|---|---|
connect() failed | backend 포트 닫힘, 방화벽 |
connection refused | Tomcat down, 포트 불일치 |
upstream timed out | backend 응답 지연, timeout |
prematurely closed connection | backend crash, restart, 연결 종료 |
worker is in error state | mod_jk worker down |
Broken pipe | client 중단, proxy 연결 종료 |
문구는 제품과 버전에 따라 달라질 수 있다. 핵심은 요청이 어느 로그까지 도달했는지와, 연결 실패인지 응답 지연인지 구분하는 것이다.
임시 조치와 원인 분석 분리
| 목적 | 가능한 조치 |
|---|---|
| 사용자 영향 완화 | 문제 node 제외, rollback, maintenance 안내 |
| 처리량 회복 | Tomcat 재기동, worker 수 조정, DB connection 회복 |
| 지연 완화 | 느린 API 차단, 외부 연동 우회, timeout 임시 조정 |
| 근거 확보 | 로그 백업, thread dump, heap dump, 설정 diff 보관 |
임시 조치 후에는 반드시 원인을 다시 좁힌다. timeout을 늘려서 504가 사라졌다면 장애가 해결된 것이 아니라 느린 처리를 더 오래 기다리게 만든 것일 수 있다.
관련 문서
- 로그와 장애 진단
- Apache MPM과 KeepAlive
- Tomcat Connector와 JVM Thread
- Apache httpd-Tomcat mod_proxy 연동
- Apache httpd-Tomcat mod_jk 연동
- Nginx-Tomcat Reverse Proxy 연동
관련 블로그 기록
- LB·WAF 뒤에서 클라이언트 실제 IP 살리기 — 앞단을 여러 번 지나는 요청에서 원 IP를 추적한 사례
- 폐쇄망에서 서버 시간 맞추기 — 여러 서버 로그의 시간축을 맞추는 사례
- WAS가 죽기 전에 로그 남기기 — 장애 후 분석 근거를 남기도록 준비한 사례
공식 문서
- NGINX ngx_http_proxy_module
- Apache Module mod_proxy
- Apache Core TimeOut
- Apache Tomcat HTTP Connector
- Tomcat Connectors workers.properties
닫기 전 질문
- 이 오류 페이지는 LB, Nginx, Apache HTTP Server, Tomcat 중 누가 만든 것인가?
- 요청은 Web Server access log, error log, Tomcat log 중 어디까지 도달했는가?
- 502/503/504 중 연결 실패, backend 제외, 응답 지연 중 어느 쪽인가?
- 임시 조치로 증상이 사라진 뒤에도 원인 근거를 남겼는가?