증상: 연결은 되는데 큰 데이터에서 멈춘다

MTU/MSS 문제의 전형적인 증상은 "SSH 접속은 되는데 ls 출력이 긴 순간 멈춘다", "HTTP 헤더는 오가는데 큰 응답 바디에서 hang이 걸린다", "TLS handshake의 큰 Certificate 패킷에서 끊긴다" 같은 형태다. 작은 패킷은 통과하고 큰 패킷만 사라지기 때문에 애플리케이션 로그에는 원인이 남지 않고, 흔히 "가끔 느리다"로 오진된다. 근본 원인은 경로상 어느 구간의 MTU가 양 끝단보다 작은데, 그 사실을 알려주는 ICMP가 차단되어 Path MTU Discovery(PMTUD)가 깨지는 것이다.

왜 문제가 되는가: MTU, MSS, DF 비트

MTU는 링크가 한 번에 실어 나를 수 있는 IP 패킷 최대 크기다. 이더넷 기본은 1500이지만 PPPoE(1492), IPsec/GRE/VXLAN 같은 터널은 오버헤드만큼 작아진다. TCP는 handshake 때 MSS를 교환하는데, 보통 MSS = MTU - 40(IPv4/TCP 헤더)이다.

송신 측이 MTU 1500 기준으로 1460 MSS 패킷을 보내고, 경로 중간에 MTU 1400 터널이 있다면 그 라우터는 두 가지 중 하나를 한다. DF(Don't Fragment) 비트가 켜져 있으면 패킷을 버리고 ICMP Fragmentation Needed(Type 3, Code 4)로 "1400으로 줄여라"를 알린다. 이 ICMP가 방화벽에서 무조건 차단되면 송신 측은 계속 1460을 재전송하고 영원히 도달하지 못한다. 이것이 PMTUD 블랙홀이다.

진단: 어디서 잘리는지 특정한다

가장 빠른 확인은 DF 비트를 켠 상태로 ping 크기를 키워보는 것이다. 어느 크기부터 실패하는지가 경로 MTU다.

# Linux: DF 비트(-M do) 고정, 페이로드 크기를 바꿔가며 확인
# 1472 = 1500 - 20(IP) - 8(ICMP)
ping -M do -s 1472 10.0.0.10   # 성공하면 경로 MTU >= 1500
ping -M do -s 1372 10.0.0.10   # 1372 성공, 1472 실패면 경로 MTU ~1400

# macOS는 옵션이 다르다
ping -D -s 1372 10.0.0.10

# tracepath로 구간별 MTU 자동 탐지
tracepath 10.0.0.10

실패 크기를 찾았으면 실제 MTU = 페이로드 + 28이다. tcpdump로 ICMP Frag Needed가 실제로 돌아오는지, 아니면 삼켜지는지도 함께 봐야 한다.

# ICMP Type 3 Code 4(Fragmentation Needed)가 오는지 확인
tcpdump -ni any 'icmp and icmp[0] == 3 and icmp[1] == 4'

# 큰 패킷이 DF로 재전송만 반복되는지(블랙홀 징후)
tcpdump -ni eth0 'tcp port 443 and (tcp[13] & 0x02 != 0 or ip[6] & 0x40 != 0)'

해결책 비교

방법동작적용 위치주의점
ICMP 허용PMTUD 정상 복구방화벽/보안그룹근본 해결, 최우선
MSS ClampingSYN의 MSS를 강제 하향게이트웨이/라우터ICMP 못 열 때 차선책
인터페이스 MTU 조정출발부터 작게 전송호스트/터널 IF경로 전체가 일관해야

해결 1: ICMP를 열고 MSS Clamping을 건다

이상적인 해결은 ICMP Type 3 Code 4를 방화벽에서 허용하는 것이다. 정책상 열 수 없다면 게이트웨이에서 TCP SYN의 MSS를 경로 MTU에 맞게 깎는 MSS clamping이 실무 표준이다. 터널 장비에서 특히 중요하다.

# iptables: 나가는 SYN의 MSS를 경로 MTU에 맞춰 자동 조정
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
  -j TCPMSS --clamp-mss-to-pmtu

# 값을 직접 지정해야 하는 경우(예: MTU 1400 -> MSS 1360)
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
  -j TCPMSS --set-mss 1360

해결 2: 컨테이너/오버레이 환경

Kubernetes의 VXLAN 오버레이(예: Flannel, Calico VXLAN)는 노드 MTU에서 50바이트를 빼야 한다. CNI가 자동 계산하지만 언더레이가 점보프레임이 아니거나 이중 터널이면 수동 지정이 필요하다.

# Calico: VXLAN 오버헤드 반영한 파드 MTU 지정
apiVersion: v1
kind: ConfigMap
metadata:
  name: calico-config
  namespace: kube-system
data:
  # 노드 MTU 1500 - VXLAN 50 = 1450
  veth_mtu: "1450"

주의점

  • IPv6에는 라우터 단 fragmentation이 없어 PMTUD가 필수다. ICMPv6 Packet Too Big(Type 2)을 막으면 즉시 블랙홀이 된다.
  • MSS clamping은 TCP만 보정한다. UDP 기반(QUIC, WireGuard, DNS over UDP)은 clamping 대상이 아니므로 애플리케이션의 DF/PMTUD 처리나 MTU 자체를 맞춰야 한다.
  • 클라우드 환경은 인스턴스 타입·ENI마다 MTU가 다르다(9001 점보 vs 1500). 하이브리드 연결(VPN/Direct Connect) 구간이 1500 미만인 경우가 잦으니 양 끝단 MTU를 반드시 확인한다.
  • MTU를 무작정 낮추면 패킷당 오버헤드 비율이 커져 처리량이 떨어진다. 경로 MTU를 실측한 뒤 그 값에 정확히 맞추는 것이 원칙이다.