Docker 网络模式详解:bridge/host 生产选型

Docker 网络模式是容器化部署里最容易被忽略、却最容易翻车的一环。同一个镜像,用 bridge 跑通了、换 host 就端口冲突;开发机容器互相 ping 得通、上了生产却解析不了服务名。本文把 bridge / host / none / container 四种单机网络模式,加上 overlay 与 macvlan 两种进阶驱动,从原理讲到实测,最后给出一张可以直接照着抄的生产选型表。

一、先看全景:Docker 到底有几种网络驱动

Docker 的网络能力由 网络驱动(network driver) 提供。装好 Docker 后默认会创建三个网络,可以直接列出来:

# 查看当前所有网络
docker network ls
# NETWORK ID     NAME      DRIVER    SCOPE
# 9f2a1c4b8e11   bridge    bridge    local
# 3d7e0a9f2c55   host      host      local
# 6b1c8d3a7e90   none      null      local

# 查看 bridge 网络的详细配置(网段、网关、已连接容器)
docker network inspect bridge

其中 bridgehostnone 是单机三大基础模式,另外还有两个常用但需要手动创建的驱动:overlay(跨主机,Swarm/K8s 场景)和 macvlan(容器直接拿局域网 IP)。此外 --network container:<name> 这种”共享网络栈”的用法,习惯上也被称作 container 模式。

二、bridge 模式:默认选项,也是最多误解的地方

不加 --network 参数时,容器就跑在默认的 bridge 网络上。Docker 会在宿主机创建一张名为 docker0 的虚拟网桥,每启动一个容器就生成一对 veth 设备:一端放进容器的网络命名空间(表现为容器里的 eth0),另一端挂到 docker0 上。容器出网靠 NAT(SNAT/MASQUERADE),外部访问容器靠 DNAT 端口映射。

默认 bridge 与自定义 bridge 的关键差异

这是生产环境最该记住的一条:默认 bridge 网络不提供容器名 DNS 解析,自定义 bridge 提供。 很多人抱怨”容器之间用服务名连不上数据库”,根因就是还挂在默认 bridge 上。实测对比:

# 场景 A:默认 bridge —— 用容器名解析会失败
docker run -d --name db-a redis:7-alpine
docker run --rm alpine sh -c "ping -c1 db-a"
# ping: bad address 'db-a'    ← 解析不了

# 场景 B:自定义 bridge —— 内置 DNS 生效
docker network create --driver bridge \
  --subnet 172.28.0.0/16 --gateway 172.28.0.1 app-net

docker run -d --name db-b --network app-net redis:7-alpine
docker run --rm --network app-net alpine sh -c "ping -c1 db-b"
# 64 bytes from 172.28.0.2: seq=0 ttl=64 time=0.089 ms   ← 成功

# 已运行的容器也可以热接入 / 断开
docker network connect app-net db-a
docker network disconnect bridge db-a

除了 DNS,自定义 bridge 还带来两个好处:一是网络隔离,不同业务放在不同网络里,彼此天然不可达;二是网段可控,避免 172.17.0.0/16 与公司内网冲突(这是混合云环境的高频事故)。

端口映射背后发生了什么

-p 8080:80 并不是”把容器端口搬到宿主机”,而是往 iptables 的 nat 表里插了一条 DNAT 规则。可以直接验证:

docker run -d --name web -p 8080:80 nginx:alpine

# 查看 Docker 自动生成的 DNAT 规则
sudo iptables -t nat -L DOCKER -n --line-numbers
# DNAT  tcp  --  0.0.0.0/0  0.0.0.0/0  tcp dpt:8080 to:172.17.0.3:80

# 只监听本机回环,不对外暴露(配合反向代理的推荐做法)
docker run -d --name web-safe -p 127.0.0.1:8081:80 nginx:alpine

注意最后那条:把端口绑定到 127.0.0.1,再由宿主机上的 Nginx 反代进来,比直接 -p 80:80 暴露安全得多,也方便统一做 HTTPS 与限流。具体配置可以参考站内这篇 Nginx 反向代理完整配置:负载均衡 + HTTPS + 缓存优化

另一个常见坑:-p 的 DNAT 规则优先级高于 ufw/firewalld 的 INPUT 规则,也就是说你以为防火墙拦住了,实际端口已经全网可达。要么绑回环,要么在 /etc/docker/daemon.json 里设 "iptables": false 并自行管理规则(后者需要自己补 NAT,慎用)。

三、host 模式:拿隔离性换性能

--network host 让容器不创建独立网络命名空间,直接复用宿主机的网络栈。容器里 ip addr 看到的就是宿主机的网卡,listen 80 就是真的占用宿主机 80 端口。

# host 模式:-p 参数会被忽略并给出警告
docker run -d --name web-host --network host nginx:alpine
# WARNING: Published ports are discarded when using host network mode

# 容器内看到的就是宿主机网卡
docker exec web-host ip -brief addr
# lo    UNKNOWN  127.0.0.1/8
# eth0  UP       192.168.10.21/24     ← 宿主机地址

# 端口直接落在宿主机上
ss -lntp | grep :80

优点很明确:没有 NAT 转换和 veth 转发开销,在高并发短连接、大流量转发、UDP 密集型场景(如网关、DPDK 类应用、Prometheus node_exporter、日志采集 Agent)延迟更低、吞吐更高。代价是三条硬伤:

  • 端口冲突:同一宿主机不能跑两个都监听 80 的容器,横向扩容受限。
  • 隔离性丧失:容器被攻破后可直接访问宿主机所有本地监听服务(包括只绑 127.0.0.1 的 MySQL/Redis)。
  • 可移植性差:Docker Desktop(macOS/Windows)上 host 模式行为与 Linux 不一致,本地调通不代表线上可用。

