Docker五种网络模式详解:bridge、host、none、container、overlay原理与选型
Docker 用久了,早晚会撞上网络这堵墙。我见过太多人镜像构建得飞起、Compose 编排写得溜,结果一碰到"容器 A 连不上容器 B""宿主机访问不了容器端口""跨主机通信延迟高得离谱"就彻底卡壳。问题往往不在 Docker 本身,而在于没搞清楚它底层那几套网络模式各自解决什么问题、在什么场景下该用哪一套。这篇就把 Docker 的五种通信模式——bridge、host、none、container、overlay——从原理到实操完整拆一遍,顺带把我在生产环境里踩过的坑和排查思路一并交代清楚。不管你是刚装完 Docker Desktop 想搞明白容器怎么上网的新手,还是已经在跑微服务集群、被 overlay 网络折腾过的老手,应该都能从里面找到点有用的东西。
1. 先搞明白 Docker 网络到底在解决什么问题
1.1 容器网络的三层诉求
很多人一上来就背"bridge 是默认模式、host 是共用宿主机网络",背完还是不知道怎么选。要真正理解,得先想清楚容器网络要同时满足三件事。
第一是隔离性。容器之间默认不应该互相看见,更不应该随便访问宿主机的内部服务。这是安全底线,也是为什么 Docker 默认给每个容器塞进一个独立的 network namespace。
第二是连通性。隔离归隔离,该通的必须通——容器要能访问外网拉依赖,外部要能访问容器暴露的服务,同一应用的多个容器之间要能互相调用。
第三是可移植性。同一份镜像,在开发机上跑、在测试环境跑、在云上跑,网络行为应该尽量一致,不能因为换了台机器就全乱套。
这三件事本身是互相拉扯的:隔离越强,连通越麻烦;可移植性越好,性能往往越难榨到极致。Docker 的五种网络模式,本质上就是在这三个维度上做不同的取舍。
1.2 network namespace 是理解一切的地基
Docker 网络的所有魔法,根子都在 Linux 的 network namespace 上。你可以把它想象成一台机器里隔出来的多个"虚拟网络房间":每个房间有自己独立的网卡、路由表、iptables 规则、端口空间。一个容器默认就住在一个这样的房间里,它看到的 eth0、它以为的 localhost,都只属于它自己。
宿主机则扮演"大楼管理员"的角色,通过 veth pair(虚拟网线对)把容器的房间和外面的世界连起来。veth pair 就像一根两头都是网口的网线,一头插在容器的 namespace 里(容器内看到的是 eth0),另一头插在宿主机的某个网桥上(比如 docker0)。数据从容器出去,经过这根虚拟网线到达网桥,再由网桥决定往哪转发。
理解了这一层,后面所有模式就都好解释了:bridge 模式是给容器单独建房间再拉网线到网桥;host 模式是干脆不让容器住单间,直接跟宿主机共用一套网络;none 模式是给个房间但不拉网线;container 模式是两个容器挤进同一个房间;overlay 模式则是把网线拉到了另一台物理机器上。
1.3 一张表看清五种模式的定位
在深入每种模式之前,先用一张表把它们的核心差异摆出来,方便你建立整体印象。
| 模式 | 网络隔离 | 性能 | 典型场景 | 跨主机 |
|---|---|---|---|---|
| bridge | 强 | 中等(有 NAT 开销) | 单机多容器、默认场景 | 否 |
| host | 无 | 最高(无 NAT) | 高性能、端口固定的服务 | 否 |
| none | 完全隔离 | 不适用 | 离线计算、自定义网络 | 否 |
| container | 共享他容器 | 高 | Sidecar、共享网络栈 | 否 |
| overlay | 强(逻辑隔离) | 较低(有封装开销) | 跨主机集群、Swarm | 是 |
这张表建议先记个大概,下面逐层拆开讲,你会发现每一格背后都有具体的原理支撑。
2. bridge 模式:默认选项背后的网桥与 NAT 机制
2.1 docker0 网桥是怎么工作的
你装完 Docker,执行 ip addr 会看到一个叫 docker0 的虚拟网桥,默认地址通常是 172.17.0.1/16。这就是 bridge 模式的核心枢纽。
每启动一个使用默认 bridge 网络的容器,Docker 会做这么几件事:创建一对 veth pair,一头放进容器 namespace 命名成 eth0,另一头挂到 docker0 上;从 172.17.0.0/16 里给容器分配一个 IP(比如 172.17.0.2);在容器内设置默认路由指向 172.17.0.1。这样一来,同一台宿主机上所有 bridge 容器都在同一个二层网络里,通过 docker0 互相能通。
但这里有个新手最容易困惑的点:默认 bridge 网络下的容器之间,只能用 IP 互相访问,不能用容器名。因为默认 bridge 不提供自动 DNS 解析。你 docker run 两个容器,想用名字 ping 通,是做不到的。这也是为什么现在更推荐用自定义 bridge 网络。
2.2 自定义 bridge 网络为什么更好用
从 Docker 1.10 开始,你可以自己创建 bridge 网络:
然后启动容器时指定 --network mynet。自定义 bridge 相比默认 bridge 有三个实打实的好处。
第一是自动 DNS 解析。同一自定义网络里的容器,可以直接用容器名互相访问。Docker 内置了一个嵌入式 DNS 服务器(监听在 127.0.0.11),容器发起的域名查询会被它接管。这意味着你在应用配置里写 db:3306 就能连上名为 db 的容器,不用关心它 IP 是多少、重启后 IP 变没变。
第二是更好的隔离。不同自定义网络之间默认不通,你可以按应用划分网络,比如前端一个网、后端一个网,天然形成边界。
第三是能动态加入和退出。运行中的容器可以 docker network connect 挂到新网络上,不用重启,这在做灰度或者临时调试时特别方便。
2.3 端口映射与 iptables 的隐藏工作
外部要访问 bridge 容器里的服务,靠的是 -p 端口映射。比如 docker run -p 8080:80 nginx,把宿主机 8080 映射到容器 80。
这条命令背后,Docker 实际做了两件事:一是在宿主机上启动一个 docker-proxy 进程监听 8080(用户态转发),二是往 iptables 的 nat 表里写 DNAT 规则,把发往宿主机 8080 的包改写到容器 IP 的 80 端口。很多人不知道 docker-proxy 的存在,排查问题时看到这个进程占着端口还以为是异常,其实它是正常组件。
提示:如果你在宿主机上
netstat看到 docker-proxy 监听了一堆端口,别急着 kill 它,那是端口映射的正常实现。真正决定流量走向的是 iptables 规则,可以用iptables -t nat -L -n查看。
2.4 bridge 模式的性能代价
bridge 模式不是没有成本的。数据包从容器出去要经过:容器 eth0 → veth pair → docker0 网桥 → iptables NAT → 宿主机物理网卡。这一路上网桥转发和 NAT 转换都要消耗 CPU,高并发场景下这个开销不能忽略。
我实测过一个简单的 HTTP 服务,host 模式下的 QPS 比 bridge 模式高出大概 15% 到 25%,具体取决于包大小和并发量。所以对性能极度敏感、又不需要多容器隔离的服务,host 模式往往更划算。这个取舍后面讲 host 时还会展开。
3. host 与 none:两个极端场景的正确打开方式
3.1 host 模式:直接共用宿主机网络栈
docker run --network host nginx 一执行,容器就不再拥有独立的 network namespace,而是直接使用宿主机的网卡、IP、端口空间。容器里监听 80,宿主机上就是 80,不需要任何 -p 映射。
host 模式最大的价值是性能。因为没有了 veth pair、没有了网桥转发、没有了 NAT,数据包路径跟直接在宿主机上跑进程几乎一样。前面提到的 15% 到 25% 的性能差距就是这么省出来的。对于网络密集型应用,比如高频交易网关、实时音视频转发、压测工具,host 模式是首选。
但代价也很明显。端口冲突是第一大坑:宿主机上一个端口只能被一个进程占用,你没法像 bridge 那样把多个容器的 80 映射到宿主机不同端口。第二个坑是隔离性丧失:容器能看到宿主机所有网络接口,能访问宿主机上所有监听的服务,安全边界基本没了。
注意:host 模式在 Linux 上原生支持,但在 Docker Desktop(Windows/Mac)上行为不同——因为 Docker Desktop 实际跑在一个 Linux 虚拟机里,host 模式指的是那个虚拟机的网络,不是你的 Windows/Mac 宿主机。这一点很多人第一次用会懵。
3.2 none 模式:彻底断网的容器
docker run --network none 启动的容器,只有一个 loopback 接口,没有任何对外连接能力。听起来好像没啥用,但在几个场景里它是刚需。
一是离线计算任务。比如你要跑一个数据处理脚本,出于安全合规要求,它绝对不能联网,none 模式就是最干净的保证。二是自定义网络的起点。有些高级用法会先用 none 起容器,再手动把 veth pair 接进自己管理的网桥,实现完全自定义的网络拓扑。三是安全沙箱。跑不可信代码时,断网能挡掉一大类风险。
none 模式下容器不是不能通信,你可以手动 docker network connect 把它接进某个网络,或者用 nsenter 进到它的 namespace 里手动配置。它更像是一张白纸,给你最大的控制权。
3.3 host 与 none 的选型判断
这两个模式看似对立,其实判断逻辑很清晰。问自己两个问题:这个服务需要被外部访问吗?需要跟其他容器通信吗?
如果两个都要,且对性能敏感、端口固定,选 host。如果两个都不要,或者你要自己完全掌控网络配置,选 none。如果只是普通业务容器,需要隔离又需要互通,那还是老老实实用 bridge 或自定义网络。
我个人的经验是:生产环境里 host 模式要慎用,除非你非常清楚端口规划和安全边界。none 模式则常出现在安全要求高的批处理任务里,属于"平时不用,用时救命"的类型。
4. container 模式:共享网络栈的 Sidecar 玩法
4.1 container 模式的本质
docker run --network container:目标容器名 这个模式,让新容器直接复用另一个已存在容器的 network namespace。两个容器共享同一套网卡、IP、端口空间,它们看到的网络世界完全一样。
这跟 host 模式有点像,区别在于共享的对象不同:host 是共享宿主机的网络栈,container 是共享另一个容器的网络栈。所以 container 模式既保留了"不占用宿主机端口"的好处,又实现了两个容器之间的零距离通信——它们互相访问 localhost 就能通。
4.2 Sidecar 模式的经典应用
container 模式最典型的用法就是 Sidecar(边车)模式。设想一个场景:你的主应用容器只负责业务逻辑,网络代理、日志收集、监控采集这些横切关注点交给一个独立的 sidecar 容器处理,两者共享网络栈。
比如主应用监听 8080,sidecar 里的代理进程监听 9090 并负责把流量转发给 8080。因为共享 namespace,sidecar 直接访问 localhost:8080 就行,不需要知道主容器的 IP,也不受主容器重启后 IP 变化的影响。这种解耦让主应用镜像保持干净,代理逻辑可以独立升级。
Kubernetes 里的 Pod 概念,本质上就是把多个容器塞进同一个 network namespace,跟 Docker 的 container 模式是同一个思路。理解了 container 模式,再去看 K8s 的 Pod 网络模型就会觉得顺理成章。
4.3 使用 container 模式的注意事项
这个模式有几个坑必须提前知道。
第一,生命周期绑定。共享网络栈的容器,如果被共享的那个容器先停了,其他容器的网络也就断了。所以启动顺序有讲究,被依赖的容器要先起。
第二,端口不能重复。因为共享端口空间,两个容器不能监听同一个端口,否则第二个会启动失败。规划端口时要当成一个整体来考虑。
第三,调试时容易混淆。docker exec 进不同容器,看到的网络配置一模一样,排查问题时容易搞不清到底是谁在监听。建议给容器起清晰的名字,用 docker inspect 确认网络归属。
5. overlay 模式:跨主机通信的封装与代价
5.1 overlay 要解决的核心矛盾
前面四种模式都局限在单台宿主机上。一旦你的容器分布在多台机器上,bridge 网络就无能为力了——A 机器上的容器根本不知道 B 机器上容器的存在。overlay 网络就是为跨主机通信设计的。
它的核心思路是:在物理网络之上,再叠加一层逻辑网络。每个容器在这个逻辑网络里有一个虚拟 IP,不管它实际跑在哪台机器上,其他容器都能用这个虚拟 IP 访问它。底层则通过 VXLAN 技术,把逻辑网络的数据包封装进物理网络的 UDP 包里传输。
打个比方:物理网络是城市之间的高速公路,overlay 网络是在高速公路上跑的"集装箱卡车"。你的货物(容器数据包)被装进标准集装箱(VXLAN 封装),走高速公路到达目的地后再拆箱。对货物来说,它感觉自己走了一条直达专线,实际上中间经过了封装和解封装。
5.2 VXLAN 封装的开销从哪来
VXLAN 的封装不是免费的。每个数据包都要额外加上 50 字节左右的头部(外层 IP、UDP、VXLAN 头),这意味着有效载荷比例下降,小包场景下开销尤其明显。同时封装和解封装都要 CPU 参与,虽然现代网卡支持 VXLAN 卸载能缓解一部分,但整体性能还是不如 bridge。
我做过对比测试,同样的服务在 overlay 网络下的吞吐量大概比 bridge 低 10% 到 30%,延迟增加几十微秒到几百微秒不等。这个代价在微服务内部调用里通常可以接受,但对延迟敏感的场景就要慎重。
5.3 Swarm 下 overlay 网络的实操
overlay 网络最常用的载体是 Docker Swarm。初始化 Swarm 后创建 overlay 网络:
--attachable 这个参数很关键,它允许普通的独立容器(非 Swarm 服务)也接入这个 overlay 网络,方便调试和混合部署。
overlay 网络还依赖一个键值存储来同步各节点的网络状态。Swarm 模式下 Docker 内置了基于 Raft 的存储,不需要额外部署 etcd 或 Consul。但如果你用的是独立的 overlay(非 Swarm),就得自己维护键值存储,这也是为什么现在大家更倾向于直接用 Swarm 或 K8s 的 CNI 插件。
5.4 overlay 网络的排查难点
overlay 出问题时排查比单机网络麻烦得多,因为涉及多个节点。我总结几个高频问题。
节点间 UDP 4789 端口不通。VXLAN 默认用 4789 端口,如果防火墙挡了这个端口,overlay 网络直接瘫痪。排查时先在节点间用 nc -u 测一下 UDP 连通性。
MTU 不匹配。VXLAN 封装会增加包大小,如果物理网络 MTU 是 1500,overlay 里的容器 MTU 通常要设成 1450 左右,否则大包会被丢弃,表现为"小请求正常、大文件传输卡死"。这个坑非常隐蔽,我当年排查了整整一个下午。
节点时间不同步。Swarm 的 Raft 存储对时间敏感,节点间时间差太大会导致网络状态同步异常。确保所有节点开了 NTP。
提示:overlay 网络里
docker network inspect看到的容器 IP 是逻辑 IP,跟物理机 IP 完全是两回事。排查时别拿逻辑 IP 去物理网络里找,要用docker network inspect看 peers 列表确认节点分布。
6. 五种模式的选型决策与实战排查
6.1 一张决策流程图(文字版)
选型其实不用背,按顺序问自己几个问题就行。
先问:容器需要跨主机通信吗? 需要,直接上 overlay(配合 Swarm 或 K8s)。不需要,进入下一问。
再问:需要跟其他容器共享网络栈吗? 需要,用 container 模式做 sidecar。不需要,进入下一问。
再问:对网络性能极度敏感,且端口固定吗? 是,用 host 模式。不是,进入下一问。
再问:需要完全断网或自定义网络吗? 是,用 none 模式。不是,用 bridge(优先自定义 bridge)。
这个顺序背后的逻辑是:先确定范围(单机还是跨机),再确定关系(是否共享),再确定性能诉求,最后才是默认选项。按这个思路走,基本不会选错。
6.2 网络不通的通用排查链路
容器网络不通,别慌,按这条链路走一遍,八成问题能定位。
第一步,确认容器状态和网络归属。docker inspect 容器名 看 NetworkSettings,确认它在哪个网络、IP 是多少、网关是谁。
第二步,从容器内部往外 ping。docker exec -it 容器名 ping 网关IP,通了说明容器到网桥没问题;不通说明 veth pair 或网桥配置有问题。
第三步,测 DNS 解析。docker exec 容器名 nslookup 目标容器名,如果解析失败,多半是没在同一个自定义网络里,或者用了默认 bridge。
第四步,测端口连通性。docker exec 容器名 nc -zv 目标IP 目标端口,确认目标服务真的在监听、防火墙没挡。
第五步,看宿主机 iptables。iptables -t nat -L -n 和 iptables -L -n,检查有没有规则误伤。Docker 会往 iptables 里写不少规则,跟其他防火墙软件(比如 firewalld、ufw)冲突是常见问题。
第六步,抓包。前面都正常还不通,就上 tcpdump。在宿主机 docker0 上抓,在容器 eth0 上抓,对比数据包到底在哪一段丢了。
6.3 几个我踩过的真实坑
坑一:ufw 和 Docker 的 iptables 打架。Ubuntu 上开了 ufw,以为能限制容器端口,结果发现 -p 映射的端口照样能被外部访问。原因是 Docker 直接操作 iptables 的 nat 表,绕过了 ufw 的规则链。正确做法是在 DOCKER-USER 链里加规则,而不是依赖 ufw。
坑二:容器重启后 IP 变了,配置写死 IP 全崩。这是新手最常见的错误。解决办法就是用自定义 bridge 网络 + 容器名访问,让 Docker 的 DNS 帮你解析,永远不要写死 IP。
坑三:overlay 网络下大文件传输莫名中断。查了半天是 MTU 问题,把容器网络 MTU 调成 1450 后恢复正常。这个问题的迷惑性在于小请求完全正常,只有大数据量才暴露。
坑四:Docker Desktop 上 host 模式不生效。前面提过,Docker Desktop 跑在虚拟机里,host 模式指的是虚拟机网络。想在 Windows/Mac 上让容器直接用宿主机网络,基本做不到,只能靠端口映射。
6.4 性能调优的几个实用手段
如果你的容器网络成了瓶颈,可以试试这几招。
能用 host 就用 host。对性能敏感且不需要隔离的服务,host 模式省掉 NAT 和网桥开销,立竿见影。
自定义 bridge 替代默认 bridge。除了 DNS 好处,自定义 bridge 的 iptables 规则更少,转发路径略短。
调整 MTU。如果物理网络支持 jumbo frame(MTU 9000),把 Docker 网络 MTU 也调大,能显著提升大包吞吐。overlay 场景下则要确保 MTU 减去封装开销后不超过物理 MTU。
考虑 ipvlan 或 macvlan。这两个严格说不在五种模式里,但它们是 bridge 的高性能替代品。macvlan 让容器直接获得物理网络的 MAC 和 IP,性能接近 host 又有隔离。缺点是很多交换机对单网卡多 MAC 有限制,用之前要确认网络环境支持。
监控 docker-proxy 的 CPU 占用。高并发端口映射场景下,用户态的 docker-proxy 可能成为瓶颈。可以在 daemon.json 里配置 "userland-proxy": false,让 iptables 直接处理转发,减少一次用户态拷贝。但要注意,关掉之后某些依赖 docker-proxy 的场景(比如回环地址访问映射端口)可能行为变化,测试充分再上生产。
网络这块东西,看文档永远觉得懂了,一上手就发现全是细节。我的建议是别光看,自己起几个容器把五种模式都跑一遍,用 docker network inspect、ip addr、iptables -L 挨个看一遍实际配置,再故意制造几个不通的场景去排查。折腾过一遍之后,这些模式就不再是抽象概念,而是你脑子里一张清晰的图。真到了生产环境出问题的时候,这张图能帮你省下大量瞎猜的时间。