오후 2시, "API가 갑자기 안 된다"는 슬랙 메시지가 날아옵니다. 서버는 살아 있고 애플리케이션 로그에도 에러가 없습니다. 그런데 클라이언트에서는 계속 타임아웃입니다. ping은 되는데 실제 포트로 연결이 안 되고, 어느 구간에서 패킷이 사라지는지 알 수 없습니다. 이런 상황에서 ss로 소켓 상태를 읽고 tcpdump로 실제 패킷을 잡아볼 수 있다면 10분 안에 어디가 문제인지 특정할 수 있습니다.
네트워크 트러블슈팅 심화 — tcpdump와 패킷 분석
서버 로그에는 아무것도 없는데 연결이 안 된다. 애플리케이션은 멀쩡한데 응답이 느리다. 이런 상황에서 필요한 건 실제 패킷을 들여다보는 능력입니다. ss로 소켓 상태를 읽고, tcpdump로 패킷을 잡아서 무슨 일이 일어나는지 직접 확인하는 것이 이 모듈의 목표입니다.
네트워크 문제는 "안 된다"는 증상 하나로 시작합니다. 계층을 내려가면서 어디서 막혔는지 좁혀가는 것이 핵심입니다.
- 1TCP 상태 머신 — SYN_SENT/ESTABLISHED/TIME_WAIT 상태가 말하는 것
- 2ss -tnp — 소켓 상태, Recv-Q/Send-Q, 프로세스 매핑
- 3tcpdump 기초 — 필터 문법과 실시간 캡처
- 4tcpdump → Wireshark 파일로 저장해 심층 분석
- 5레이어별 진단 플로우 — 어디서 막혔는지 5분 안에 특정
- 6실제 장애 패턴 — SYN 타임아웃, TIME_WAIT 고갈, 비대칭 라우팅
TCP 상태 머신 — ss 출력으로 지금 무슨 일이 일어나는지 읽기
# 실행 중인 TCP 연결 전체 확인 (프로세스 포함)
ss -tnp
# LISTEN 중인 포트만 확인
ss -tnlp
# 특정 포트 필터
ss -tnp '( dport = :443 or sport = :443 )'
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:80 0.0.0.0:* nginx
ESTABLISHED 0 0 10.0.0.1:80 10.0.0.5:54321 nginx
SYN-SENT 0 1 10.0.0.1:45123 10.0.0.9:5432 python
TIME_WAIT 0 0 10.0.0.1:80 10.0.0.7:39201 -
상태별 진단 의미:
| 상태 | 의미 | 다량 발생 시 의심 |
|---|---|---|
SYN-SENT | 연결 시도 중, 응답 대기 | 대상 서버 방화벽 차단 또는 프로세스 없음 |
SYN-RECV | 서버가 SYN 받음, ACK 대기 | SYN Flood 공격 가능성 |
ESTABLISHED | 정상 연결 | 과다 시 Connection Pool 미사용 |
TIME_WAIT | 종료 후 대기(2MSL) | 단기 연결 반복 → 포트 고갈 위험 |
CLOSE-WAIT | 원격이 먼저 종료했는데 로컬이 안 닫음 | 애플리케이션 코드 버그 (소켓 미닫힘) |
Recv-Q / Send-Q 읽기:
# LISTEN 소켓의 Recv-Q = 수락 대기 중인 연결 큐 크기
# ESTABLISHED 소켓의 Recv-Q = 앱이 아직 읽지 않은 데이터 (바이트)
# ESTABLISHED 소켓의 Send-Q = ACK 못 받은 전송 대기 데이터 (바이트)
# Recv-Q 지속적으로 높음 → 앱 처리 속도 부족 (백프레셔)
# Send-Q 지속적으로 높음 → 네트워크 혼잡 또는 수신 측 처리 지연
tcpdump — 패킷을 직접 잡아서 보기
# 기본 문법
tcpdump [옵션] [필터 표현식]
# 주요 옵션
-i eth0 # 인터페이스 지정 (-i any: 모든 인터페이스)
-n # DNS 역조회 비활성화 (속도 향상)
-v/-vv # 상세 출력 (패킷 헤더 포함)
-c 100 # 패킷 100개 캡처 후 종료
-w dump.pcap # 파일로 저장 (Wireshark로 분석 가능)
-r dump.pcap # 저장된 파일 읽기
# 자주 쓰는 필터 조합
sudo tcpdump -i eth0 -n 'tcp port 80'
sudo tcpdump -i eth0 -n 'host 192.168.1.10'
sudo tcpdump -i eth0 -n 'tcp port 5432 and host 10.0.0.9'
sudo tcpdump -i eth0 -n 'tcp[tcpflags] & (tcp-syn) != 0' # SYN 패킷만
출력 읽기:
14:23:01.123456 IP 10.0.0.5.54321 > 10.0.0.1.80: Flags [S], seq 1234567
14:23:01.123478 IP 10.0.0.1.80 > 10.0.0.5.54321: Flags [S.], seq 9876543, ack 1234568
14:23:01.123490 IP 10.0.0.5.54321 > 10.0.0.1.80: Flags [.], ack 9876544
| Flags | 의미 |
|---|---|
[S] | SYN (연결 시작) |
[S.] | SYN-ACK (연결 응답) |
[.] | ACK |
[P.] | PSH+ACK (데이터 전송) |
[F.] | FIN (연결 종료 시작) |
[R] | RST (연결 강제 초기화) |
RST가 보이면: 서버가 연결 요청을 거부했거나, 이미 닫힌 포트에 패킷이 도달한 것입니다.
레이어별 진단 플로우
"연결이 안 된다"
│
├─ ping 대상서버IP
│ ├─ 실패 → L3 문제 (라우팅, 방화벽 ICMP 차단)
│ └─ 성공 ↓
│
├─ telnet 대상IP 포트 (또는 nc -zv 대상IP 포트)
│ ├─ 실패 → L4 문제 → 아래 확인
│ │ ├─ 대상서버: ss -tnlp | grep <포트> (프로세스 LISTEN 중인가?)
│ │ └─ 대상서버: iptables -L -n | grep <포트> (방화벽 차단인가?)
│ └─ 성공 ↓
│
├─ curl -v http://대상IP (또는 애플리케이션 직접 요청)
│ ├─ 실패 → L7 문제 (앱 설정, 인증, SSL)
│ └─ 느림 → tcpdump로 RTT 분석
│
└─ tcpdump -i eth0 -n 'host 대상IP and tcp port <포트>'
├─ SYN 후 응답 없음 → 방화벽이 패킷 DROP
├─ SYN → RST → 포트 닫힘 또는 프로세스 없음
└─ 정상 handshake 후 느림 → 앱 레이어 문제
SYN 타임아웃 — 방화벽이 패킷을 DROP하는 경우
# 증상: nc -zv 10.0.0.9 5432 → Connection timed out (즉시 Refused 아님)
# "timed out"은 패킷이 사라졌다는 뜻. "refused"는 RST를 받았다는 뜻.
# 제어 노드에서 tcpdump (SYN 패킷이 전송되는지 확인)
sudo tcpdump -i eth0 -n 'host 10.0.0.9 and tcp port 5432'
# → SYN 보냄 [S] 보이지만 [S.] 응답 없음 → 패킷이 대상 서버에 안 도달하거나 DROP됨
# 대상 서버에서 확인
sudo tcpdump -i any -n 'tcp port 5432' # 패킷이 도달하는가?
sudo iptables -L INPUT -n -v | grep 5432 # 방화벽 DROP 규칙 있는가?
sudo ss -tnlp | grep 5432 # 프로세스가 LISTEN 중인가?
방화벽 DROP vs REJECT 차이:
DROP: 패킷을 조용히 버림 → 클라이언트는 타임아웃까지 기다림REJECT: RST 응답을 즉시 보냄 → 클라이언트는 즉시 "Connection refused" 받음
소켓 누수 — 애플리케이션이 연결을 닫지 않는 버그
# 증상 확인
ss -tnp | grep CLOSE-WAIT | wc -l # 개수 확인
ss -tnp | grep CLOSE-WAIT # 어떤 프로세스인지 확인
# CLOSE-WAIT의 의미:
# 원격 피어가 FIN을 보내 먼저 연결 종료를 요청했지만
# 로컬 애플리케이션이 소켓을 close()하지 않아 연결이 남아있는 상태
# 원인: 데이터베이스/HTTP 클라이언트에서 connection.close() 누락
# 특히 예외 처리 경로(try/except)에서 close가 호출되지 않는 케이스
# 임시 조치: 해당 프로세스 재시작
# 근본 조치: 코드에서 finally: conn.close() 또는 with 구문 사용 확인
실무에서 tcpdump가 쓰이는 순간
API 간헐적 지연 — "로그는 정상인데 왜 느리지?" 케이스:
# 1단계: 어느 구간에서 지연이 발생하는지 tcpdump로 확인
sudo tcpdump -i eth0 -n -w /tmp/api-trace.pcap 'tcp port 8080' &
# API 요청 재현
kill %1
# 2단계: RTT 계산 (SYN → SYN-ACK 시간이 정상인가?)
tcpdump -r /tmp/api-trace.pcap -n -tt | head -20
# 3단계: Wireshark에서 pcap 파일 열어 TCP Stream Follow
# → "Time between request and response" 확인
# 자주 발견되는 패턴:
# SYN → SYN-ACK: 0.001ms (네트워크는 정상)
# Request → Response: 800ms (앱 처리 지연)
# → DB 쿼리 슬로우 로그 확인으로 전환
온콜 대응 체크리스트:
1. ss -tnp | wc -l → 연결 수 급증?
2. ss -tnp | grep -c TIME_WAIT → TIME_WAIT 고갈?
3. ss -tnp | grep -c CLOSE_WAIT → 소켓 누수?
4. ss -tnlp → 포트 정상 LISTEN?
5. tcpdump -i any -c 50 -n ... → 패킷 실제 도달?