LabHub

네트워크 트러블슈팅 · 계층별로 좁히기 · 이론

안 된다는 신고를 네 층으로 쪼개기

LabHub 에서 이어서 보기

한 줄 요약

네트워크 장애 진단의 90%는 "어느 층에서 멈췄는가"를 먼저 정하는 것으로 끝난다. 층을 정하지 않고 도구를 켜면 시간만 흐른다.

왜 이게 필요했나

"API 가 안 붙어요" 라는 신고를 받으면 사람들은 보통 이렇게 한다. ping 을 쳐 보고, 방화벽 규칙을 열어 보고, DNS 를 확인하고, 애플리케이션 로그를 뒤진다. 순서가 없다. 그래서 같은 곳을 두 번 보고, 안 본 곳을 계속 안 본다.

숙련자는 다르게 한다. 먼저 층을 특정한다. 그리고 그 층에 맞는 도구 하나만 쓴다.

어떻게 동작하나

실무에서 쓰는 층은 OSI 7계층이 아니라 네 개면 충분하다.

| 층 | 질문 | 확인 도구 | 실패했을 때의 얼굴 |
| --- | --- | --- | --- |
| 이름 | 이 이름이 어떤 IP 로 풀리나 | getent hosts, dig, resolvectl query | Name or service not known |
| 도달 | 그 IP 까지 패킷이 가나 | ping, traceroute -U, ip route | Network is unreachable, 무응답 |
| 포트 | 그 IP 의 그 포트가 열려 있나 | nc -zv, ss -ltn, curl --connect-timeout | Connection refused / timed out |
| 응답 | 열렸는데 답이 이상한가 | curl -v, curl -w, 애플리케이션 로그 | 5xx, 느림, 잘못된 본문 |

핵심은 각 층의 실패가 서로 다른 얼굴을 한다는 것이다. 그 얼굴만 정확히 읽어도 층이 정해진다.

errno 별 판별표를 외워 두면 신고 문장만 보고도 층이 갈린다.

| 메시지 | errno | 실제로 일어난 일 | 먼저 볼 곳 |
| --- | --- | --- | --- |
| Name or service not known | EAI_NONAME | 이름 해석에서 실패. 소켓을 열지도 못했다 | nsswitch.conf, resolv.conf, /etc/hosts |
| Network is unreachable | ENETUNREACH | 라우팅 테이블에 경로가 없다 | ip route, 특히 IPv6 경로 |
| No route to host | EHOSTUNREACH | ICMP unreachable 수신 또는 ARP 무응답 | 대상 전원, ARP, REJECT 규칙 |
| Connection refused | ECONNREFUSED | 목적지가 RST 를 돌려줬다 | 대상 호스트의 프로세스와 바인딩 주소 |
| Connection timed out | ETIMEDOUT | SYN 을 다 재전송하도록 무응답 | 방화벽 DROP, 보안 그룹, accept 큐 |

여기서 가장 자주 틀리는 것이 refused 를 방화벽 탓으로 돌리는 것이다. refused 는 RST 를 받았다는 뜻이고, RST 를 받았다면 라우팅·보안그룹·NAT·네트워크 정책을 이미 다 통과했다는 증거다. 실무 방화벽은 거의 언제나 DROP 정책이라 막힌 포트는 timeout 으로 나타난다. refused 를 보고 방화벽을 뒤지는 것은 이미 통과한 관문을 다시 확인하는 일이다.

시간도 증거다. 리눅스 기본값(net.ipv4.tcp_syn_retries=6)에서 응답 없는 연결은 1+2+4+8+16+32+64 = 127초 만에 포기한다. 애플리케이션 타임아웃이 30초라고 적혀 있는데 실제로 127초 만에 실패했다면, 그 타임아웃 설정이 적용되지 않고 있다는 신호다.

현장에서 만나는 모습

"로컬에서는 되는데 원격에서만 안 돼요." 십중팔구 바인딩 주소 문제다. ss -ltnp 에서 127.0.0.1:8080 으로 리슨하고 있으면 파드 IP 로 오는 연결은 커널이 RST 로 거절한다 — refused 다. 반대로 파드 IP 로도 timeout 이면 그때부터 네트워크 정책을 본다. 이 한 번의 갈림이 조사 범위를 절반으로 줄인다.

다음에서 할 것

가장 아래 층인 이름 해석부터 시작한다. /etc/hostsnsswitch.conf 가 왜 dig 보다 먼저인지, 그리고 왜 dig 는 그 둘을 전혀 보지 않는지를 손으로 확인한다.