Docker 网络模式全景解析:bridge、host、overlay 与 macvlan 选型决策
一、为什么网络模式决定了容器边界
容器网络不仅决定“能不能访问”,还决定隔离级别、端口暴露方式、性能、服务发现和跨主机能力。默认 bridge 对本地开发很友好,但它通过 NAT 访问外部网络,且端口发布会占用宿主机端口。
Docker 的典型 bridge 路径是:
1 | |
容器内部的 eth0 与宿主机上的 veth 一端连接。Docker 创建 Linux bridge,把多个容器接入同一二层网络,再通过 iptables 完成端口发布和 SNAT。
二、bridge 模式
默认 bridge 可通过端口映射访问:
1 | |
更推荐创建自定义网络:
1 | |
自定义网络提供内置 DNS,容器可以通过服务名互访。默认 bridge 的自动服务发现能力和隔离行为不如自定义网络清晰。
三、host、none 与 container
host 模式不创建独立网络命名空间:
1 | |
容器直接使用宿主机网络栈,因此没有 -p 的意义,也会产生端口冲突。它适合高性能采集器、节点监控和需要绑定大量端口的程序,但隔离性较弱。
none 模式几乎没有网络:
1 | |
它适合离线构建、纯计算任务和对网络极度敏感的处理流程。需要注意,应用仍可能通过挂载的 Unix Socket、设备或共享目录与外界交互。
container 模式让一个容器复用另一个容器的网络命名空间:
1 | |
这类模式可用于 sidecar 调试,但两个容器共享 IP、端口和 localhost,配置冲突也会共同发生。
四、overlay 跨主机网络
overlay 需要 Docker Swarm 初始化:
1 | |
overlay 为跨主机服务提供虚拟网络,底层通常使用 VXLAN 封装。封装会增加报文开销,因此必须关注路径 MTU。云环境安全组还要放通节点之间所需的集群通信端口。
Overlay 更适合 Swarm。Kubernetes 通常由 CNI 插件提供网络,例如 Calico、Cilium 或 Flannel。不要把 Docker overlay 直接当作 Kubernetes 网络方案。
五、macvlan 与 ipvlan
macvlan 让容器拥有看起来像物理设备的独立 MAC 和 IP:
1 | |
1 | |
它适合遗留系统、网络设备和必须直接出现在局域网中的应用。但很多宿主机默认无法直接访问 macvlan 容器,需要额外创建 macvlan 子接口。大量容器也会增加交换网络中的 MAC 学习压力。
ipvlan 通常共享父接口的 MAC,适合 MAC 地址受限的环境。L2 模式强调二层通信,L3 模式则更接近路由。实际选择前应确认交换机、云网卡和上游网络是否支持。
六、DNS 服务发现与网络隔离
同一自定义网络中的容器可以使用容器名解析:
1 | |
如果容器需要同时加入多个网络,可以使用 alias:
1 | |
典型架构是:反向代理加入 frontend 和 backend,API 加入 backend,数据库只加入 backend。这样数据库不直接暴露给宿主机或外部网络。
七、实战:检查路由、端口和 MTU
1 | |
跨主机网络出现连接超时时,先检查:
- 容器 IP 是否正确;
- 服务是否监听
0.0.0.0而不是127.0.0.1; - 宿主机防火墙和云安全组;
- overlay 封装后的 MTU;
- DNS 是否解析到旧容器 IP。
应用层“偶尔超时”而小包正常、大包失败,常见原因就是 MTU 或路径分片。应在真实链路上测试,而不是只在本机 ping。
八、为什么 bridge 不一定是性能陷阱
NAT、iptables 和 veth 确实会带来开销,但在大多数 Web 服务中,瓶颈通常仍是应用、磁盘或数据库。只有高 PPS、低延迟、海量连接场景才值得评估 host、ipvlan、XDP 或专用 CNI。
对于 Kubernetes,Cilium 通过 eBPF 减少部分 iptables 路径,并提供网络策略、可观测性和服务负载均衡;Calico 则在路由和网络策略方面成熟。选择 CNI 应基于集群规模、云环境、策略需求和团队运维能力,而不是只看单项吞吐。
九、踩坑与决策树
| 场景 | 推荐 |
|---|---|
| 单机 Web 服务 | 自定义 bridge |
| 采集器、节点监控 | host |
| 离线任务 | none |
| sidecar 共享 localhost | container |
| Swarm 跨主机 | overlay |
| 局域网独立 IP | macvlan/ipvlan |
| Kubernetes | Cilium、Calico 等 CNI |
端口冲突只会发生在 host 或端口发布层面;容器之间通过自定义网络服务名通信时,不需要把每个内部端口发布到宿主机。