네트워크 장애를 볼 때 명령어 이름부터 외우면 출력은 많아지지만 원인은 잘 좁혀지지 않는다. 먼저 실패한 계층을 가정하고, 그 가정을 확인할 도구를 하나씩 고르는 편이 빠르다.
예를 들어 https://api.example.com/health가 열리지 않는다면 DNS, 경로, TCP 연결, TLS, HTTP, 로컬 프로세스 중 어디까지 정상인지 나눠 본다. ping 한 번의 성공이나 실패만으로 전체 서비스를 판정하지 않는다.
증상과 도구를 연결하면
| 확인할 것 | 우선 도구 | 답할 수 있는 질문 | 답하지 못하는 것 |
|---|---|---|---|
| 이름 해석 | dig |
어떤 DNS 서버가 어떤 레코드를 돌려주는가 | 그 IP의 서비스가 정상인가 |
| IP 도달성·RTT 표본 | ping |
ICMP Echo에 응답하는가 | TCP·HTTP가 정상인가 |
| 중간 홉 | traceroute |
TTL이 줄어들 때 어느 홉이 응답하는가 | 실제 애플리케이션 패킷과 완전히 같은 경로인가 |
| TCP 연결 | nc |
특정 호스트·포트에 TCP 연결이 성립하는가 | TLS·HTTP 응답이 올바른가 |
| TLS·HTTP | curl |
연결, 인증서 검증, 상태 코드, 헤더가 어떤가 | 서버 내부 처리의 근본 원인이 무엇인가 |
| 로컬 소켓 | ss |
어느 주소·포트를 어떤 프로세스가 듣는가 | 방화벽 바깥에서 접근 가능한가 |
| 라우팅 선택 | ip route get |
커널이 목적지에 어떤 경로·출발지 IP를 쓰는가 | 원격 구간 전체가 정상인가 |
명령은 운영체제와 배포판, 권한에 따라 옵션과 출력이 다르다. 아래 예시는 Linux를 중심으로 하고 macOS 차이를 따로 적었다.
1단계: DNS 응답을 확인한다
dig api.example.com A
dig api.example.com AAAA
dig api.example.com CNAME
기본 출력에서 최종 답뿐 아니라 질의한 서버, 응답 코드, TTL을 본다. 로컬 기본 resolver와 공개·사내 DNS의 결과가 다른지 비교할 때는 서버를 명시한다.
dig @10.0.0.2 api.example.com A
IP 주소를 이름으로 찾는 reverse lookup은 PTR 레코드가 있어야 한다.
dig -x 203.0.113.10
NXDOMAIN은 이름이 없다는 응답이고, timeout은 DNS 서버까지의 통신이나 방화벽 문제일 수 있다. 둘을 같은 “DNS 실패”로 묶지 않는다. dig의 옵션과 출력 필드는 ISC BIND의 dig 매뉴얼을 기준으로 확인할 수 있다. DNS 질의 흐름 자체는 DNS 재귀·반복 조회 과정에서 이어서 정리했다.
2단계: ICMP 도달성과 손실 표본을 본다
ping -c 4 203.0.113.10
ping -c 4 api.example.com
IP에는 응답하지만 이름에는 실패하면 DNS 쪽을 먼저 의심할 수 있다. 둘 다 실패하더라도 서버가 죽었다고 단정하지 않는다. 호스트나 중간 방화벽이 ICMP Echo를 차단하면서 TCP 443은 허용할 수 있기 때문이다.
ping이 보여 주는 왕복시간과 손실률도 짧은 측정 구간의 표본이다. 애플리케이션 트래픽과 우선순위·경로가 다를 수 있고, 응답 제한 때문에 손실처럼 보일 수도 있다. Linux ping 매뉴얼의 종료 코드와 옵션을 함께 확인한다.
3단계: 커널이 고른 경로와 중간 홉을 확인한다
Linux에서는 특정 목적지에 대한 라우팅 결정을 읽을 수 있다.
ip route get 203.0.113.10
출력의 gateway, device, source address가 예상과 맞는지 본다. VPN이나 여러 NIC가 있는 환경에서는 잘못된 출발지와 경로가 문제를 만든다.
경로 중 응답하는 홉을 보려면 다음을 사용한다.
traceroute api.example.com
traceroute -I api.example.com
기본 probe 방식과 -I의 ICMP 방식은 네트워크 정책에 따라 결과가 달라질 수 있다. 별표가 나온 홉이 반드시 장애 지점인 것도 아니다. 라우터가 TTL 만료 응답을 생략하면서 이후 홉은 정상적으로 응답할 수 있다. 왕복 경로가 비대칭이면 표시된 한 방향만으로 전체 흐름을 설명할 수도 없다. 자세한 원리는 traceroute와 hop의 의미와 traceroute 매뉴얼에서 확인할 수 있다.
4단계: TCP 포트가 실제로 열리는지 확인한다
원격 셸 접속용 telnet 대신 포트 연결 확인에는 nc를 쓰는 편이 목적이 분명하다.
nc -vz -w 3 api.example.com 443
- 성공: TCP handshake가 완료됐다는 뜻이다.
Connection refused: 대상까지 도달했지만 해당 포트에서 수신하지 않거나 거부했다.- timeout: 경로, 방화벽, 보안그룹, 응답 유실 등 후보가 여럿이다.
TCP 연결 성공은 TLS handshake나 HTTP 애플리케이션 정상까지 보장하지 않는다. 또한 nc 자체는 통신을 암호화하지 않는다. 운영 서비스에 임의 데이터를 전송하지 말고 연결 여부만 확인한다. 옵션은 운영체제 구현마다 다르므로 OpenBSD nc 매뉴얼 또는 설치된 man nc를 기준으로 한다.
5단계: curl로 TLS와 HTTP를 분리한다
curl --connect-timeout 3 --max-time 10 \
--verbose https://api.example.com/health
--verbose 출력에서는 다음 순서를 본다.
- 어느 IP로 연결을 시도했는가
- TCP 연결이 성립했는가
- TLS 인증서의 이름과 신뢰 체인이 검증됐는가
- HTTP 요청이 전송됐는가
- 상태 코드와 응답 헤더가 무엇인가
본문이 필요 없고 응답 헤더만 확인하려면 서버가 HEAD를 지원하는지 전제하고 --head를 쓸 수 있다.
curl --head --location https://www.example.com/
인증서 오류를 보기 싫다고 -k 또는 --insecure를 상시 사용하면 중요한 단서를 지우게 된다. 로컬 self-signed 실험이 아니라면 원인을 해결하는 것이 우선이다. --verbose에는 인증 헤더와 민감한 정보가 나타날 수 있으므로 공유 전 반드시 지운다. 옵션의 정확한 의미는 curl 공식 매뉴얼을 따른다.
6단계: 서버 안에서 listener를 확인한다
Linux에서는 netstat보다 iproute2의 ss가 일반적인 첫 선택이다.
ss -lntp
ss -nt state established
다음 차이를 확인한다.
127.0.0.1:8080: 로컬 loopback에서만 수신0.0.0.0:8080: 모든 IPv4 로컬 주소에서 수신[::]:8080: IPv6 wildcard이며 IPv4 동작은 시스템 설정에 따라 확인 필요
프로세스가 127.0.0.1에만 bind돼 있다면 외부 방화벽을 열어도 원격에서 접근할 수 없다. 반대로 listener가 있다고 container port, Kubernetes Service, host firewall, cloud security rule이 모두 정상이라는 뜻은 아니다. ss 필드는 ss 매뉴얼에서 확인한다.
macOS에는 기본적으로 ss와 Linux의 ip 명령이 없다. 로컬 listener는 다음처럼 확인할 수 있다.
lsof -nP -iTCP -sTCP:LISTEN
netstat -rn
route -n get 203.0.113.10
증상별 최소 진단 흐름
도메인만 접속되지 않는다
dig로 A·AAAA·CNAME 확인
→ 반환 IP가 기대와 같은지 확인
→ nc로 해당 IP:port 연결
→ curl로 Host/SNI를 포함한 TLS·HTTP 확인
서버에서는 정상인데 외부에서 안 된다
ss로 bind 주소와 listener 확인
→ 로컬 curl 확인
→ host firewall·container/Kubernetes 노출 확인
→ 외부 위치에서 nc와 curl 재확인
간헐적으로 느리다
한 번의 ping 평균보다 시간대별 DNS lookup, connect, TLS, first byte 시간을 분리한다.
curl --output /dev/null --silent --show-error \
--write-out 'dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n' \
https://api.example.com/health
여러 시점과 위치에서 측정하고 서버 로그·metric의 같은 timestamp와 맞춘다. 한 번의 숫자로 원인을 단정하지 않는다.
기록해야 재현할 수 있다
장애 메모에는 명령어 출력 전체보다 판단에 필요한 조건을 남긴다.
- 측정 시각과 timezone
- 실행 위치와 source IP, VPN 여부
- 대상 hostname·IP·port
- DNS server와 응답 레코드
- 성공·실패 단계와 오류 원문
- 재시도 횟수와 간격
- 민감 정보를 제거한 핵심 출력
좋은 네트워크 진단은 도구를 많이 실행하는 일이 아니라, DNS에서 애플리케이션까지 경계를 하나씩 확인해 후보를 줄이는 일이다.
참고 자료
'배움과 성장 > 네트워크·보안' 카테고리의 다른 글
| 반송파란 무엇인가: 변조·복조와 디지털 통신까지 (0) | 2024.08.17 |
|---|---|
| SSH1과 SSH2 차이: 현대 OpenSSH에서 확인할 보안 기준 (0) | 2024.08.15 |
| DNS 조회 과정: 스텁 리졸버·재귀 리졸버·권한 서버의 역할 (0) | 2024.08.15 |
| URI·URL·URN 차이: Query·Fragment와 HTTP Safe Method까지 (0) | 2024.08.15 |
| SSH와 Telnet 차이: 원격 접속에서 Telnet을 운영에 쓰면 안 되는 이유 (0) | 2024.08.15 |
댓글