docker network create로 브리지 네트워크를 routed 모드로 만들고, 라우터에 그 컨테이너 서브넷으로 가는 정적 경로까지 넣었다고 하자. NAT를 껐으니 이제 외부 호스트에서 컨테이너 IP로 바로 붙을 수 있을까? 실제로는 대부분 여기서 한 번 더 막힌다. Docker Engine 공식 문서를 보면 "라우팅이 가능하다"와 "실제로 뚫려 있다"는 서로 다른 조건이고, 그 사이에 opt-in 스위치가 하나 더 있다.
Docker 브리지 네트워크의 외부 접근성은 NAT 온/오프라는 단일 스위치가 아니라 두 개의 독립적인 축으로 결정된다. 하나는 마스커레이딩/DNAT 축(나가는 패킷의 소스 주소를 바꾸는지, 들어오는 패킷을 컨테이너로 포워딩하는지), 다른 하나는 direct-routing 허용 축(라우팅이 가능한 패킷을 실제로 통과시킬지)이다. 브리지 드라이버의 com.docker.network.bridge.gateway_mode_ipv4/_ipv6 옵션이 첫 번째 축을 4가지(nat/nat-unprotected/routed/isolated)로 결정하고, 두 번째 축은 별도의 opt-in 플래그가 결정한다.
기본값인 nat 모드에서는 나가는 패킷이 항상 마스커레이딩되고, 들어오는 패킷은 -p로 게시한 포트만 DNAT/PAT 대상이 된다. 네 모드를 비교하면 아래와 같다.
| 모드 | NAT/masquerade | 발신 소스주소 | 외부 도달 조건 |
|---|---|---|---|
| nat (기본) | 있음 | 호스트 주소로 치환 | 호스트 게시 주소로만 |
| nat-unprotected | 있음 | 호스트 주소로 치환 | 호스트 주소 + 필터 생략 |
| routed | 없음 | 컨테이너 자신 주소 | 라우팅 경로 + direct-routing 허용 필요 |
| isolated | - | - | 브리지에 주소 자체 없음(--internal) |
routed가 없애는 건 마스커레이딩/DNAT 규칙뿐이다. 나가는 패킷은 컨테이너 자신의 IP를 그대로 소스로 쓰고, 게시된 포트만 통과시키는 필터 규칙은 여전히 살아 있다. 그런데 라우팅 정보가 있어도 Docker는 한 번 더 잠금을 건다. opt-in 경로는 두 가지다.
--allow-direct-routing (daemon.json의 allow-direct-routing: true) — 모든 브리지 네트워크에서 게시된 포트에 대한 direct routing 허용.com.docker.network.bridge.trusted_host_interfaces — 지정한 인터페이스(예: eth3)로부터만 그 네트워크로의 direct routing 허용.공식 문서가 이 이중 잠금의 이유를 명시하진 않지만, 라우팅 테이블 항목 하나가 실수로든 의도적으로든 배포됐을 때 컨테이너가 곧바로 노출되지 않도록 하는 방어선으로 읽힌다 — 라우팅 가능성과 실제 통과 허용을 분리해 둔 셈이다. 같은 nat 테이블 기반 DNAT 메커니즘은 kube-proxy의 iptables 모드 ClusterIP 라우팅에서도 conntrack과 함께 등장한다.
# routed 모드 네트워크 생성 + 정적 라우트는 있다고 가정
docker network create --subnet 203.0.113.0/24 \
-o com.docker.network.bridge.gateway_mode_ipv4=routed routednet
docker run -d --network routednet -p 8081:80 --name c-routed nginx
# 실제 opt-in 여부 확인
docker network inspect routednet -f '{{json .Options}}'
sudo iptables -t nat -L -n -v # routednet 관련 규칙이 없어야 정상
라우팅 O, trusted_host_interfaces/allow-direct-routing X인 상태라면 패킷은 커널 라우팅 테이블을 타고 호스트까지 도착하지만, Docker가 만든 필터 규칙에서 걸러진다 — iptables -t nat -L에는 아무것도 안 보이니 "NAT 문제는 아닌데" 싶어지고, 정작 막고 있는 건 filter 테이블 쪽 규칙이라 원인을 한 단계 더 내려가서 봐야 한다.
NAT가 꺼진 것과 실제로 뚫려 있는 것은 다른 이야기다 — routed 모드는 첫 번째 잠금(마스커레이딩)만 풀 뿐, 두 번째 잠금(direct-routing opt-in)은 별도로 열어야 한다. 다음에 더 볼 만한 것은 iptables 대신 nftables 백엔드를 쓸 때 이 필터 규칙 구조가 어떻게 달라지는지, 그리고 단일 호스트 브리지 모델이 Swarm overlay(VXLAN) 같은 멀티호스트 네트워크와 어느 지점부터 갈라지는지다.
/engine/network/)/engine/network/port-publishing/)/engine/network/drivers/bridge/)