MTU
고친 사람 github-actions[bot]
MTU 는 네트워크 구간 하나가 한 번에 실어 나를 수 있는 데이터 크기의 상한을 정합니다. 이 상한을 넘는 덩어리는 그 구간을 통째로 지나가지 못합니다. 그래서 보내는 쪽은 데이터를 이 크기에 맞춰 잘라서 내보냅니다.
쉽고 빠른 이해
MTU 는 네트워크 구간 하나가 한 번에 받아 주는 최대 크기입니다. 흔한 유선 랜인 이더넷은 1500바이트입니다.
왜 상한을 두는지는 받는 장비 사정 때문입니다. 장비는 덩어리 하나를 통째로 담을 만큼의 메모리를 미리 잡아 둡니다. 게다가 덩어리가 오다가 깨지면 그 덩어리 전체를 다시 보내야 합니다. 크기를 무한정 키우면 두 값이 같이 커집니다.
어떻게 도는가:
- 보내는 쪽이 이 크기에 맞춰 데이터를 잘라 내보냅니다
- 크기를 넘긴 덩어리를 만난 장비는 잘게 나누거나 버립니다
- 버린 장비는 「너무 크다」는 알림을 보낸 쪽으로 돌려보냅니다
대가도 있습니다. 작게 잡으면 덩어리 수가 늘고, 덩어리마다 붙는 주소 정보의 몫도 같이 늘어납니다. 크게 잡으면 가는 길의 모든 구간이 그 크기를 받아 줘야 하고, 한 덩어리가 깨질 때 다시 보내는 양도 커집니다.
상세
짐을 높이 쌓은 트럭은 가는 길에 낮은 굴다리가 하나만 있어도 그 길을 지나가지 못합니다. 그래서 싣는 사람은 길에서 가장 낮은 굴다리의 높이부터 알아봅니다. 짐은 그 높이 아래로 낮춰 여러 번에 나눠 싣습니다.
MTU(Maximum Transmission Unit, 최대 전송 단위)는 한 링크가 한 번에 실어 나를 수 있는 데이터의 최대 크기입니다. 링크는 장비와 장비를 잇는 한 구간을 말합니다. 내 노트북과 공유기 사이가 링크 하나이고, 공유기와 통신사 장비 사이가 또 다른 링크 하나입니다. 이더넷으로 이어진 링크의 MTU 는 1500바이트입니다.
이 절은 먼저 MTU 가 무엇의 크기를 재는지 못 박고, 그런 상한이 왜 있는지를 봅니다. 이어서 링크마다 값이 다를 때 무엇이 기준이 되는지, 상한을 넘긴 데이터에 무슨 일이 생기는지를 봅니다. 마지막으로 보내는 쪽이 미리 크기를 맞추는 방법과, 상한을 키울 때 내주는 것을 봅니다.
MTU 가 재는 크기
MTU 가 재는 것은 링크 위를 흐르는 덩어리 전체가 아닙니다. 링크 계층이 배달을 위해 앞뒤에 덧붙이는 겉포장을 뺀 알맹이입니다. 이더넷은 이 알맹이를 프레임에 실어 나릅니다.
알맹이 하나는 보통 IP(Internet Protocol, 인터넷 프로토콜) 패킷 하나입니다. 그래서 이더넷에서 MTU 가 1500바이트라는 말은 IP 패킷 하나가 1500바이트까지 된다는 뜻입니다.
이 1500바이트 안에는 IP 가 붙이는 헤더도 들어갑니다. 헤더는 받는 쪽 주소처럼 배달에 필요한 정보를 덩어리 앞에 붙인 토막입니다. 헤더가 앞을 차지하므로 실제 데이터에 쓸 수 있는 몫은 그만큼 줄어듭니다.
한 패킷이 어떻게 나뉘는지를 그림으로 보면 이렇습니다.
block-beta columns 2 a["IP 헤더 20바이트"]:1 b["보낼 데이터 1480바이트"]:1 c["IP 패킷 1500바이트 · 이더넷의 MTU"]:2
헤더가 20바이트를 먼저 쓰고 남은 1480바이트가 데이터 몫입니다. 애플리케이션이 보기에 1480바이트를 보낸 것이지만 선 위로는 1500바이트가 흐르는 셈입니다.
상한이 있는 까닭
상한이 없다면 한 번에 아무리 큰 덩어리라도 보낼 수 있어 편할 것 같습니다. 그러지 못하는 이유가 셋 있습니다.
첫째는 받는 장비의 메모리입니다. 장비는 덩어리 하나를 통째로 담을 만큼의 메모리를 미리 잡아 둡니다. 상한이 없으면 얼마를 잡아 둬야 하는지 정할 수 없습니다.
둘째는 오류를 잡아내는 단위입니다. 선을 지나는 동안 값이 한 군데만 어긋나도 그 덩어리는 전부 버려지고 다시 보내집니다. 덩어리가 클수록 한 번 어긋날 때 다시 보내는 양이 커집니다.
셋째는 뒤에 선 덩어리의 대기입니다. 한 덩어리가 선을 쓰는 동안 뒤의 덩어리는 기다립니다. 큰 덩어리를 허용하면 뒤에 선 작은 요청이 그만큼 오래 기다립니다.
링크마다 다른 값과 경로 MTU
MTU 는 링크 규격이 정합니다. 그래서 가는 길의 구간마다 값이 다릅니다. 특히 터널을 지나는 구간은 겉포장이 한 겹 더 붙는 만큼 알맹이에 쓸 수 있는 크기가 줄어듭니다.
보내는 쪽에 실제로 걸리는 상한은 첫 구간의 MTU 가 아닙니다. 가는 길 전체에서 가장 작은 MTU 입니다. 이 값을 경로 MTU 라고 부릅니다.
flowchart TD
A["보내는 쪽"] --> B["구간 1 · MTU 1500"]
B --> C["구간 2 · MTU 1400"]
C --> D["구간 3 · MTU 1500"]
D --> E["받는 쪽"]
C -.-> F["경로 MTU 1400 · 가장 작은 값이 기준"]
가운데에 1400바이트짜리 구간이 하나 끼면 앞뒤가 아무리 넓어도 경로 MTU 는 1400바이트가 됩니다. 곤란한 대목은 보내는 쪽이 이 값을 처음부터 알 수 없다는 것입니다. 각 구간의 사정은 그 구간을 쥔 장비만 압니다.
상한을 넘긴 패킷의 처리
상한을 넘긴 패킷을 만난 라우터에게는 길이 둘입니다. 하나는 패킷을 작은 조각으로 나누어 보내는 것입니다. 이 나누는 일을 단편화라고 하고, 조각을 모아 원래 패킷으로 되돌리는 일을 재조립이라고 합니다. 재조립은 목적지가 합니다.
다른 하나는 나누지 않고 버리는 것입니다. 대신 버린 장비가 「너무 커서 못 보냈다」는 알림을 보낸 쪽으로 돌려줍니다. 이 알림을 나르는 것이 ICMP(Internet Control Message Protocol, 인터넷 제어 메시지 프로토콜)입니다.
둘 중 어느 길로 가는지는 IP 판본에 따라 갈립니다. IPv4에서는 중간 라우터도 패킷을 나눌 수 있습니다. IPv6에서는 중간 라우터가 나누지 않습니다. 나누는 일은 보내는 쪽만 하고 중간 라우터는 버린 뒤 알림만 돌려줍니다. 대신 IPv6 는 모든 링크가 1280바이트 이상의 MTU 를 갖기를 요구합니다.
단편화에는 값이 붙습니다. 조각 가운데 하나만 사라져도 목적지는 원래 패킷을 되살리지 못합니다. 조각 열 개 중 하나를 잃으면 열 개를 다시 받아야 합니다. 그래서 요즘 설계는 중간에서 나누기보다 처음부터 맞춰 보내는 쪽을 고릅니다.
상위 계층이 크기를 맞추는 방법
중간에서 나뉘는 것을 피하려면 보내는 쪽이 처음부터 경로 MTU 안에 들어가게 잘라야 합니다. 그 일을 어떻게 하는지는 프로토콜마다 다릅니다.
TCP(Transmission Control Protocol, 전송 제어 프로토콜)는 연결을 맺을 때 한 덩이에 실을 데이터의 최대 크기를 서로 알려 줍니다. 이 값이 MSS(Maximum Segment Size, 최대 세그먼트 크기)입니다. 이더넷이라면 MTU 1500바이트에서 IP 헤더 20바이트와 TCP 헤더 20바이트를 뺀 1460바이트가 됩니다.
UDP(User Datagram Protocol, 사용자 데이터그램 프로토콜)에는 이런 흥정이 없습니다. 애플리케이션이 한 번에 넘긴 만큼이 한 덩이가 됩니다. 그래서 UDP 로 큰 데이터를 보내는 프로그램은 자기가 직접 크기를 챙겨야 합니다.
경로 MTU 를 알아내는 일 자체에도 이름이 있습니다. 경로 MTU 탐색입니다. 나누지 말라는 표시를 붙여 패킷을 보내 보고, 너무 크다는 알림이 오면 크기를 줄이는 식으로 값을 좁혀 갑니다.
큰 응답만 사라지는 증상
경로 MTU 탐색은 알림이 돌아온다는 전제 위에 섭니다. 중간 장비가 보안 설정으로 ICMP 를 막아 버리면 그 전제가 깨집니다.
그러면 설명하기 어려운 증상이 납니다. 연결은 맺어지고 작은 요청은 오갑니다. 그런데 큰 응답이 오는 순간 통신이 멈춥니다. 보낸 쪽은 패킷이 버려진 줄도, 크기를 줄여야 하는 줄도 모른 채 같은 크기로 다시 보냅니다. 이것을 경로 MTU 블랙홀이라고 부릅니다.
백엔드에서 이 증상은 터널을 거치는 구간에서 자주 만납니다. 「짧은 응답은 오는데 목록 조회만 멈춘다」가 그 모습입니다.
MTU 를 키우는 점보 프레임
링크 규격이 정한 값이라고 해서 손댈 수 없는 것은 아닙니다. 장비와 설정이 받쳐 주면 인터페이스의 MTU 를 이더넷 규격이 정한 1500바이트보다 크게 잡을 수 있습니다. 이렇게 키운 프레임을 점보 프레임이라고 부릅니다.
키우면 같은 데이터를 보낼 때 덩어리 수가 줄고 덩어리마다 붙던 헤더도 그만큼 줄어듭니다. 데이터센터 안이나 저장 장치를 잇는 망처럼 구간을 한 팀이 다 쥐고 있는 곳에서 씁니다.
크기를 키우고 줄일 때 무엇이 오가는지를 잣대별로 보면 이렇습니다.
| 잣대 | MTU 가 작을 때 | MTU 가 클 때 |
|---|---|---|
| 데이터 대비 헤더의 몫 | 커진다 | 작아진다 |
| 한 덩어리가 깨졌을 때 다시 보내는 양 | 적다 | 많다 |
| 한 덩어리가 선을 붙잡고 있는 시간 | 짧다 | 길다 |
| 경로의 모든 구간이 받아 줘야 하나 | 아니다 | 그렇다 |
마지막 줄이 실무에서 제일 자주 발목을 잡습니다. 내 서버의 MTU 를 키워도 가는 길 어딘가가 그 크기를 못 받으면 그 구간에서 버려집니다.
관련 항목
MTU 가 크기를 재는 대상
패킷 · 프레임 · 데이터그램 · 세그먼트 · 페이로드 · 헤더 · 옥텟
MTU 를 넘긴 데이터가 거치는 처리 단계
단편화 · 재조립 · 경로 MTU 탐색 · MSS 클램핑 · MSS
MTU 값을 정하는 링크 규격과 설정
이더넷 · 와이파이 · 점보 프레임 · 터널링 · VPN · 링크 계층
MTU 위에 얹혀 이 크기에 맞추는 프로토콜
IP · IPv4 · IPv6 · TCP · UDP · ICMP · QUIC
MTU 가 어긋났을 때 나는 장애
PMTU 블랙홀 · 패킷 손실 · 헤드 오브 라인 블로킹 · 타임아웃
다른 이름: Maximum Transmission Unit · 최대 전송 단위