容器能跑起来很简单,跑得「通」却需要一点网络知识。新手最常见的困惑就是:为什么两个容器互相 ping 不通?为什么 -p 8080:80 有时不生效?为什么有的容器一启动就「抢」了宿主机的端口?
答案都指向同一个东西——Docker 网络模式。本文从零讲清 Docker 的 6 种网络模式(Bridge、Host、None、Overlay、Macvlan、Ipvlan)的工作原理、优缺点,并给出不同场景下的选型建议。
网络基础:理解三个关键概念
在进入模式之前,先建立三个底层概念,理解它们之后,各种模式的区别就一目了然。
网络命名空间(Network Namespace)
Linux 的隔离机制之一。每个容器拥有独立的网络栈:自己的网卡、IP 地址、路由表和防火墙规则。Docker 的每种网络模式,本质上就是「容器网络命名空间和宿主机网络命名空间之间如何建立联系」。
veth 虚拟网卡对
一根「虚拟网线」,一头插在容器里,另一头插在宿主机的虚拟网桥上,数据包就在这根网线上双向流动。
NAT 地址转换
宿主机启动时开启 IP 转发,并通过 iptables 做源地址转换:容器访问外网时,数据包源地址被替换成宿主机 IP,回来后反向转换。NAT 让容器「借」宿主机上网,但多了一层转换开销。
六大网络模式详解
Docker 安装完成后默认自带三个网络:bridge、host、none,另外还有 overlay、macvlan、ipvlan 三种驱动可按需创建。用 docker network ls 即可查看:
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
0c07a4c1d7a6 bridge bridge local
a1f2b3c4d5e6 host host local
9e8f7a6b5c4d none null localBridge:默认模式,单机主力
工作原理:Docker 守护进程启动时创建虚拟网桥 docker0(默认网段 172.17.0.0/16),每个容器通过 veth 对连到网桥上,获得一个私有 IP;容器访问外网走 NAT,外部访问容器通过 -p 端口映射。
# 以默认 bridge 启动
docker run -d -p 8080:80 --name web nginx
# 查看容器 IP
docker inspect web | grep -i ipaddress默认 bridge 的三个短板:
- 没有容器名 DNS:同网段容器之间只能靠 IP 通信,而容器重启后 IP 会变,极其脆弱。
- 所有容器都挤在同一个网段:不同业务的容器互相可见,隔离性差。
- 不能热插拔:默认 bridge 上运行的容器无法动态
docker network connect到其他网络。
自定义 Bridge 网络(强烈推荐):创建一个属于自己的网桥,以上三个问题全部解决:
# 创建自定义桥接网络
docker network create my-app-net
# 两个容器都连到该网络
docker run -d --network my-app-net --name api my-api:latest
docker run -d --network my-app-net --name db mysql:8.0
# 容器间直接用名字通信(自动 DNS 解析)
docker exec api ping db自定义网络自带内置 DNS,api 容器直接 ping db 就能解析到 db 容器的 IP,与 IP 变化无关。同时不同自定义网络之间天然隔离,即使映射了相同端口也不冲突。绝大多数单机场景,这是最优解。
Host:共享宿主机网络栈
工作原理:容器不创建独立网络命名空间,直接复用宿主机的网络栈——容器里看到的网卡、IP 就是宿主机的。
# 启动方式:--network host
docker run -d --network host --name node-exporter prom/node-exporter特点:
- 零 NAT 开销,性能最接近裸机,延迟最低(桥接模式大约有 5%–8% 的性能损耗)。
- 不需要
-p端口映射:容器进程监听什么端口,宿主机就有什么端口,-p参数会被忽略。 - 代价是隔离性归零:端口冲突(宿主机 80 被占,容器就起不来)、被入侵的容器可以直接操作宿主机网络栈、安全风险最高。
- 平台限制:Docker Desktop(macOS/Windows)不支持 host 模式(除非开启实验性支持),因为容器实际跑在虚拟机里。
适合:监控采集器(Prometheus Node Exporter)、需要大量端口或抓包的场景、对性能极端敏感的应用。
None:完全断网
容器只有回环接口(lo),没有网卡、没有 IP、无法访问外网,也拒绝外部访问。适合对安全性要求极高、或者根本不需要网络的场景:
docker run --network none --name sandbox my-image:latest典型用途:离线计算任务、安全沙箱、纯数据处理容器(只通过挂载卷交换数据)。
Container:与另一个容器共享网络
不常用但值得一提:新容器与指定容器共享同一个网络命名空间,两者通过 127.0.0.1 互访,像是「同一个容器里的两个进程」。
docker run --network container:db --name sidecar my-sidecar:latest典型场景:边车(sidecar)模式,比如把日志采集、网络代理注入到业务容器旁边,不暴露多余端口。
Overlay:跨主机通信
工作原理:基于 VXLAN 隧道技术,在多个宿主机之间建立二层虚拟网络,容器就像连在同一个交换机上,可以跨主机按名字互相通信。需要先初始化 Swarm 集群:
# 集群中任一节点初始化 Swarm
docker swarm init
# 创建 overlay 网络
docker network create -d overlay --attachable my-overlay-net
# 在不同宿主机上用服务方式部署,即可跨主机互通
docker service create --network my-overlay-net --name web --replicas 3 nginx注意事项:需要开放 2377(Swarm 管理)、7946(节点间通信)、4789(VXLAN)三个端口;VXLAN 封装会增加约 50 字节包头,建议把 MTU 调低(如 1450)避免分片丢包;启用加密(--opt encrypted)会有额外性能开销。
适合:Docker Swarm 集群、跨主机的微服务架构。注意:如果用的是 Kubernetes,网络层通常交给 CNI 插件(Calico、Cilium 等),不会再直接用 Docker 的 overlay。
Macvlan 与 Ipvlan:让容器接入物理网络
Macvlan:给每个容器分配独立的 MAC 地址和物理局域网 IP,容器看起来就像一台插在交换机上的真实设备,无需 NAT、无端口映射,性能和延迟接近物理机:
# 假设宿主机网卡是 eth0,局域网 192.168.1.0/24
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
-o parent=eth0 my-macvlan-net
docker run -d --network my-macvlan-net --ip 192.168.1.100 --name legacy my-app:latest适用:老应用迁移(原来跑在物理机/VM 上,IP 固定)、IoT 设备、VLAN 网络集成。
但有两个坑:
- 宿主机无法直接访问 macvlan 容器(内核限制),需要额外创建 macvlan shim 接口做桥接,麻烦。
- 部分云厂商 VPC 环境禁止 macvlan(网络策略限制),且交换机可能需要开启混杂模式。
Ipvlan:与 macvlan 类似,但所有容器共享宿主机的 MAC 地址,只分配独立 IP。MAC 地址消耗少、对传统交换机兼容性更好,适合需要大量容器接入同一子网的场景(比如一个网段跑上千个容器)。注意 ipvlan 有 L2 和 L3 两种模式,L3 模式下宿主机需开启 ip_forward。
模式对比总表
| 维度 | Bridge | Host | None | Overlay | Macvlan/Ipvlan |
|---|---|---|---|---|---|
| 网络隔离 | 好(可按网络隔离) | 无 | 完全隔离 | 好 | 弱(二层直连) |
| 性能 | 中(NAT 损耗约 5%–8%) | 最好(近裸机) | — | 中(VXLAN 开销) | 很好 |
| 跨主机 | 否 | 否 | 否 | 是 | 否(仅同局域网) |
| NAT | 是 | 否 | 否 | 是(可加密) | 否 |
| 按名字解析 | 自定义网络支持 | 用宿主机 DNS | 无 | Swarm 服务发现 | 走物理网络 DNS |
| 配置复杂度 | 低 | 极低 | 高(需手动配) | 高 | 中 |
| 端口冲突风险 | 低 | 高 | — | 低 | 低 |
如何选择:一张决策路径图
选型不必纠结,按下面这棵决策树走就行:
需要跨主机通信吗?
├─ 是 → Overlay(Swarm 集群)
└─ 否
├─ 需要容器像物理设备一样接入局域网?
│ ├─ 是 → Macvlan(需 MAC 数量多时用 Ipvlan)
│ └─ 否
│ └─ 需要极致性能且不在乎隔离?
│ ├─ 是 → Host(注意端口冲突)
│ └─ 否
│ ├─ 需要完全断网隔离? → None
│ └─ 默认场景 → 自定义 Bridge(90% 的答案)按场景直接抄作业
| 场景 | 推荐模式 | 理由 |
|---|---|---|
| 单机多容器应用(Web + DB + Redis) | 自定义 Bridge | 名字互访、网络隔离、最省心 |
| 开发调试 | 自定义 Bridge | 与生产一致,便于排查 |
| 监控采集(Node Exporter 等) | Host | 需要抓宿主机指标,性能优先 |
| 对延迟极度敏感(游戏服务器、压测) | Host 或 Macvlan | 无 NAT 开销 |
| Swarm 集群 / 跨主机微服务 | Overlay | 唯一跨主机内置方案 |
| 旧应用迁移、固定 IP、IoT | Macvlan | 模拟物理设备,IP 不变 |
| 大量容器接入同一子网 | Ipvlan | 共享 MAC,地址不浪费 |
| 安全沙箱、离线计算 | None | 完全隔离,拒绝一切网络 |
实战:docker compose 里的网络配置
Compose 文件里的网络配置是最高频的实战场景。默认情况下 Compose 会为每个项目自动创建专属网络,所有服务互相可见,且自带服务名 DNS:
services:
web:
image: nginx
ports:
- "8080:80"
api:
image: my-api:latest
db:
image: mysql:8.0
# 不写 networks 时,三个服务都在项目默认网络里,
# api 容器里直接 curl http://db:3306 即可访问数据库需要更精细的隔离时,可以显式拆分网络——比如让 web 能访问外部互联网,而 db 完全内网化:
services:
web:
image: nginx
ports:
- "8080:80"
networks:
- frontend
api:
image: my-api:latest
networks:
- frontend
- backend
db:
image: mysql:8.0
networks:
- backend
networks:
frontend: {}
backend:
internal: true # backend 网络禁止访问外网,db 只能被 api 访问internal: true 是生产环境加固数据库、消息队列的常用手段——数据库容器彻底断外网,即使被攻破也无法外传数据。
常见坑与避坑建议
坑 1:默认 bridge 上靠 IP 通信,重启后 IP 变了。 解法:一律使用自定义 Bridge 网络 + 容器名通信,禁止裸 IP。
坑 2:host 模式端口冲突导致容器起不来。 解法:先 ss -lntp 确认端口占用;能用 bridge 映射就别用 host。
坑 3:macvlan 下宿主机访问不了容器。 解法:这是内核限制不是 bug,按官方文档添加 macvlan shim 接口,或改用 ipvlan。
坑 4:overlay 跨主机大流量丢包。 解法:检查 MTU,VXLAN 建议降到 1450 并统一各节点 MTU 配置。
坑 5:容器访问外网很慢或不通。 解法:检查宿主机的 net.ipv4.ip_forward 是否开启(sysctl net.ipv4.ip_forward),这是 NAT 出网的前提。
总结
- 默认选择永远是自定义 Bridge:它覆盖了 90% 的单机场景,容器名 DNS + 网络隔离 + 灵活插拔。
- 追求极致性能且能接受低隔离 → Host。
- 需要跨主机集群 → Overlay(Swarm)或交给 K8s 的 CNI。
- 需要接入物理网络、固定 IP → Macvlan,大量容器则用 Ipvlan。
- 安全隔离、完全断网 → None。
选型的核心不是「哪个最快」,而是「哪个匹配场景」。绝大多数时候,一个自定义 Bridge 网络就够了。
评论 (0)
暂无评论,快来抢沙发吧!