四、none 与 container 模式:两个小众但有用的选项

none:只有 lo 的绝对隔离

--network none 会给容器一个独立的网络命名空间,但只配 lo 回环,没有任何外部连通性。适合纯计算型任务:离线数据处理、批量图片转码、不受信任代码的沙箱执行。

docker run --rm --network none alpine sh -c "ip -brief addr; wget -T3 -qO- https://example.com || echo '无法出网(预期行为)'"
# lo  UNKNOWN  127.0.0.1/8
# 无法出网(预期行为)

container:共享网络栈的 sidecar 雏形

--network container:<name> 让新容器复用目标容器的网络命名空间,两者共享同一 IP 和端口空间,可以用 127.0.0.1 互访。这正是 Kubernetes 里 Pod 内多容器通信的底层机制(Pod 通过 pause 容器持有网络命名空间)。

# 主容器
docker run -d --name app-main -p 8080:80 nginx:alpine

# sidecar 共享其网络:可直接用 localhost 访问主容器服务
docker run --rm --network container:app-main alpine \
  sh -c "wget -qO- http://127.0.0.1:80 | head -3"
# <!DOCTYPE html> ...

# 注意:sidecar 不能再单独指定 -p / --hostname / --dns

五、进阶:overlay 与 macvlan

当业务从单机走向多机,就需要跨主机网络。overlay 基于 VXLAN 隧道,把多台宿主机上的容器接入同一个逻辑二层网络,需要先初始化 Swarm(或直接用 K8s CNI):

# 初始化 Swarm 后创建加密的 overlay 网络
docker swarm init --advertise-addr 192.168.10.21
docker network create -d overlay --attachable --opt encrypted app-overlay

# 普通 docker run 也能接入(靠 --attachable)
docker run -d --name svc-a --network app-overlay redis:7-alpine

macvlan 则给容器分配宿主机同网段的真实 MAC 与 IP,让容器在局域网里像一台独立主机,适合需要被 DHCP/监控系统直接发现的遗留系统迁移。缺点是需要交换机支持混杂模式,且宿主机默认无法访问自己的 macvlan 容器。

六、六种模式横向对比与生产选型

模式网络命名空间容器名 DNSNAT 开销隔离性典型场景
默认 bridge独立❌ 不支持临时调试,不建议生产
自定义 bridge独立✅ 支持单机生产首选,Compose 默认
host共享宿主机网关、采集 Agent、极致低延迟
none独立(仅 lo)最高离线计算、不可信代码沙箱
container共享目标容器随目标sidecar、抓包调试
overlay独立✅ 支持VXLAN 封装跨主机集群、Swarm 服务

落到实操,选型可以简化成四句话:

  1. 默认就用自定义 bridge。 用 Compose 时它会自动为你创建项目专属网络,服务名即域名,什么都不用配。
  2. 只有实测证明 NAT 是瓶颈时才上 host。 先压测拿数据,别凭感觉切。
  3. 对外只暴露入口层。 数据库、缓存等中间件不加 -p,只在内部网络里被访问。
  4. 多机就别硬撑单机网络。 该上 overlay 或直接迁 K8s,用 host + 手工端口规划维护成本极高。

一个符合上述原则的 Compose 片段(注意 redis 完全不对外暴露端口):

services:
  api:
    image: myapp/api:1.4.0
    ports:
      - "127.0.0.1:8080:8080"   # 只绑回环,由宿主 Nginx 反代
    networks: [backend]
    depends_on: [redis]
  redis:
    image: redis:7-alpine
    networks: [backend]         # 无 ports,外部完全不可达
    command: ["redis-server", "--appendonly", "yes"]

networks:
  backend:
    driver: bridge
    ipam:
      config:
        - subnet: 172.29.0.0/16

这种”中间件只走内网”的模式,也是 Redis 生产部署的基本安全前提——具体的缓存策略设计可以看 Redis 缓存设计:穿透、击穿、雪崩与最佳实践

七、五个高频踩坑速查

  • 容器里访问宿主机服务失败:不要写 127.0.0.1(那是容器自己)。Linux 上用 --add-host=host.docker.internal:host-gateway,或直接用 docker0 网关 IP(通常 172.17.0.1)。
  • 网段与内网冲突导致整台机器失联:在 /etc/docker/daemon.jsondefault-address-pools 预先规划,例如 {"base":"172.28.0.0/14","size":24}
  • DNS 解析慢或失败:容器默认继承宿主机 /etc/resolv.conf;宿主机若用 127.0.0.53(systemd-resolved),需显式指定 --dns 223.5.5.5
  • 容器 IP 重启后变了:bridge 网络 IP 不保证固定,永远用服务名而不是 IP 做互访;确需固定用 --ip 配合自定义网络。
  • 抓包无处下手:用 docker run --rm --network container:<目标> nicolaka/netshoot tcpdump -i any -nn,不必往业务镜像里塞调试工具。

小结

Docker 网络模式的选择本质是隔离性、性能、可运维性三者的取舍:自定义 bridge 是 90% 场景的正确答案,host 是有实测依据时的性能特化,none 和 container 各有其精准用途,overlay 则是走向集群的必经之路。把”自定义网络 + 服务名互访 + 入口层才暴露端口”这三条固化成团队规范,绝大多数容器网络故障就不会发生。想进一步把这套部署流程自动化,可以接着看 GitHub Actions 实战:从零搭建 CI/CD 流水线

上一篇 Vue3 + TypeScript 工程化:类型安全的前端架构
下一篇 大模型 RAG 进阶:混合检索与重排序优化实战