Docker网络这5种模式,你真的都搞明白了吗?
很多人用Docker用了一两年,docker run -p 8080:80一把梭,容器起来能访问就行。但一旦碰到容器互通、跨主机通信、性能调优这些场景,就开始抓瞎了——为什么我的容器ping不通另一个容器?为什么换了host模式性能就上去了?overlay到底怎么跨主机的?macvlan和bridge有啥本质区别?
Docker一共有5种网络模式:bridge、host、none、overlay、macvlan。每种背后的实现原理完全不同,适用场景也大相径庭。今天把这5种掰开了揉碎了讲清楚。
Bridge模式:默认的,也是最容易被误解的
你不指定任何网络参数启动容器,用的就是bridge模式。Docker安装完之后会在宿主机上创建一个叫docker0的虚拟网桥,默认网段是172.17.0.0/16。
底层发生了什么?每启动一个容器,Docker会:
- 创建一对veth pair(虚拟以太网设备对),就像一根虚拟网线的两头
- 一头插进容器的网络命名空间里,变成容器的
eth0 - 另一头挂在宿主机的
docker0网桥上 - 给容器分配一个
172.17.0.x的IP - 在宿主机的iptables里加一条NAT规则,把外部流量转发给容器
你可以自己验证一下:
# 启动一个容器dockerrun-d--nametestnginx# 在宿主机上看veth设备iplinkshowtypeveth# 你会看到类似 veth3a8f9b2@if7 这样的设备# 看docker0网桥上挂了哪些设备brctl show docker0# bridge name bridge id STP enabled interfaces# docker0 8000.024216a5b5e5 no veth3a8f9b2# 看iptables的NAT规则iptables-tnat-L-n|grep172.17# MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0# DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.2:80默认bridge的坑:Docker自带的那个bridge(就是docker0)不支持容器间DNS发现。你用容器名是ping不通其他容器的,只能用IP。这也是为什么Docker Compose会自动创建一个自定义bridge网络——自定义bridge才支持通过容器名互相解析。
# 默认bridge - 容器名不通dockerrun-d--nameweb nginxdockerrun-it--rmalpinepingweb# ping: bad address 'web' ← 不行# 自定义bridge - 容器名可达dockernetwork create mynetdockerrun-d--nameweb--networkmynet nginxdockerrun-it--rm--networkmynet alpinepingweb# PING web (172.18.0.2): 56 data bytes# 64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.089 ms ← 通了bridge模式适合什么场景:单机上跑多个容器,容器之间需要互通,但又想和宿主机保持网络隔离。绝大多数开发环境和中小型生产部署用bridge就够了。
性能代价:所有流量都要经过NAT和iptables规则,有一层转发开销。实测大概会带来2-5%的吞吐量损失和微秒级的延迟增加。对大多数Web应用来说感知不到,但如果你跑的是高频交易或者万兆网卡打满的场景,这个开销就不可忽略了。
Host模式:直接把容器的网络墙拆了
Host模式非常暴力——容器不再创建自己的网络命名空间,直接共享宿主机的网络栈。容器里的进程看到的网络接口、IP地址、路由表,和你在宿主机上看到的一模一样。
dockerrun-d--networkhost--namenginx-host nginx# 容器里看网络dockerexecnginx-hostipaddr# 你会看到宿主机的eth0、lo等所有接口# 宿主机上直接就能看到nginx监听的80端口ss-tlnp|grep:80# LISTEN 0 511 0.0.0.0:80 users:(("nginx",pid=12345,fd=6))注意到没?不需要-p 80:80了。因为容器和宿主机共享端口空间,nginx在容器里监听80,宿主机上80端口就直接被占了。
这也意味着一个严重限制:你不能在同一台宿主机上用host模式启动两个都监听80端口的容器——端口会冲突。
host模式的性能优势:没有了veth pair、没有了bridge转发、没有了iptables NAT规则,网络性能和裸机完全一致。有人做过测试,在高吞吐场景下,host模式比bridge快15-20%。对于那些需要极致网络性能的应用——比如做GPU推理服务的模型并行通信、高性能数据库、流媒体转码——host模式是个实打实的选择。
# 一个典型的高性能场景:Redis用host模式避免NAT开销dockerrun-d--networkhost--nameredis redis:7\--bind0.0.0.0--port6379--maxmemory4gb# 对比bridge模式下的Redis延迟# host模式:avg latency ~0.08ms# bridge模式:avg latency ~0.12ms# 50%的延迟差距,对缓存服务来说是能感知到的host模式适合什么场景:
- 对网络延迟极度敏感的应用(数据库、缓存、消息队列)
- 需要监听大量端口或动态端口的服务(比如FTP被动模式)
- 性能测试/压测场景,需要排除网络虚拟化的干扰
- 容器只是个打包工具,不需要网络隔离的场景
host模式的代价:完全丧失网络隔离。容器里的进程能看到宿主机的所有网络流量,能绑定任意端口。从安全角度讲,这基本等于没有网络层面的容器隔离。
None模式:彻底断网
None模式就是字面意思——容器里除了一个lo回环接口之外,什么都没有。没有eth0,没有IP地址,没有路由,没有任何对外通信的能力。
dockerrun-it--networknone alpinesh# 容器里只有lo/# ip addr1: lo:<LOOPBACK,UP,LOWER_UP>mtu65536inet127.0.0.1/8 scopehostlo /# ping 8.8.8.8PING8.8.8.8(8.8.8.8):56data bytes ping: sendmsg: Network is unreachable为什么会有人用这个?几个场景:
安全隔离:某些处理敏感数据的容器(比如加密运算、密钥生成),你根本不希望它有任何网络能力,以防万一被攻破后横向移动。
自定义网络:你想完全手动控制容器的网络配置——自己创建网络命名空间、自己配veth pair、自己设路由。一些CNI插件(比如Kubernetes里的Calico、Cilium)就是先用none模式创建容器,然后再由插件接管网络配置。
纯计算任务:容器只是跑一个CPU密集型的批处理任务,输入输出都通过挂载的volume传递,根本不需要网络。
# 安全场景:密钥生成容器,完全隔离网络dockerrun--networknone-v/secure/output:/output\my-keygen-image generate--output/output/key.pem实话说,生产环境里直接用none的不多。但如果你在做安全合规相关的东西,审计人员看到你的敏感容器用的是--network none,会比较放心。
Overlay模式:跨主机通信的唯一官方解
前面说的bridge、host、none都是单机上的事。当你有多台Docker宿主机,需要让不同机器上的容器互相通信时,overlay网络就登场了。
Overlay的原理是VXLAN封装——把容器的二层帧封在一个UDP包里,通过宿主机的物理网络传输到另一台宿主机,再解封给目标容器。对容器来说,它觉得自己和远程的容器在同一个二层网络里。
容器A (Host1) 容器B (Host2) eth0: 10.0.0.2 eth0: 10.0.0.3 | | [VXLAN封装] [VXLAN解封] | | Host1 eth0: 192.168.1.10 ---UDP---> Host2 eth0: 192.168.1.11要用overlay网络,你需要Docker Swarm模式(或者手动配置一个key-value store如Consul/etcd来做服务发现):
# 初始化Swarm(在manager节点)dockerswarm init --advertise-addr192.168.1.10# worker节点加入dockerswarmjoin--token<token>192.168.1.10:2377# 创建overlay网络dockernetwork create-doverlay--attachablemy-overlay# 在任意节点上启动容器并加入overlay网络dockerrun-d--nameweb--networkmy-overlay nginxdockerrun-d--nameapi--networkmy-overlay my-api-image# web和api可以通过容器名互通,即使它们在不同的物理机上dockerexecwebpingapi# PING api (10.0.1.3): 56 data bytes# 64 bytes from 10.0.1.3: seq=0 ttl=64 time=0.543 msOverlay的性能开销:VXLAN封装/解封有成本。每个包要多封50字节的头(VXLAN 8字节 + UDP 8字节 + IP 20字节 + Ethernet 14字节),而且有一次额外的内核态封包/解包操作。实测吞吐量比物理网络直连大概低10-15%,延迟多几百微秒。
Overlay适合什么场景:
- Docker Swarm集群里的服务间通信
- 跨数据中心的容器互联
- 多租户环境下的网络隔离(每个租户一个overlay网络)
Overlay不适合什么场景:
- 对延迟极度敏感的服务(那点封装开销可能受不了)
- 单机部署(杀鸡用牛刀)
- 需要精确控制MTU的场景(VXLAN封装会吃掉50字节,如果你的物理网络MTU是1500,overlay的有效MTU就只有1450)
顺带一提,如果你用Kubernetes而不是Docker Swarm,K8s的CNI插件(Calico、Cilium、Flannel)也是用类似的思路实现跨节点通信的,只不过有些用的是VXLAN,有些用的是IP-in-IP,有些用的是WireGuard,有些直接用BGP做路由。原理殊途同归。
Macvlan模式:让容器假装是一台物理机
Macvlan是最"真实"的一种网络模式。它给容器分配一个独立的MAC地址,让容器直接出现在你的物理网络上,和宿主机处于同一个二层网段。对上游交换机来说,这个容器就是网络上的一台独立设备。
# 创建macvlan网络# 假设宿主机的物理网卡是eth0,网段是192.168.1.0/24,网关192.168.1.1dockernetwork create-dmacvlan\--subnet=192.168.1.0/24\--gateway=192.168.1.1\-oparent=eth0\my-macvlan# 启动容器,直接分配物理网段的IPdockerrun-d--networkmy-macvlan--ip192.168.1.100--nameweb nginx# 同一个局域网里的其他机器可以直接访问 192.168.1.100:80# 不需要端口映射!不需要NAT!从网络拓扑上看是这样的:
物理交换机 |-- 宿主机 eth0: 192.168.1.10 (MAC: aa:bb:cc:dd:ee:01) |-- 容器 web: 192.168.1.100 (MAC: 02:42:ac:11:00:02) ← 独立MAC |-- 其他物理设备: 192.168.1.xxxMacvlan的一个大坑:容器和宿主机之间默认是不通的。没错,你的容器虽然和宿主机在"同一个网段",但因为Linux内核的macvlan实现机制,宿主机的物理网卡不会接收发给macvlan子接口的流量。解决办法是在宿主机上也创建一个macvlan接口来通信:
# 宿主机上创建一个macvlan接口,用来和容器通信iplinkaddmac0linketh0typemacvlan mode bridgeipaddradd192.168.1.200/24 dev mac0iplinksetmac0 up# 现在宿主机可以通过192.168.1.200这个地址和容器互通了ping192.168.1.100# 从mac0接口出去,可以到达容器Macvlan适合什么场景:
- 容器需要在物理网络上有自己的IP(比如对接老旧的硬件设备、SNMP监控系统)
- 需要绕过NAT的场景(某些协议对NAT不友好,比如SIP、某些工业协议)
- 家庭实验室/HomeLab里给容器分配局域网IP(智能家居、NAS服务)
- 需要VLAN隔离的企业环境
# VLAN场景:容器直接接入指定VLAN# 宿主机eth0上已经配了VLAN 100的子接口 eth0.100dockernetwork create-dmacvlan\--subnet=10.100.0.0/24\--gateway=10.100.0.1\-oparent=eth0.100\vlan100-netdockerrun-d--networkvlan100-net--ip10.100.0.50 my-app# 这个容器现在在VLAN 100里,有自己的IPMacvlan vs Ipvlan:顺便提一嘴ipvlan。macvlan给每个容器分配独立的MAC地址,ipvlan则是所有容器共享宿主机的MAC地址,只在IP层做隔离。当你的交换机有MAC地址数量限制(某些云环境或老旧交换机),或者你需要运行几百个容器时,ipvlan比macvlan更合适——因为不会触发交换机的MAC地址表溢出。
# ipvlan L2模式 - 共享MAC,独立IPdockernetwork create-dipvlan\--subnet=192.168.1.0/24\--gateway=192.168.1.1\-oparent=eth0\-oipvlan_mode=l2\my-ipvlan一张表搞清楚怎么选
| 维度 | Bridge | Host | None | Overlay | Macvlan |
|---|---|---|---|---|---|
| 隔离性 | ✅ 好 | ❌ 无 | ✅ 完全 | ✅ 好 | ✅ 中等 |
| 性能 | 中等 | 最好 | N/A | 较低 | 好 |
| 跨主机 | ❌ | ❌ | ❌ | ✅ | ❌ (需L3) |
| 端口映射 | 需要 | 不需要 | N/A | 需要 | 不需要 |
| 容器互通 | 同bridge可通 | 走lo | 不通 | 同overlay可通 | 同网段可通 |
| DNS发现 | 自定义bridge支持 | 不支持 | 不支持 | 支持 | 不支持 |
| 复杂度 | 低 | 最低 | 最低 | 高 | 中等 |
| 典型场景 | 开发/中小生产 | 高性能服务 | 安全隔离 | 集群/多主机 | 接入物理网络 |
实战中的组合用法
真实的生产环境里,这几种模式经常混着用。举个例子,一个典型的微服务部署:
# docker-compose.yml - 混合网络模式的实际例子version:'3.8'services:# Nginx反向代理 - 用host模式,直接监听80/443,性能最好nginx:image:nginx:latestnetwork_mode:hostvolumes:-./nginx.conf:/etc/nginx/nginx.conf# 应用服务 - 用自定义bridge,容器间通过名字互通api:image:my-api:latestnetworks:-backendworker:image:my-worker:latestnetworks:-backend# Redis缓存 - 也在bridge网络里,但如果对延迟敏感也可以用hostredis:image:redis:7networks:-backend# 如果延迟敏感,换成:# network_mode: host# 密钥管理容器 - none模式,彻底隔离vault-init:image:vault-init:latestnetwork_mode:nonevolumes:-secrets:/secretsnetworks:backend:driver:bridgeipam:config:-subnet:172.20.0.0/16volumes:secrets:还有一个容易被忽略的点:Docker Compose默认就会创建自定义bridge网络,所以你在compose里定义的服务天然支持容器名DNS解析——这比手动docker run然后指望默认bridge要好使得多。
排障思路
网络问题是Docker里最让人头疼的。分享几个排障的常用命令和思路:
# 1. 先确认容器用的什么网络模式dockerinspect<container>--format'{{.HostConfig.NetworkMode}}'# 2. 看容器的IP和网络配置dockerinspect<container>--format'{{json .NetworkSettings.Networks}}'|jq.# 3. 看宿主机上的网桥和veth设备brctl showiplinkshowtypeveth# 4. 看iptables规则是否正确(bridge模式下端口映射靠这个)iptables-tnat-L-n-v|grep<container_ip># 5. 进容器里排查dockerexec-it<container>sh# 然后: ip addr / ip route / ping / curl / nslookup# 6. 抓包 - 在宿主机的veth接口上抓容器的流量tcpdump-iveth3a8f9b2-nn# 7. overlay网络排障 - 检查VXLAN隧道ip-dlinkshowtypevxlan最常见的几个坑:
- 容器ping不通外网 → 检查宿主机的
ip_forward是否开启:sysctl net.ipv4.ip_forward - 端口映射不生效 → 检查iptables的DOCKER链有没有被其他防火墙规则覆盖
- overlay网络容器间不通 → 检查宿主机之间的UDP 4789端口(VXLAN)是否放通
- macvlan容器ping不通宿主机 → 正常现象,需要额外创建macvlan接口(前面讲过)
选型就一句话
单机开发测试→ bridge(默认的就行,自定义bridge更好)
极致性能→ host(牺牲隔离换性能)
安全隔离 / 纯计算→ none
多主机集群→ overlay
接入物理网络 / 绕NAT→ macvlan
不用想太复杂。90%的场景用自定义bridge就够了,剩下10%根据你的具体需求(性能、安全、跨主机)来选对应的模式。
