很多人在本地跑 Docker 时都用过 -p 8080:80 把容器端口映射到宿主机,但一旦把应用搬到多台机器、或者要和宿主机上的数据库互相访问,就会出现「容器里连不上 X」「主机看不到容器 IP」这类问题。这些现象的根源,基本都是没搞清楚 Docker 的网络模式到底是怎么工作的。
Docker 安装完成后会自动创建几个网络,默认情况下容器走的是 Bridge 模式,相当于给每个容器发了张内网地址,通过 NAT 出去了。但 Bridge、Host、Overlay、Macvlan、None 这几种模式在网络路径、性能、隔离性和可访问范围上差别很大,选错了要么性能损失明显,要么根本不通。
这篇文章会把 Docker 常见网络模式的原理、适用场景、真实命令和排查方法讲清楚,最后给出一张对比表和选型建议,帮助你在单机、多容器、跨主机集群这三种典型场景下做出正确选择。
一、先看 Docker 默认创建了哪些网络
安装 Docker 后,用下面命令可以看到系统预置的网络:
docker network ls
输出大致是:
NETWORK ID NAME DRIVER SCOPE
a1b2c3d4e5f6 bridge bridge local
f6e5d4c3b2a1 host host local
1a2b3c4d5e6f none null local
其中 bridge 就是默认网络,所有不指定 --network 的容器都会加入它;host 表示容器与宿主机共享网络命名空间;none 表示不给容器配置任何网络。此外还能看到 DRIVER 和 SCOPE 两列——Driver 决定网络实现方式,Scope 表示这个网络是只能在本机用(local)还是能跨主机(swarm)。
查看某个网络的细节,比如默认 bridge 的子网和网关:
docker network inspect bridge
你会看到 Subnet 通常是 172.17.0.0/16,Gateway 是 172.17.0.1。这个 172.17 网段是 Docker 的默认约定,如果它和你的办公网或机房内网冲突,后面会讲到怎么改。
二、Bridge 模式:单机默认,也最常用
Bridge 模式的核心是 Docker 在宿主机上创建了一个虚拟网桥(默认叫 docker0),每个容器通过一对 veth 设备接到这个网桥上,容器获得一个 172.17 网段的内网地址,出公网时由宿主机做 SNAT 转换。
自定义 bridge 网络比默认 bridge 更好用,因为它自带 DNS 名称解析,容器之间可以用容器名互访:
# 创建一个自定义 bridge 网络
docker network create --driver bridge \
--subnet 172.20.0.0/16 \
--gateway 172.20.0.1 \
mynet
# 启动两个容器加入同一网络
docker run -d --name web --network mynet nginx:alpine
docker run -d --name app --network mynet alpine sleep 3600
# 在 app 里直接用容器名访问 web
docker exec -it app ping -c 2 web
这里的关键点是:默认 bridge 网络不支持容器名互访,必须用 --link(已过时)或者直接写 IP;而自定义 bridge 网络内置了 DNS,容器名就是域名,这一点非常实用。
端口映射方面,Bridge 模式下容器端口默认只在内网可见,对外要显式发布:
docker run -d -p 8080:80 --name web nginx:alpine
Bridge 模式的性能开销主要来自 NAT,一般在高并发短连接场景下会有几个百分点的额外延迟,经验值通常在 5% 到 15% 之间,具体取决于流量模型。对于绝大多数 Web 应用,这个损耗完全可以接受。
三、Host 模式:去掉 NAT,性能最好
Host 模式下容器不再拥有独立的网络命名空间,而是直接复用宿主机的网卡、IP 和端口。启动方式:
docker run -d --network host --name web nginx:alpine
这时 nginx 直接监听宿主机的 80 端口,不需要 -p。优点是:
- 少了一层 NAT,网络性能接近裸机;
- 容器内看到的就是宿主机网卡,排查网络问题时少了变量。
代价也很明显:
- 端口冲突:两个容器都想监听 80 就必然打架;
- 隔离性差:容器能直接看到宿主机所有网络接口;
- 跨平台不一致:这一特性在 Linux 上才完整支持,早期 Docker Desktop 在 macOS/Windows 上对 host 网络支持有限。
所以 Host 模式适合对延迟敏感、端口固定的场景,比如高吞吐的网关、采集代理、性能压测客户端。
四、Overlay 模式:跨主机通信的答案
单机 Bridge 只能解决一台机器上的容器互访。当你有三台、五台机器组成集群,容器要跨主机互相寻址时,就需要 Overlay 网络。
Overlay 的作用是在物理网络之上再叠一层虚拟网络,让不同宿主机上的容器感觉它们处在同一个二层网络里。Docker 原生的 Overlay 依赖 Swarm 模式,创建方式:
# 初始化 swarm(只在管理节点执行)
docker swarm init --advertise-addr 192.168.1.10
# 创建 overlay 网络
docker network create --driver overlay --attachable myoverlay
--attachable 允许普通的独立容器(非 service)也接入这个 overlay 网络,方便调试。Overlay 的底层通常用 VXLAN 封装,会在原有数据包外面再套一层 UDP 头,因此 MTU 会变小——这是很多 overlay 网络「大包丢失、小请求正常」问题的根源,后文会专门讲。
需要注意的是,Docker Swarm 现在用得少了,生产环境更常见的是 Kubernetes 配合 Calico、Flannel、Cilium 等 CNI 插件实现跨主机网络。它们思路相通:都是通过某种隧道或路由方案,把 Pod 网络打通。
五、Macvlan 与 None:两个特殊但有用的模式
Macvlan 模式让容器直接从物理网络获取一个真实的二层地址,相当于给容器发了一张「真实的网卡」。它适合需要让容器像物理机一样接入现有网络的场景,比如老系统的授权绑定 MAC 地址、需要被同网段设备直接发现的场景:
docker network create -d macvlan \
--subnet 192.168.1.0/24 \
--gateway 192.168.1.1 \
-o parent=eth0 \
macnet
风险提示:Macvlan 在某些无线网卡或交换机上可能不工作,且配错 --subnet 可能导致 IP 冲突甚至断网,建议先在测试环境验证。回滚方式就是 docker network rm macnet 并停掉相关容器。
None 模式则相反,它给容器一个完全隔离的网络命名空间,除了 lo 什么都没有。适合不需要网络、只做计算或数据处理的批处理容器。
docker run -d --network none --name isolated alpine sleep 3600
六、六种网络模式对比
| 模式 | 网络命名空间 | IP 来源 | 性能 | 跨主机 | 典型场景 |
|---|---|---|---|---|---|
| Bridge(默认) | 独立 | docker0 网段 | 有 NAT 损耗 | 不支持 | 单机多容器互访 |
| 自定义 Bridge | 独立 | 自定义网段 | 有 NAT 损耗,含内置 DNS | 不支持 | 推荐的单机方案 |
| Host | 共享宿主机 | 宿主机 IP | 接近裸机 | 视宿主机而定 | 低延迟、固定端口 |
| Overlay | 独立 | 虚拟网段 | VXLAN 封装有损耗 | 支持 | Swarm/集群跨主机 |
| Macvlan | 独立 | 物理网段真实 IP | 接近裸机 | 依赖二层网络 | 需真实二层地址 |
| None | 仅 lo | 无 | 无网络 | 不支持 | 离线批处理 |
从运维经验看,单机场景优先用自定义 Bridge,跨主机交给 Kubernetes 的 CNI,Host 只在确有性能诉求时使用。
七、典型排障与踩坑
网段冲突。如果宿主机所在内网就是 172.17.0.0/16,docker0 会和真实网络撞车,导致某些地址访问异常。解决办法是在 /etc/docker/daemon.json 里改默认地址池:
{
"bip": "172.31.0.1/24",
"default-address-pools": [
{ "base": "172.30.0.0/16", "size": 24 }
]
}
改完重启 Docker 生效:
# RHEL/CentOS/Rocky
sudo systemctl restart docker
# Debian/Ubuntu
sudo systemctl restart docker
风险提示:改动默认网段会影响正在运行的容器,重启后已有容器需要重新连接网络,建议在维护窗口操作,回滚方式是还原 daemon.json 并重启。
Overlay 大包不通。因为 VXLAN 封装会占用额外头部,如果物理网卡 MTU 是 1500,容器内实际可用 MTU 常常只有 1450 左右。表现是 ping 小包正常、传输大文件卡死。排查:
docker exec -it app ip link show eth0
docker exec -it app ping -M do -s 1472 web # 逐步减小探测 MTU
容器访问宿主机服务。Linux 上可以直接用 172.17.0.1(docker0 网关)访问宿主机;也可以加一个 --add-host:
docker run --add-host host.docker.internal:host-gateway nginx:alpine
常见问题
Q1:为什么容器里 ping 不通 另一个容器名,但用 IP 可以?
A:你用的是默认 bridge 网络,它没有 DNS。换成 docker network create 出来的自定义网络,把两个容器都接进去,容器名就能直接解析。
Q2:-p 8080:80 生效但 -p 8080:80/udp 不通,怎么回事?
A:默认协议是 TCP,UDP 服务必须显式写 /udp,如 -p 53:53/udp。
Q3:Host 模式下 -p 还有意义吗?
A:没有。Host 模式容器直接用宿主机端口,-p 会被忽略并可能给出警告。
Q4:容器重启后 IP 变了,配置该怎么写?
A:不要把容器 IP 写死在配置里。用自定义网络的容器名或 Docker 的 DNS 别名,Kubernetes 里则用 Service 名。
Q5:Overlay 网络里两个容器能通,但延迟偏高怎么办?
A:先确认物理网络是否做过额外封装,并检查 MTU 设置。若宿主机网卡 MTU 已是 1500,建议把容器内 MTU 调低到 1450 左右测试。
总结
- 单机多容器互访,用自定义 Bridge,自带 DNS、比默认 bridge 好用,是日常首选。
- 对网络延迟敏感、端口固定的服务用 Host 模式,但要接受端口冲突和隔离性下降。
- 跨主机通信选 Overlay(Swarm)或 Kubernetes 的 CNI 插件,注意 VXLAN 封装带来的 MTU 缩减问题。
- 需要容器拿到真实二层地址时用 Macvlan,但务必先在测试环境验证,避免 IP 冲突。
- 排查网络问题先
docker network inspect看子网和网关,再ip addr、ping -M do逐步定位,别急着改配置。
企业QQ咨询




