Docker 网络模式:bridge 和 host 怎么选

Docker 的 bridge、host、none 三种网络模式到底什么区别?小够用大白话讲清原理、怎么选,以及端口映射时最容易踩的坑。

我是小够。Docker 玩到一定程度,都会遇到网络问题:容器之间怎么互通?端口怎么映射?bridge 和 host 选哪个?今天把 Docker 的网络模式一次讲明白。

先理解:容器网络在解决什么

每个 Docker 容器都是一座孤岛,网络模式决定的是:孤岛和外界怎么连。三种主流模式,bridge、host、none,覆盖 99% 的场景。

bridge:默认模式,大多数时候用它

docker run 不指定网络,默认就是 bridge。原理:Docker 在宿主机上建一座虚拟网桥(docker0),每个容器接一根虚拟网线到桥上,容器之间通过内网 IP 互通,访问外网走 NAT 出去。

特点:

  • 容器有独立的内网 IP(一般是 172.17.x.x),互相隔离。
  • 外界访问容器里的服务,要做端口映射:-p 8080:80,意思是宿主机的 8080 端口转发到容器的 80 端口。
  • 容器之间想互通,用容器名当主机名就行(同一自定义网络下),不用记 IP。
docker run -d -p 8080:80 --name myweb nginx

这行命令:后台跑个 nginx,宿主机 8080 端口映射到容器 80 端口,浏览器打开 服务器IP:8080 就能看到页面。

什么时候用 bridge:默认就用它。跑网站、跑应用、跑数据库,bridge 加端口映射,够用了。

host:和宿主机共用网络

--network host,容器直接用宿主机的网络栈,没有隔离:容器里开 80 端口,就是宿主机的 80 端口,不用映射。

特点:

  • 性能最好:少了一层 NAT 转发,延迟最低,吞吐最高。
  • 端口直接暴露:容器开什么端口,宿主机就开什么端口,省了映射的麻烦,也少了映射的困惑。
  • 容易冲突:两个容器都想用 80 端口,直接打架。端口规划得自己做好。
  • 隔离性差:容器能看到宿主机的网络,安全性比 bridge 低一档。

什么时候用 host:对网络性能敏感的(比如 DNS 服务、高并发网关)、端口特别多的(映射写到手酸)、或者某些必须用宿主机网络的特殊软件。普通网站、应用,老老实实 bridge。

none:没有网络,特殊场景

--network none,容器没有网卡,只能用回环地址自己跟自己玩。听起来没用,但有些场景要的就是隔离:跑一次性计算任务、处理敏感数据、不需要任何网络的批处理。用得少,知道有这个选项就行。

怎么选:一张决策表

场景选哪个
默认,不知道选啥bridge
跑网站、API、数据库bridge + 端口映射
对延迟敏感、高并发host
端口多到映射写不完host
完全不需要网络none
多容器互相调用自定义 bridge 网络(docker compose 默认就是)

新手记住一句就行:默认 bridge,性能不够再 host。别一上来就 host,端口冲突和安全问题够你喝一壶。

最容易踩的坑:端口映射的 0.0.0.0

跑容器时 -p 8080:80,默认绑定的是 0.0.0.0:8080,意思是所有网卡、公网可访问。本意只想内网用的数据库,一个 -p 3306:3306 就暴露到公网了,扫描器分分钟找上门。

两个习惯:

  1. 只映射必须公开的端口,数据库、管理后台这类,能不映射就不映射,容器之间内网互通就行。
  2. 公网暴露的端口,配合 UFW 防火墙收紧(本系列第一篇讲过),该限 IP 的限 IP。

docker compose:多容器的正确姿势

容器一多,docker run 参数写到崩溃,换 docker compose。一个 compose.yaml 把几个容器、网络、端口映射写在一起,一键起停:

services:
  web:
    image: nginx
    ports:
      - "8080:80"
  db:
    image: mysql:8
    environment:
      MYSQL_ROOT_PASSWORD: example

