摘要:docker run 的 --network 参数决定容器要不要新建 NET namespace、以及怎么接入宿主机。本节拆解 bridge、host、none、container 四种模式的设备拓扑与数据路径,给出验证命令与选型依据。
四种网络模式看着繁杂,其实只是同一组内核机制的不同组合。把选择过程拆成三道题就清楚了:要不要新建独立的网络栈?不要,就是 host 模式,直接借宿主机的;要,接着问第二道——这个网络栈里放不放设备与地址?什么都不放,就是 none 模式,只剩一个回环;放,就是 bridge 或自定义网络,由 Docker 造 veth、分地址、配路由。第三道题:独立网络栈是自己新建,还是复用另一个容器的?复用即 container 模式,两容器同吃一锅饭。三道题答完,模式自然确定,不需要死记参数表。下面按使用频率逐个展开。
不指定 --network 时容器落在 bridge 模式。它的物理结构分三件:
docker0 网桥。宿主机上一个虚拟二层设备,配网段通常是 172.17.0.0/16,自己占 172.17.0.1,充当这条内网街的网关。
veth pair。一对虚拟网线,从任何一端塞进去的包会从另一端出来。容器创建时 Docker 建一对:一端放进容器的 NET namespace 成为 eth0,另一端留在宿主机并挂到 docker0 上。容器与外界的一切流量都走这根线。
iptables NAT 规则。容器访问外网时做源地址转换(SNAT,把 172.17.0.2 换成宿主机 IP),外部访问映射端口时做目标地址转换(DNAT,4.2 细讲)。
亲手摸一遍:
docker run -d --name net-demo nginx:alpine docker exec net-demo ip addr
容器里 eth0 的地址形如 172.17.0.2。同时在宿主机上能看到线的另一端:
ip link | grep veth
输出一串 vethxxxx@ifY 设备——每跑一个 bridge 容器,宿主机就多一根这样的线。容器里的默认路由指向 172.17.0.1,即 docker0:
docker exec net-demo ip route # default via 172.17.0.1 dev eth0
bridge 模式的隔离性最好、最通用,代价是 NAT 带来的一点点性能损耗与端口映射的心智负担。绝大多数服务用它。
docker run -d --network host --name host-demo nginx:alpine
host 模式不新建 NET namespace,容器进程直接挂进宿主机的网络栈:容器里看到的网卡就是宿主机的网卡,bind 80 就是占宿主机的 80。两个好处:性能零损耗(无 NAT、无 veth、无协议栈切换);无需端口映射。两个代价:端口空间与宿主机共用,起第二个 bind 80 的 host 容器直接失败;隔离性归零,容器能监听宿主机所有接口。
验证模式:
docker exec host-demo ip addr
输出与宿主机 ip addr 完全一致——因为它就是同一套网络栈。适用场景很窄:高吞吐网关类组件、性能实测基线、需要监听宿主机端口的诊断工具。
docker run -it --network none alpine sh
容器里只有一个 lo 回环设备,无路由、无 DNS。看似无用,实际两类任务靠它:安全要求极高的离线处理(密钥运算、敏感数据脱敏),以及需要完全自控网络栈的场景(自己在容器里建 wireguard 之类隧道)。
docker run -d --name web nginx:alpine docker run -d --network container:web --name sidecar alpine sleep 3600
sidecar 不新建 NET namespace,直接加入 web 的。两个容器同 IP、同端口空间、可通过 lo 互相访问。一个直接推论:sidecar 里起的服务 bind 80 会与 web 的 80 冲突,共享前要规划好端口。这个模式是 Kubernetes Pod 多容器共址的原理前身——Pod 里的容器就是这样共享一个网络栈、彼此 localhost 直达。
| 模式 | NET ns | IP | 性能 | 适用 |
|---|---|---|---|---|
| bridge | 新建 | 容器私有 IP | 有 NAT 损耗 | 默认选择,绝大多数服务 |
| host | 共享宿主机 | 宿主机 IP | 零损耗 | 网关、高吞吐、性能敏感 |
| none | 新建但空 | 仅 lo | 无网络 | 离线任务、安全隔离 |
| container | 共享另一容器 | 同宿主容器 | 同容器间零损耗 | sidecar 共址 |
⚠️ 一个高频困惑先在这里拆掉:bridge 模式的容器访问宿主机上的服务,该用什么地址?容器内的 172.17.0.1 是 docker0 在宿主机一侧的地址,所以宿主机服务只要监听在 0.0.0.0 或明确包含 docker0 接口,容器用 172.17.0.1 就能访问到;宿主机服务若只监听 127.0.0.1,容器是够不着的。反过来,宿主机访问容器直接用容器 IP 即可,同宿主的 bridge 容器彼此也是 IP 直连可达(默认网桥上被 iptables 拦截的其实主要是跨网段情形,同网段互通)。
网络模式的选择题,本质是"要不要新建 NET namespace、新建了接到哪"这两问。把 namespace 概念吃透,四种模式不需要背。
下一节把 bridge 模式的两件日常事讲透:对外端口映射、对内按名互访。