Docker 网络模式实战:bridge、host、macvlan 到底怎么选(2026)

结论先说:90% 的场景用自定义 bridge 网络就够了,需要容器像物理机一样出现在局域网时才上 macvlan,host 模式只留给性能极度敏感的少数服务;而最危险的坑是 Docker 的端口映射直接写 iptables,绕过了 UFW,你放行规则里没开的端口照样裸奔公网。 我在 2026 年 10 月给一台 Ubuntu 22.04(Docker 27)的机器做安全加固时才真正把这事想明白——之前一直以为 UFW 管着一切,后来发现容器映射的端口在 UFW 眼里根本不存在。这篇按「原理—实操—排错」的顺序,把三种模式讲透。

Docker 三种网络模式的决策链路

bridge、host、macvlan 到底差在哪?

三种模式的核心区别是容器用谁的 IP 出现在网络上:

维度 bridge(默认) host macvlan
容器 IP 网桥内网 IP(172.x) 直接用宿主 IP 独立局域网 IP(192.168.x)
端口暴露 需 -p 映射 不需要,直接监听 不需要,天然可达
容器间互访 同网络内按服务名互访 按宿主端口区分 局域网内直接互访
性能 经过 NAT,损耗 5-10% 最好,零转换 接近物理机
隔离性 最好 最差 中
典型场景 Web 服务、compose 全家桶 高性能代理、采集器 NAS 工具、需要固定 IP 的设备

一句话记忆:bridge 是「隔离小区+统一门牌」,host 是「住进宿主家里」,macvlan 是「给容器发一张独立门牌」。 日常先 bridge,有特殊理由才换,这是不变的原则。

三种模式怎么实操?

环境:Ubuntu 22.04 + Docker 27。四步走:自建 bridge、跑一个 host 服务、配 macvlan、最后是排错命令。

Docker 网络实操四步

第一步:自建 bridge 网络并让容器互访

默认的 bridge0 网络不支持容器名互访(只能 IP),这是默认网络和自定义网络最大的区别,也是很多人「容器之间 ping 不通」的原因:

$ docker network create appnet
$ docker run -d --name web --network appnet nginx:1.27
$ docker run -d --name db --network appnet -e POSTGRES_PASSWORD=xxx postgres:16
$ docker exec web ping -c 2 db
PING db (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.088 ms

--network appnet 之后,db 这个名字就是可解析的 DNS 记录。而 Docker Compose 默认会为每个项目创建自定义网络,所以 compose 里服务名互访天然可用——很多人不知道这个便利其实是「自定义网络」给的,手动 docker run 时就露馅了。

步骤 1:创建自定义 bridge 网络

第二步:host 模式跑对性能敏感的服务

$ docker run -d --network host --name node-exporter prom/node-exporter:v1.8.2
$ ss -tlnp | grep 9100
LISTEN 0 4096 *:9100 users:(("node-exporter",pid=1234,fd=3))

host 模式下容器直接用宿主的网络栈,没有 NAT 转换,-p 参数无效也不需要。我把它用在两类场景:一是 Prometheus 监控的 node_exporter——它要看宿主的真实网络指标,host 模式天然合适;二是流量大的代理转发类服务,省掉一层 NAT。风险也要说透:容器监听的任何端口都直接暴露在宿主上,绕过了端口映射这一层隔离,host 模式里跑的镜像必须信得过,且最好配合 --read-only 等加固参数。

步骤 2:host 模式运行服务

第三步:macvlan 给容器独立局域网 IP

场景:家里软路由上跑一个 NAS 工具,希望它像一台真机器一样出现在 192.168.1.x 网段,手机直接访问它的 IP,不经过任何端口映射:

networks:
  lan:
    driver: macvlan
    driver_opts:
      parent: eth0
    ipam:
      config:
        - subnet: 192.168.1.0/24
          gateway: 192.168.1.1

services:
  nas-tool:
    image: your-nas-tool:latest
    networks:
      lan:
        ipv4_address: 192.168.1.50

parent: eth0 指定容器流量从宿主哪块物理网卡出去,ipv4_address 必须是局域网里没被占用的地址(建议在路由器上做 DHCP 保留)。起来之后 ping 192.168.1.50 从局域网任何设备都能通。

但这里有一个经典反转:我一开始以为容器有了独立 IP 就万事大吉,后来发现宿主机自己反而 ping 不通这个容器了——宿主和 macvlan 容器在内核里是两条隔离的链路,这是 macvlan 的设计限制。 解法是在宿主上建一个「垫片」接口:

$ ip link add macvlan-shim link eth0 type macvlan mode bridge
$ ip addr add 192.168.1.240/32 dev macvlan-shim
$ ip link set macvlan-shim up
$ ip route add 192.168.1.50/32 dev macvlan-shim

建一次永久生效需要写进 netplan 或 systemd unit。垫片接口让宿主通过它「挂到」同一个 macvlan 上,宿主与容器之间就通了。

步骤 3:配置 macvlan 独立 IP

第四步:排查命令清单

日常排错四条命令覆盖 90% 的问题:

$ docker network inspect appnet
# 看成员列表、网段、网关——「容器为什么互访不通」先查它
$ docker exec web ping -c 2 db
# 容器间连通性 + DNS 解析
$ docker exec web wget -qO- http://db:5432
# 端口级验证,ping 通不代表服务端口通
$ iptables -L DOCKER -n | grep 8080
# 查 Docker 写进内核的端口转发规则

docker network inspect 的输出里我最常看 Containers 段——容器到底在不在网络里、IP 是多少,一眼定案。

步骤 4:网络排错命令

为什么容器端口绕过了 UFW?

根因:Docker 的 -p 端口映射不走 UFW 的规则链,而是直接在 iptables 的 DOCKER 链里插入转发规则,而这条链的优先级在 UFW 之前。 所以 ufw deny 6379 挡不住 -p 6379:6379 的 Redis——外部流量在 UFW 看到之前就已经被 DOCKER 链放行了。这就是那台加固机器上扫出来的问题:UFW 显示只开了 22/80/443,实际 nmap 能扫到七八个容器端口。

解法按优先级:

  1. 映射时只绑本机回环:-p 127.0.0.1:6379:6379,需要公网访问的交给反向代理统一入口,这是最干净的办法。
  2. 用 DOCKER-USER 链过滤:Docker 官方预留了这个链,用户规则写在这里:
$ iptables -I DOCKER-USER -p tcp --dport 6379 ! -s 10.0.0.0/8 -j DROP
  1. 装 ufw-docker 工具(github 上 gawel/ufw-docker),它帮你把 UFW 和 Docker 的规则协调好,省得手写 iptables。

千万不要用 "iptables": false 关掉 Docker 的 iptables 管理——这会导致容器出站转发全断,属于拆承重墙式解法。详细的规则模板在UFW + Docker 防火墙实战里写过,不重复。

怎么选,一张速查表

需求是「几个 Web 服务互相调用」——bridge + compose,完事。需求是「容器要在局域网当一台独立机器」——macvlan + 垫片接口。需求是「榨干性能或要看宿主网络栈」——host,同时接受它的隔离代价。至于某些教程鼓吹的「全部用 host 最快」,单机跑两三个容器的场景下 NAT 损耗根本感知不到,为这点性能放弃隔离不划算。如果容器网络通了但服务还是从公网访问不到,多半是没有公网 IP 的网络环境,那是另一个问题,用 FRP 或 Cloudflare Tunnel 解决。

常见问题

Q:compose 项目之间能互访吗?
A:默认不能,每个项目有独立网络。让它们加入同一个 external: true 的自定义网络即可,服务名照常互访,这是多 compose 项目协作的标准做法。

Q:macvlan 容器能上外网吗?
A:能。网关指向路由器,出站走正常路由。要注意的只是上面说的宿主互访问题,不是外网问题。

Q:host 模式下容器还能用 docker port 查端口吗?
A:不能,docker port 输出为空,因为不存在映射。用 ss -tlnp 在宿主上看,进程名就是容器内的进程。

Q:怎么确认某个端口到底有没有被 UFW 挡住?
A:别看 UFW 的规则输出,直接从外部机器 nmap -p 6379 你的服务器IP,或者 nc -zv。UFW 说没开但外部能连,就是 Docker 绕过链的现场。

跨主机的场景这篇没展开:单机的三种模式选完,多台机器之间的容器通信要走 overlay 网络或者干脆用 WireGuard 组网篇的思路把几台 VPS 串成私网——后者是我目前的主用方案,配置量小、无额外组件。最后留一张排错速查:容器互 ping 不通用 docker network inspect 看是否同网;端口映射不通用 docker port 核对;宿主机访问不到容器先查 UFW 的 DOCKER-USER 链。网络问题九成出在这三处。

常见问题

相关阅读

上一篇 从 950MB 瘦到 152MB:一次 Docker 镜像瘦身的逐层实测(2026)