compose 默认给这些容器建一个自定义 bridge 网络,容器之间用服务名(web、db)直接互通,不用记 IP,不用手动映射内部端口。这是生产环境的标准姿势,早用早省心。

自定义网络:生产环境标配

默认的 bridge(docker0)有个缺点:容器重启 IP 会变,靠 IP 互通不靠谱。生产环境都用自定义 bridge 网络:

docker network create mynet
docker run -d --network mynet --name web nginx
docker run -d --network mynet --name db mysql:8

同一自定义网络下,容器之间用容器名当主机名直接互通,web 连 db 就写 db:3306,IP 变了也不怕。docker compose 默认就是这么干的,所以前面说 compose 是标准姿势。

容器互通的三种方式

  1. 容器名互通(推荐):同一自定义网络,用服务名直连,最省心。
  2. 端口映射经宿主机:A 容器映射到宿主机端口,B 容器连宿主机 IP 加端口。绕远,能用但不优雅。
  3. 共享网络栈:--network container:web,两个容器共用一套网络,localhost 互通。适合边车(sidecar)模式,比如主应用加日志收集。

第一种覆盖 90% 的场景,记住它就行。

overlay:多机器才用得上

单机玩 Docker,bridge 加自定义网络走天下。overlay 是给多台机器(Swarm / 跨主机)准备的,容器跨宿主机互通。个人用户基本用不上,知道有这个概念,面试…看文档时不懵就行。

实战:WordPress 的网络长什么样

services:
  wordpress:
    image: wordpress
    ports:
      - "8080:80"
    networks:
      - wpnet
  db:
    image: mysql:8
    networks:
      - wpnet
networks:
  wpnet:

wordpress 和 db 都在 wpnet 里,wordpress 连数据库写 db:3306。db 没有 ports 映射,外界连不进来,只能 wordpress 连,安全。8080 映射出来给访客。这就是典型的「前端暴露、后端内网」结构,举一反三,什么应用都这么搭。

排错:容器连不上怎么办

按这个顺序查:

  1. 在不在一个网络:docker network inspect 看两个容器是不是同一网络,不在就连不上。
  2. 端口对不对:容器内服务监听的是哪个端口,映射的是哪个,别搞混。服务监听 8080,你映射 80,转了个寂寞。
  3. 服务起来没:docker logs 看容器日志,服务没启动,网络再通也没用。
  4. 防火墙:宿主机 UFW 没放行映射的端口,公网进不来。内网互通不受 UFW 影响,别背锅给防火墙。
  5. DNS:自定义网络下容器名解析不了,检查网络模式对不对,默认 bridge 下容器名是解析不了的,必须自定义网络。

IPv6 与 Docker:一句话

宿主机有 IPv6,容器默认拿不到,要额外配置。个人用户先别折腾,IPv4 够用。等哪天 IPv6 真成刚需了,再回头看文档,官方文档写得很清楚。

生产环境 checklist

容器上生产前,对着过一遍:

  • 只映射了必须公开的端口
  • 数据库、管理后台没暴露公网
  • 用了自定义网络,服务名互通
  • 重要数据挂了 volume,重建容器不丢数据
  • 设了 restart: always,机器重启容器自启
  • 日志有轮转,别让 json 日志吃掉硬盘

最后一条展开说一句:Docker 默认日志不轮转,跑半年的容器,日志几个 G 很常见。compose 里加个 logging 配置,或者定期清,别等硬盘满了才想起来。

小够的唠叨

Docker 网络看着复杂,其实就三句话:默认 bridge 够用,端口映射想清楚,数据库别暴露公网。网络模式选错了,最多是性能差一点、麻烦一点;端口暴露错了,是安全问题。分清主次,别在 bridge 和 host 的选择上纠结半天,转头把数据库端口映射到了公网。compose 用起来,自定义网络是标配,这两样掌握了,Docker 网络这关就算过了。以后看到容器连不通,先想网络模式,再想端口映射,最后看防火墙,这个排查顺序能解决九成问题。剩下一成,日志里找答案,社区里问,别自己闷头试到半夜,效率最高,身体也重要,真的。