새벽 2시 15분. 슬랙 알림이 울립니다. "결제 API 오류 급증 — 고객 민원 다수 접수됨". 모니터링 대시보드를 열어보니 HTTP 5xx 에러율이 갑자기 치솟고 있습니다.
Tomcat은 살아있습니다. DB도 응답합니다. 그런데 왜 502가 쏟아질까요.
이 상황에서 "어느 구간이 문제인가"를 5분 안에 파악하지 못하면 복구는 점점 늦어집니다. HTTP 요청이 클라이언트에서 DB까지 거치는 모든 구간을 알고 있어야 — 장애 위치를 빠르게 좁힐 수 있습니다.
- 1DNS 조회 순서(/etc/hosts → DNS 서버)와 TTL의 의미를 설명할 수 있다
- 2L4/L7 로드밸런서의 차이와 X-Forwarded-For 헤더의 역할을 설명할 수 있다
- 3Nginx의 요청 처리 흐름과 access_log에서 upstream 정보를 읽을 수 있다
- 4curl -v로 HTTP 요청 전체 흐름을 추적하고 각 단계를 해석할 수 있다
- 5502 에러 발생 시 Nginx-Tomcat 구간의 문제를 진단하는 루틴을 실행할 수 있다
DNS와 도메인 해석 단계
요청의 시작은 도메인 이름을 IP 주소로 바꾸는 것입니다. 이 변환이 실패하거나 잘못된 IP를 반환하면 요청은 아예 시작되지 않습니다.
DNS 조회 순서와 /etc/hosts의 역할
브라우저가 https://example.com을 요청하는 순간, OS는 IP 주소를 알아내기 위해 순서대로 다음 위치를 조회합니다.
확대
IP를 변경했는데 일부 사용자만 계속 구 서버로 접속하는 문제가 있었습니다. DNS TTL이 아직 살아있어 캐시된 IP를 사용하고 있었기 때문입니다. 개발팀에서 /etc/hosts에 테스트 서버 IP를 등록해뒀다가 실수로 운영 서버에도 같은 파일을 배포해 트래픽이 엉뚱한 서버로 가기도 합니다. DNS가 어떻게 동작하는지 모르면 이런 상황에서 원인조차 찾을 수 없습니다.
1단계 — /etc/hosts 우선 조회
OS가 가장 먼저 확인하는 곳입니다. 파일에 도메인-IP 매핑이 있으면 DNS 서버로 질의를 보내지 않고 즉시 사용합니다.
# /etc/hosts 예시
127.0.0.1 localhost
192.168.1.50 internal-api.company.com
이 특성을 알면 두 가지 실수를 이해할 수 있습니다. 개발계에서 /etc/hosts에 내부 도메인을 추가해뒀다가 운영계에서 그 항목 때문에 잘못된 서버로 요청이 가는 경우, 반대로 장애 대응 중 hosts를 수정했지만 다른 서버에는 반영 안 된 경우가 대표적입니다.
2단계 — /etc/resolv.conf로 DNS 서버 지정
/etc/hosts에 항목이 없으면 /etc/resolv.conf에 기록된 DNS 서버로 질의를 보냅니다.
# /etc/resolv.conf 예시
nameserver 8.8.8.8 # 첫 번째로 질의할 DNS 서버
nameserver 8.8.4.4 # 첫 번째 실패 시 대체
search company.com # 짧은 호스트명에 자동으로 붙이는 도메인
DNS 질의 흐름 — 캐시 → 재귀 질의
DNS 서버가 응답을 갖고 있으면 캐시에서 즉시 반환합니다. 없으면 루트 DNS → TLD DNS → 권한 DNS 순으로 재귀 질의를 수행합니다.
TTL(Time To Live)과 DNS 전파
DNS 레코드에는 TTL이 설정돼 있습니다. TTL이 300이면 5분 동안 캐시가 유지됩니다. IP를 변경해도 기존 TTL이 만료되기 전까지는 옛 IP로 요청이 갈 수 있습니다. "도메인 바꿨는데 일부 사용자만 반영이 안 된다"는 이 TTL 때문입니다.
내부 DNS vs 공인 DNS
사내 서비스는 공인 DNS에 등록하지 않습니다. 내부 DNS 서버를 따로 운영하거나 /etc/hosts를 사용해 내부 도메인을 해석합니다. 인프라 엔지니어는 내부 DNS 레코드 추가 요청을 자주 받습니다.
HTTP 요청 한 건의 생애 — 전체 단계 한눈에
브라우저가 요청 하나를 보내면 실제로 무슨 일이 일어나나 — DNS부터 커넥션 종료까지 7단계
주소창에 https://shop.example.com/order를 치고 Enter를 누르면 잠시 뒤 화면이 그려집니다. 이 짧은 순간에 브라우저와 서버 사이에서는 이름 해석 → 연결 → 암호 협상 → 요청 전송 → 서버 라우팅 → 핸들러 처리 → 응답 → 커넥션 정리가 순서대로 일어납니다. 앞 절의 DNS는 이 흐름의 첫 단계일 뿐입니다. 전체 단계를 하나의 지도로 갖고 있으면 "느리다", "에러가 난다"는 막연한 증상을 "몇 번째 단계에서 깨졌나"로 좁혀 진단할 수 있습니다.
[브라우저] GET https://shop.example.com/order
│
① DNS 해석 shop.example.com → 203.0.113.10 (/etc/hosts → resolv.conf → 재귀질의)
│
② TCP 3-way SYN → SYN-ACK → ACK 로 443 연결 (LB·서버까지 경로가 열려야 함)
│
③ TLS 핸드셰이크 ClientHello → 인증서 검증 → 세션키 (HTTPS일 때만, 이후 전 구간 암호화)
│
④ 요청 전송 요청 라인 + 헤더(Host·Cookie·UA) + (본문)
│
⑤ 서버 수신·라우팅 LB → WAF → Nginx server_name·location 매칭 → upstream 선택
│
⑥ 핸들러 처리 WAS 스레드 배정 → 앱 로직 → DB 커넥션풀 → SQL → 결과 조립
│
⑦ 응답 생성·반환 상태코드 + 헤더 + 본문 을 역순으로 되돌림
▼
[브라우저] 200 OK / 렌더링 (Keep-Alive면 커넥션 재사용, 아니면 종료)
각 단계에서 무슨 일이 일어나고, 막히면 어떤 증상인가:
| 단계 | 하는 일 | 여기서 막히면 |
|---|---|---|
| ① DNS 해석 | 도메인을 IP로 변환. /etc/hosts → /etc/resolv.conf DNS → 재귀질의 순. TTL 동안 캐시 | 해석 실패 → could not resolve host / 옛 IP 캐시 → 엉뚱한 서버 접속(TTL·hosts 확인) |
| ② TCP 연결 | 해석된 IP의 443(또는 80)으로 3-way 핸드셰이크 | 방화벽·SG 차단 → Connection timed out(무응답) / 리스너 없음 → Connection refused(RST 즉시) |
| ③ TLS 핸드셰이크 | 인증서 제시·검증, 세션키 합의. 이후 전 구간 암호화 | 인증서 만료·체인 불완전·SNI 불일치 → SSL handshake failed·인증서 경고 |
| ④ 요청 전송 | 요청 라인(GET /order HTTP/1.1)과 헤더를 전송. Host 헤더로 가상호스트 지정 | Host 누락·오타 → 엉뚱한 server 블록 매칭 / 헤더 과대 → 431 |
| ⑤ 수신·라우팅 | LB·WAF 통과 후 Nginx가 server_name·location을 매칭해 정적 서빙 or upstream 선택 | WAF 차단 → 403(Nginx 로그엔 없음) / 매칭 실패 → 404 / upstream 미기동 → 502 |
| ⑥ 핸들러 처리 | WAS가 스레드를 배정해 앱 로직 실행 → DB 커넥션풀에서 커넥션 얻어 SQL 실행 | 커넥션풀 고갈·슬로우쿼리 → 응답 지연 → Nginx proxy_read_timeout 초과 시 504 |
| ⑦ 응답·종료 | 상태코드·헤더·본문을 역순(DB→앱→WAS→Nginx→LB→클라이언트)으로 반환. Keep-Alive면 커넥션 유지 | 앱 예외 → 500 / 응답 도중 연결 끊김 → 502·upstream prematurely closed |
즉 응답 하나는 이 7단계가 모두 성공했다는 뜻입니다. 상태코드는 "몇 번째 단계에서 깨졌는가"의 신호입니다 — 4xx는 대체로 ①⑤의 클라이언트·라우팅 문제(이름·인증서·경로·권한), ⑦의 서버·백엔드 문제(upstream 연결 5xx는 대체로 ⑤502, 처리 지연 504, 앱 예외 500)입니다. 그래서 장애 때 curl -v로 어느 단계까지 갔는지(Trying = ②, SSL connection = ③, < HTTP/1.1 = ⑦ 응답 도달) 먼저 확인하면, 뒤이어 볼 로그(LB·Nginx·WAS·DB)의 범위를 한 계층으로 좁힐 수 있습니다.
로드밸런서 통과 단계
DNS가 반환한 IP는 실제 서버의 IP가 아니라 로드밸런서의 VIP(가상 IP)인 경우가 대부분입니다.
L4 vs L7 로드밸런서, 그리고 클라이언트 IP 보존
Nginx 접근 로그에서 모든 IP가 같은 주소로 찍히는 현상이 있었습니다. 앞에 L7 로드밸런서가 있는데 X-Forwarded-For 헤더 설정이 없어서 Nginx 입장에서는 모든 요청이 LB에서 오는 것처럼 보였기 때문입니다. 클라이언트 IP를 모르면 특정 사용자 문제 추적도, 보안 차단도 불가능해집니다. L4와 L7의 동작 방식 차이가 IP 보존 방법을 결정합니다.
확대
L4 로드밸런서 — IP:Port 기반 라우팅
L4 LB는 패킷의 IP 주소와 포트만 보고 라우팅합니다. HTTP 헤더를 읽지 않습니다. 처리 속도가 빠르지만 URL 경로나 헤더 기반 라우팅은 불가능합니다.
클라이언트 IP 보존 방법으로 DSR(Direct Server Return)이나 프록시 프로토콜을 사용하지만, 설정이 없으면 서버에서 보이는 $remote_addr은 LB의 IP가 됩니다.
L7 로드밸런서 — HTTP 헤더 기반 라우팅
L7 LB는 HTTP 요청 전체를 읽습니다. URL 경로, 쿠키, 헤더 값에 따라 다른 서버로 보낼 수 있습니다.
L7 LB가 요청을 백엔드로 전달할 때 원래 클라이언트 IP를 헤더에 담아 보냅니다.
| 헤더 | 내용 |
|---|---|
X-Forwarded-For | 클라이언트 실제 IP (프록시 체인 포함 가능) |
X-Real-IP | 최종 클라이언트 IP만 단독으로 기록 |
X-Forwarded-Proto | 원래 프로토콜 (http / https) |
Nginx의 access_log에서 $remote_addr은 LB의 IP를, $http_x_forwarded_for는 실제 클라이언트 IP를 출력합니다.
WAF(Web Application Firewall)
대부분의 L7 LB 앞단이나 내부에 WAF가 위치합니다. SQL Injection, XSS, 비정상 요청 패턴을 탐지해 차단합니다. WAF가 요청을 차단하면 클라이언트에게 403이 반환되고 Nginx에는 요청이 도달하지 않습니다. "서버는 정상인데 403이 떨어진다"면 WAF 차단 로그를 먼저 확인해야 합니다.
SSL 종료 위치
SSL(TLS)을 어디서 끊느냐는 보안 정책과 운영 편의성의 균형입니다.
| 종료 위치 | 장점 | 단점 |
|---|---|---|
| LB에서 종료 | 인증서 한 곳에서 관리, 백엔드 부하 감소 | LB → Nginx 구간은 평문 HTTP |
| Nginx에서 종료 | 암호화 구간 연장 | 서버마다 인증서 관리 필요 |
내부망이 신뢰 가능하면 LB에서 종료하는 것이 일반적입니다.
Nginx 처리 단계
LB를 통과한 요청은 Nginx에 도달합니다. Nginx는 요청을 직접 처리하거나 Tomcat으로 전달합니다.
Nginx의 요청 처리 흐름과 로그 읽기
Nginx는 단순한 정적 파일 서버가 아니라 요청을 받아 어디로 보낼지 결정하는 트래픽 제어 지점입니다. location 블록 매칭 규칙을 모르면 특정 경로의 502 원인을 찾는 데 한참이 걸리고, access_log의 upstream 변수를 읽지 못하면 어느 Tomcat 인스턴스에 문제가 생겼는지 파악할 수 없습니다. Nginx 설정과 로그 구조를 이해하는 것이 요청 흐름 추적의 핵심입니다.
요청 수신 → server 블록 → location 블록
특정 API 경로만 502가 났는데 Tomcat은 살아있었습니다. Nginx 설정을 확인하니 해당 경로의 location 블록이 Tomcat이 아닌 다른 upstream을 바라보고 있었습니다. location 블록 매칭 규칙을 몰라서 어떤 요청이 어디로 가는지 파악하지 못했기 때문입니다. access_log의 $upstream_addr을 읽을 수 있으면 요청이 어느 Tomcat으로 갔는지 1분 안에 확인할 수 있습니다.
Nginx는 도착한 요청의 Host 헤더와 URL 경로를 보고 어떻게 처리할지 결정합니다.
server {
listen 80;
server_name example.com;
# 정적 파일 직접 서빙
location /static/ {
root /var/www/html;
}
# 동적 요청은 Tomcat으로 전달
location /api/ {
proxy_pass http://tomcat_upstream;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
}
}
upstream tomcat_upstream {
server 10.0.1.10:8080;
server 10.0.1.11:8080;
keepalive 32; # 커넥션 재사용 (매 요청마다 TCP 연결 새로 맺지 않음)
}
정적 파일 vs proxy_pass
- 정적 파일(
.html,.js,.css, 이미지): Nginx가 디스크에서 직접 읽어 응답. Tomcat까지 가지 않습니다. /api/경로:proxy_pass로 Tomcat에 전달. Nginx는 중간 전달자 역할.
access_log에서 읽을 수 있는 정보
# 기본 log_format에 upstream 정보 추가
log_format main '$remote_addr - $request - $status - $upstream_addr - $upstream_response_time - $http_x_forwarded_for';
| 변수 | 의미 |
|---|---|
$remote_addr | 요청을 보낸 IP (LB IP가 찍힘) |
$http_x_forwarded_for | 실제 클라이언트 IP |
$upstream_addr | 요청을 전달한 Tomcat 서버 IP:Port |
$upstream_response_time | Tomcat 응답 시간 (초 단위) |
$status | 최종 HTTP 상태 코드 |
$upstream_response_time이 길어지기 시작하면 Nginx-Tomcat 구간이 아니라 Tomcat 내부(또는 DB)가 느린 것입니다.
확대
Tomcat + App + DB 단계
Nginx로부터 요청을 받은 Tomcat은 Java 애플리케이션을 실행합니다.
Tomcat에서 DB까지의 처리 흐름과 로그 위치
Nginx는 빠른데 응답이 느리다는 제보가 왔습니다. Nginx $upstream_response_time을 보니 30초가 찍혀 있었고, Tomcat catalina.out에는 DB 커넥션 타임아웃 오류가 가득했습니다. 문제는 Nginx-Tomcat 구간이 아니라 Tomcat-DB 구간이었습니다. 어느 단계에서 시간이 소비되는지 알아야 정확한 원인을 찾을 수 있습니다.
요청 처리 순서
Tomcat 수신
→ Filter 체인 (인증, 로깅, CORS 등)
→ Dispatcher Servlet
→ Controller (URL 매핑)
→ Service (비즈니스 로직)
→ Repository / DAO
→ DB Connection Pool에서 커넥션 획득
→ SQL 실행 → DB 응답
→ 커넥션 반환 (Pool로 되돌림)
← 결과 반환
← DTO 조립
← JSON 직렬화
← HTTP 응답
← Nginx로 반환
← 클라이언트에 전달
DB Connection Pool
Tomcat은 매 요청마다 DB에 새로 연결하지 않습니다. 미리 만들어둔 커넥션 Pool에서 빌려 쓰고 반납합니다. Pool의 커넥션이 모두 사용 중이면 요청은 대기 상태가 됩니다. 이 대기가 쌓이면 Tomcat 응답 시간이 길어지고 결국 Nginx에서 타임아웃이 발생해 502가 됩니다.
각 단계별 로그 위치
| 구성요소 | 로그 위치 | 확인할 내용 |
|---|---|---|
| Nginx | /var/log/nginx/access.log | 요청 유입, 상태 코드, upstream 응답 시간 |
| Nginx | /var/log/nginx/error.log | upstream 연결 실패, 타임아웃 |
| Tomcat | $CATALINA_HOME/logs/catalina.out | JVM 오류, 애플리케이션 예외 |
| 애플리케이션 | /app/logs/application.log (경로 다양) | 비즈니스 로직 에러 |
| DB | MySQL slow query log | 느린 쿼리 (long_query_time 초과) |
응답은 역순으로 반환됩니다. DB → Repository → Service → Controller → Tomcat → Nginx → LB → 클라이언트. 각 구간에서 처리 시간이 누적됩니다.
단계별 요청 추적 실습
로컬 DNS 캐시 없이 직접 조회
nslookup example.comDNS 전체 위임 경로 추적
dig +trace example.comHTTP 요청 전 과정 상세 추적
curl -v https://example.com/api/health응답 헤더에서 X-Forwarded-For 확인
curl -sI https://example.com | grep -i forwarded- curl -v 출력에서 먼저 "Trying IP:PORT" 줄로 DNS 해석 결과 확인, 그 다음 "Connected to" 줄로 TCP 연결 성공 확인, 마지막으로 "< HTTP/1.1 XXX" 로 응답 코드 확인 — 각 단계에서 멈추면 해당 레이어 문제
- 연결 단계 기준: "Trying"에서 멈추면 방화벽/라우팅 문제, "SSL connection" 단계에서 실패하면 인증서/TLS 버전 문제, 200이면 정상, 502이면 Nginx는 살아있지만 Tomcat 연결 실패, connection refused이면 Nginx 자체 미기동
- "Connected to" 성공인데 응답 코드가 502이면 → Nginx와 Tomcat 사이 구간 문제 — ss -tlnp | grep 8080 으로 Tomcat이 8080에서 리스닝 중인지 확인 후 curl http://127.0.0.1:8080 으로 직접 Tomcat 테스트
Nginx access.log 실시간 확인
tail -f /var/log/nginx/access.log특정 경로 요청만 필터링
grep 'POST /api/login' /var/log/nginx/access.log | tail -20upstream 응답 시간 기준 정렬
awk '{print $NF, $7}' /var/log/nginx/access.log | sort -rn | head -205xx 에러만 추출
grep ' 5[0-9][0-9] ' /var/log/nginx/access.log | tail -30- Nginx access.log에서 먼저 5xx 에러 발생 upstream_addr 패턴 확인, 그 다음 upstream_response_time 값으로 지연 구간 파악 — 특정 upstream_addr 집중이면 단일 서버 장애, 전체 upstream에 502이면 네트워크/방화벽 문제
- upstream_response_time 기준: 1초 미만=정상, 5~10초=WAS 처리 지연(DB 슬로우쿼리 의심), 30초 근접=proxy_read_timeout 초과 직전(504로 이어질 수 있음) — 30초 근접이 반복되면 Tomcat 스레드 덤프로 원인 추적
- 502가 특정 upstream_addr에 집중되는데 해당 Tomcat에 직접 curl은 성공하면 → Nginx→Tomcat 구간 방화벽 규칙 또는 keepalive 연결 문제 — error.log에서 해당 upstream_addr의 "connect() failed" 메시지 확인
장애 시나리오 — 새벽 2시의 502 추적
심화 — 요청은 '정확히 한 번'이 아닐 수 있다
심화: 프록시 재시도와 멱등성 — 타임아웃은 실패가 아니라 중복을 만든다
지금까지 요청 흐름을 '한 번 흘러가는 파이프라인'으로 봤습니다. 하지만 흐름 곳곳의 타임아웃·재시도 때문에, 같은 요청이 백엔드에서 두 번 실행될 수 있습니다. 이걸 모르면 새벽의 '결제 이중 승인' 같은 사고의 원인을 흐름도 어디에서도 못 찾습니다.
- Nginx는 재시도할 수 있습니다: upstream 하나가 연결 실패하거나 응답이 늦으면, Nginx는
proxy_next_upstream정책에 따라 다음 upstream(다른 WAS)으로 요청을 다시 보낼 수 있습니다. GET처럼 멱등한 요청은 두 번 실행돼도 결과가 같아 안전합니다. - 문제는 비멱등 요청입니다: POST 결제·주문 생성은 백엔드가 첫 요청을 이미 처리했는데 응답만 늦은 상태일 수 있습니다. 이때 Nginx가
proxy_read_timeout을 근거로 '실패'라 판단해 다른 WAS에 같은 POST를 재전송하면, 결제가 두 번 일어납니다. 사용자의 '재시도' 클릭까지 겹치면 중복 경로가 둘이 됩니다. - 그래서 방어는 세 층위입니다: ① 앱단 — 멱등성 키(주문ID 기반
Idempotency-Key)로 같은 키의 요청은 한 번만 반영하고, 두 번째는 첫 결과를 그대로 돌려줍니다(가장 확실한 방어). ② Nginx단 — 비멱등 경로는proxy_next_upstream에서 timeout을 빼거나 아예 재시도하지 않게 명시. ③ 타임아웃 정합 — 계층별 타임아웃을 '백엔드 실제 처리시간 < 앞단 타임아웃'이 되도록 백엔드→Nginx→LB→클라이언트 순으로 맞춰 '끝났는데 타임아웃'을 애초에 줄입니다.
핵심 한 줄: 타임아웃·재시도는 실패를 감추는 장치가 아니라 중복을 만들 수 있는 장치입니다. 그래서 돈이 걸린 비멱등 연산은 반드시 앱단 멱등성으로 지켜야 하고, 인프라(재시도·타임아웃)만으로는 '정확히 한 번'을 보장할 수 없습니다.
상황: 결제 API가 간헐적으로 느려지는 시간대에, 사용자는 '결제 실패/시간 초과' 화면을 보고 다시 시도합니다. 그런데 정산 내역을 보면 같은 주문에 승인이 두 건 잡혀 있습니다. Tomcat·DB는 정상이고 에러 로그에도 뚜렷한 예외가 없습니다.
원인: 요청이 두 번 실행됐습니다. 경로가 겹칩니다 — ① 백엔드는 결제를 이미 처리했지만 응답이 Nginx proxy_read_timeout을 넘겨, Nginx가 upstream 실패로 보고 다음 WAS에 같은 POST를 재전송(proxy_next_upstream). ② 사용자가 실패 화면을 보고 '재시도'를 눌러 또 한 번. 어느 쪽이든 근본은 '비멱등 POST가 한 번만 실행된다는 보장이 없는데 아무도 그 보장을 하지 않은' 것입니다. Tomcat이 살아 있는 것과 '중복 실행이 없다'는 것은 별개입니다.
진단: Nginx access_log에서 그 주문의 결제 요청이 서로 다른 $upstream_addr로 두 번 찍혔는지(재시도 흔적), 또는 같은 클라이언트가 짧은 간격으로 두 번 POST했는지 봅니다. 문제 요청의 $upstream_response_time이 proxy_read_timeout 값 근처에서 잘렸는지 확인하고, catalina.out·DB에서 해당 결제가 두 트랜잭션으로 커밋됐는지 대조하면 확정됩니다.
해결: 근본 해결은 앱단 멱등성 키입니다 — 주문ID 기반 Idempotency-Key로 같은 키의 결제는 최초 한 번만 반영하고, 재전송·재시도는 첫 결과를 그대로 반환합니다. 인프라 측 완화로 Nginx는 비멱등 결제 경로에서 재시도하지 않도록 proxy_next_upstream을 조정하고, 타임아웃 계층을 '백엔드 처리시간 < 앞단 타임아웃'으로 정합해 '끝났는데 타임아웃'을 줄입니다(Nginx 리버스 프록시와 로드밸런싱 설정).
장애 대응 루틴 — 어느 구간인지 5분 안에 좁히는 방법
새벽 2시에 결제 API 오류 알림이 오면 다음 순서로 확인합니다.
1단계 — 구간 범위 파악 (1분)
외부에서 접근이 아예 안 되는지, 내부에서는 되는지 확인합니다. 외부만 안 되면 LB 또는 WAF 구간입니다. 내부에서도 안 되면 Nginx 이하 구간입니다.
2단계 — Nginx 로그 확인 (1분)
tail -50 /var/log/nginx/access.log
tail -50 /var/log/nginx/error.log
502 에러가 보이고 upstream이 언급되면 Nginx-Tomcat 구간입니다. 에러가 없으면 Nginx까지는 정상.
3단계 — Tomcat 직접 연결 확인 (1분)
curl -v http://WAS_IP:8080/health
이 명령이 성공하면 Nginx 설정 문제입니다. 실패하면 Tomcat 자체 또는 그 이하(DB, 커넥션 풀)를 확인합니다.
4단계 — 로그 수집 및 에스컬레이션 (2분)
각 계층 로그를 확인하고 개발팀에 공유합니다.
# Tomcat 에러 로그
tail -100 /opt/tomcat/logs/catalina.out | grep -E 'ERROR|Exception'
# DB 연결 상태 (DBA에게 확인 요청)
# slow query log, connection count 등
"어느 구간이 문제인가"를 먼저 좁혀야 복구 방향이 결정됩니다. 로그도 안 보고 Tomcat부터 재시작하는 것은 원인을 모르는 채로 증거를 지우는 행위입니다.
명령어·단축키 빠른 참조
이 모듈에서 다룬 요청 흐름 추적·장애 구간 좁히기 명령을 실전 옵션과 함께 모았습니다. "예" 열의 조합을 그대로 써도 됩니다.
| 명령어/단축키 | 용도 | 자주 쓰는 예 |
|---|---|---|
curl -v https://… | DNS→TCP→TLS→응답 단계별 추적 | Trying/Connected to/< HTTP 로 멈춘 지점 파악 |
nslookup example.com | 도메인→IP 해석 확인 | 공인 DNS 직접: nslookup example.com 8.8.8.8 |
dig +trace example.com | 루트→권한 DNS 위임 경로·TTL | 짧은 조회는 dig +short |
tail -f /var/log/nginx/access.log | 요청·상태코드·upstream 실시간 | 5xx만: grep ' 5[0-9][0-9] ' access.log |
awk '{print $NF,$7}' access.log | sort -rn | 응답 느린 요청 상위 추출 | $upstream_response_time 기준 지연 구간 파악 |
ss -tlnp | grep 8080 | Tomcat 리스닝 확인 | 프로세스는 ss -tlnp | grep java |
grep -r proxy_pass /etc/nginx/ | 프록시 대상 설정 확인 | 경로별 upstream 오지정 여부 |
curl -v http://WAS_IP:8080/health | Nginx 우회 백엔드 직접 테스트 | 성공=Nginx 설정 문제, 실패=WAS 이하 |
firewall-cmd --list-all | 내부 방화벽 규칙 확인 | iptables는 iptables -L -n | grep 8080 |
systemd-resolve --flush-caches | Linux DNS 캐시 초기화 | Win ipconfig /flushdns, mac dscacheutil -flushcache |
grep example.com /etc/hosts | 로컬 강제 해석 항목 확인 | 잘못된 항목이 엉뚱한 서버로 보냄 |
관련 모듈로 더 깊이:
- Nginx 리버스 프록시와 로드밸런싱 설정 — 요청 흐름의 핵심 길목인 Nginx 리버스 프록시 구간을 직접 설정하는 법
- L4/L7 로드밸런서와 VIP 기반 이중화 구성 — 흐름 앞단의 L4/L7 LB 구간이 트래픽을 어떻게 분배하는지
- HTTP 에러 코드 해석과 장애 원인 추적 — 어느 구간이 문제인지 로그와 에러 코드로 좁혀가는 진단 실무
다음 모듈에서는 Nginx 설정의 핵심 — 가상 호스트, 리버스 프록시, SSL 설정을 실습합니다.