当你启动一个Docker容器时,它默认拥有一个独立的网络栈——这意味着它有自己的lo接口、路由表、ARP表以及防火墙规则。这个隔离能力由Linux网络命名空间(Network Namespace)提供。但容器终究需要对外通信,不同场景下对隔离级别、性能、跨主机组网的需求截然不同,于是Docker提供了三种原生网络驱动:bridge、host和overlay。本文不讨论“如何使用docker network create”,而是深入内核态,逐帧跟踪数据包的流转,揭示每种模式背后的设计权衡。
一、Bridge模式:默认的隔离与NAT
Bridge模式是Docker创建容器时默认挂载的网络方案。其核心组件是一个名为docker0的Linux网桥(bridge),以及为每个容器创建的一对veth设备(veth pair)。
1. 网络拓扑结构
在宿主机上,Docker守护进程会创建一个docker0网桥并分配一个私有IP子网(通常为172.17.0.0/16)。每创建一个容器,Docker会分配一个veth pair:一端(vethXXX)挂在docker0上,另一端(eth0)放入容器的网络命名空间。因此,同一宿主机内所有使用bridge网络的容器都连接到同一个二层广播域——docker0网桥。
2. 数据包从容器到外界的路径
当容器内的进程发送一个IP包,目标地址非本机网段时,数据包首先从容器的eth0发出,经过veth pair到达宿主机的vethXXX接口,然后被docker0网桥接收。网桥根据MAC地址表决定转发:如果目标MAC属于同一网桥下的另一个veth接口,则直接二层转发(容器间通信);如果目标MAC是网关(即docker0接口的MAC),则向上交给宿主机的网络协议栈。宿主机随即检查路由表,默认情况下会将流量转发至物理网卡(如eth0),但此时数据包的源IP是容器的私有IP(如172.17.0.2),外部网络无法直接路由回该地址。因此,Docker在iptables的POSTROUTING链中添加了MASQUERADE规则,对源自docker0网段的出站包做源地址转换(SNAT),把源IP替换为宿主机物理网卡的IP。回包到达宿主机后,由DNAT规则将目的IP还原为原容器IP,最终经过docker0和veth pair送回容器。
3. 端口映射的实现
docker run -p 8080:80 实际上在宿主机iptables的PREROUTING链中插入了一条DNAT规则:将宿主机8080端口入站流量重定向到容器172.17.0.2:80。回包在conntrack的协助下自动完成反向变换。
4. 限制与适用
Bridge模式的主要问题在于依赖NAT,导致端到端源IP不可见(对某些协议不友好),且所有容器间通信必须经过docker0网桥转发,存在轻微的性能损耗。但它最安全、隔离性最好,适合单机开发环境或不需要复杂网络策略的生产场景。
二、Host模式:共享网络命名空间
Host模式彻底放弃了网络隔离:容器直接使用宿主机的网络命名空间。这意味着容器内的进程看到的就是宿主机的eth0、lo等接口,监听端口直接占宿主机的IP。
1. 底层实现
当Docker创建容器时指定--network host,它不会为容器创建新的网络命名空间,也不会创建veth pair。容器进程的socket操作直接对应宿主机协议栈,因此不存在NAT、无端口映射、无网桥转发。数据包从创建到发出只需要经过协议栈和物理驱动,性能接近原生。
2. 数据包路径
例如,容器内运行Nginx监听80端口,请求到达宿主机eth0的80端口后,直接被宿主机协议栈解析,根据端口号分发给Nginx worker进程。容器内看到的IP地址、路由表完全与宿主机一致。第三方应用无需关心容器概念,就像运行在宿主机上一样。
3. 安全与冲突风险
最大的代价是安全:容器进程可以监听任何端口,也可能被宿主机或其它进程通过loopback直接访问。如果多个容器都绑定相同端口(如80),后启动的容器会因端口占用而失败。此外,容器内的进程可以执行iptables修改宿主机规则,带来严重风险。因此Host模式仅适用于对网络性能极度敏感、且容器行为完全受控的场景(如网络包采集、高性能代理)。
三、Overlay模式:跨主机的二层网络
当容器需要跨宿主机通信时(最常见的场景是Swarm或Kubernetes),Bridge模式无法扩展。Overlay网络通过VXLAN等隧道技术在宿主机之间创建虚拟二层网络,让容器感觉像是连接在同一台交换机上。
1. VXLAN封装与解封装
Docker内置的overlay驱动默认使用VXLAN(Virtual Extensible LAN)。每一台参与overlay网络的宿主机上会创建一个VTEP(VXLAN Tunnel Endpoint)接口,比如docker_gwbridge。VXLAN将虚拟二层帧封装在UDP数据包中(目的端口4789),外层IP头携带宿主机的真实IP。例如,容器A(IP 10.0.0.2)在主机1上,容器B(IP 10.0.0.3)在主机2上。当A向B发送ARP或者IP包时:
- 容器A的eth0(在host1的overlay namespace中)发出帧,到达VTEP接口。
- VTEP查询本地的FDB表(Forwarding Database),发现目标MAC对应的远程VTEP是host2,于是给数据包加上VXLAN头(VNI=overlay网络的ID),再封装UDP/IP头(源IP=host1物理IP,目标IP=host2物理IP)。
- 封包通过物理网络到达host2,host2的VTEP收到UDP/4789数据包,剥离外层头和VXLAN头,得到原始二层帧,再通过docker_gwbridge或独立的overlay网桥转发给容器B的veth接口。
2. 控制平面:分布式键值存储
Docker Swarm模式下,overlay网络依赖consul、etcd或内置raft存储来同步VTEP信息、MAC地址与IP分配。每个节点上的Docker守护进程会维护一张本地转发数据库,确保单播流量不泛滥。
3. 性能与注意事项
VXLAN封装带来额外的CPU开销(约10%-30%的带宽损失)以及MTU问题——VXLAN头占用50字节(标准)或更多,因此物理MTU需要设置为1550或使用巨帧。Overlay网络适合跨宿主机关联微服务,但若需极高吞吐量,建议配合SR-IOV或MacVlan等直通模式。
四、三种模式对比与选型建议
| 模式 | 隔离级别 | 跨主机通信 | 性能 | 适用场景 |
|---|---|---|---|---|
| Bridge | 强(独立namespace) | 需外部代理或NAT | 中等(有veth+iptables) | 单机开发、小规模生产 |
| Host | 无隔离 | 直接使用宿主机IP | 接近原生 | 性能敏感型(抓包、防火墙) |
| Overlay | 强(虚拟二层) | 原生支持 | 有封装开销 | 跨主机关联容器(Swarm/K8s) |
选择时需明确首要约束:如果需要隔离且规模小,Bridge即可;跨主机编排必选Overlay;而Host只有在你能完全掌控容器行为、且对延迟有极致要求时才值得冒险。
五、验证与调试方法
理解原理后,动手验证能加深记忆:
- 用
ip netns list查看容器网络命名空间(注意Docker默认不显示名称,可通过docker inspect -f '{{.State.Pid}}'获得PID,再用nsenter -t PID -n进入)。 - 查看veth pair:
ip link show | grep veth,配合ethtool -S vethXXX查看统计。 - 对于overlay,在宿主机上
tcpdump -i any port 4789即可看到VXLAN封装包。
容器网络并非黑盒,理解这三类驱动后,面对复杂的SDN方案(Calico、Flannel、Weave)也能更快分析其设计思路。核心始终围绕内存隔离、虚拟设备转发与隧道技术展开。
延伸阅读
