네트워크 기초 · IP 와 라우팅 · 이론
MTU 와 경로 — 크기 때문에만 실패하는 통신
한 줄 요약
TCP 는 자기 인터페이스의 MTU 만 보고 세그먼트 크기를 정하므로 경로 중간의 좁은 구간을 알지 못하며, 그 사실을 알려 주는 ICMP 를 막으면 큰 패킷만 조용히 사라지는 블랙홀이 생긴다.
왜 이게 필요했나
증상이 유난히 특이한 장애가 있다.
- 헬스 체크 엔드포인트는 잘 응답한다.
- 목록 조회처럼 응답이 조금만 커지면 요청이 그대로 멈춘다. 오류도 아니고 그냥 멈춘다.
- ssh 는 접속되는데 큰 디렉터리에서
ls -la를 치면 화면이 얼어붙는다. - 어떤 사이트는 TLS 핸드셰이크 단계에서 멈춘다.
이 조합이면 다른 것을 볼 필요가 없다. 경로 MTU 문제다. 그리고 대개 VPN, 터널, 오버레이 네트워크를 새로 붙였거나 경로가 바뀐 직후에 나타난다.
어떻게 동작하나
MTU 는 한 인터페이스가 한 번에 내보낼 수 있는 IP 패킷의 최대 크기다. 이더넷 기본값은 1500바이트다. TCP 는 여기서 IPv4 헤더 20바이트와 TCP 헤더 20바이트를 빼 1460바이트를 MSS 로 정하고, 핸드셰이크의 SYN 패킷에 옵션으로 실어 상대에게 알린다.
결정적인 사실은 이것이다. 양쪽 모두 자기 인터페이스의 MTU 만 보고 MSS 를 정한다. 경로 중간에 더 좁은 구간이 있는지는 아무도 모른다. 두 호스트가 1460바이트를 주고받기로 합의해도, 중간에 1420바이트짜리 터널이 있으면 그 합의는 지켜질 수 없다.
작은 요청이 성공하는 이유가 여기 있다. 헬스 체크 응답은 세그먼트 하나로 끝나 좁은 구간을 무사히 통과한다. 임계값을 넘는 패킷만 사라지므로 증상이 크기에 따라 갈린다. TLS 핸드셰이크에서 멈추는 것도 같은 이유다. ClientHello 는 작지만 서버가 보내는 인증서 체인은 수천 바이트라 곧바로 최대 크기 세그먼트가 된다.
원래 TCP 에는 이 문제를 푸는 장치가 있다. 경로 MTU 탐색(PMTUD) 이다. 리눅스는 TCP 패킷에 DF(분할 금지) 비트를 세우고, 중간 라우터가 자기 다음 홉 MTU 보다 큰 DF 패킷을 받으면 버리면서 ICMP 타입 3 코드 4(Fragmentation Needed) 로 다음 홉 MTU 를 알려 준다. 송신자는 그 값을 캐시하고 세그먼트를 작게 잘라 다시 보낸다.
문제는 이 ICMP 를 막았을 때다. 송신자는 아무것도 모른 채 계속 큰 패킷을 보내고, 그 패킷은 계속 버려진다. 재전송도 같은 크기라 또 버려진다. 오류는 나지 않는다. 연결은 ESTABLISHED 로 남아 있고 애플리케이션은 자기 타임아웃에 걸려서야 끝난다. 이것이 PMTUD 블랙홀이다.
여기서 널리 퍼진 오해를 정정해야 한다. "보안을 위해 ICMP 는 전부 막는다"는 정책은 그 자체로 장애 요인이다. 막아도 되는 것은 echo request/reply 정도이고 타입 3 코드 4 는 반드시 통과시켜야 한다. IPv6 에서는 선택의 여지조차 없다. IPv6 라우터는 패킷을 분할하지 않으므로 ICMPv6 Packet Too Big(타입 2)이 필수다.
캡슐화가 가져가는 바이트를 외워 두면 진단이 빨라진다. VXLAN 50바이트(실효 MTU 1450), WireGuard 60~80바이트(1440~1420), GRE 24바이트(1476), IPsec ESP 는 최대 약 73바이트다.
현장에서 만나는 모습
쿠버네티스에서 이 문제는 오버레이 네트워크 때문에 정기적으로 재발한다. 노드 인터페이스가 1500인데 VXLAN 인터페이스와 파드의 eth0 도 1500 으로 잡혀 있으면 즉시 블랙홀이다. VXLAN 은 50바이트를 가져가므로 1450 이어야 한다. 증상은 특징적이다. 파드끼리 작은 요청은 되는데 큰 본문의 POST 만 멈추고, kubectl exec 는 되는데 로그가 많은 파드의 kubectl logs 가 중간에 멈춘다.
현실적인 해법은 터널 진입점에서 MSS 클램핑을 거는 것이다. 지나가는 SYN 패킷의 MSS 옵션 값을 라우터가 직접 낮춰 쓰므로 ICMP 에 전혀 의존하지 않는다. 다만 TCP 에만 적용되며 QUIC 같은 UDP 기반 프로토콜은 스스로 크기를 조절해야 한다.
이어지는 퀴즈에서 확인할 것
"작은 요청은 되는데 큰 응답만 멈춘다"는 증상에서 곧바로 MTU 를 떠올릴 수 있는지, 왜 오류 없이 멈추는지 설명할 수 있는지 확인한다.