news 2026/9/11 4:04:15

Docker网络模式详解:从bridge到overlay的实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Docker网络模式详解:从bridge到overlay的实战指南

记得刚把第一版容器化应用推到服务器上时,我遇到的第一座山就是网络。明明容器起来了,应用却怎么都连不上数据库;端口映射也写上去了,外网依然访问不到服务。后来折腾了一两天,才把 bridge、host、overlay 这些概念理清楚。Docker网络模式说白了就是解决一件事:容器和容器、容器和宿主机、容器和外部世界之间如何通信,以及怎么用最合适的方式把它们连起来。这篇文章把我的实操经验整理出来,从原理到命令再到排查,尽量一次讲透,适合刚接触 Docker、或者被容器互联折腾到头晕的同学。

1. 动手之前,先搞清楚Docker网络到底长什么样

1.1 容器里的“网卡”从来都不是真的硬件

很多刚学 Docker 的朋友第一次看到容器里的 eth0 时,会产生一种错觉:这个容器好像就是一台独立的小机器。实际上,容器里那块网卡是一个虚拟网络设备,它通过一组名叫 veth pair 的“虚拟网线”挂到宿主机的 docker0 网桥上,再由宿主机负责转发数据。容器和容器之间,本质上是在同一个虚拟交换机里互相通信,只是每个容器被隔离在自己的网络命名空间内,单独的IP、路由表、防火墙规则都由内核按命名空间分别维护。

我们平时说的“网络模式”,本质上就是决定怎么给容器接上网线。用卧室打个比方:bridge 模式是给每个容器一个独立小房间,通过公用的走廊和门牌号(端口映射)对外交流;host 模式相当于直接把行李搬进客厅,和宿主机共享一扇大门,没有属于自己的房间;overlay 模式则是把多台物理机上的“小房间”用隧道连成一个小区局域网。

对网络命名空间这个概念还需要再展开一句:在 Linux 里,每个进程默认共享宿主机的网络栈,Docker 容器启动时会创建新的网络命名空间,里面拥有自己独立的路由表、iptables 规则和网卡。理解了这一点,后面排查容器网络问题时,心里才会有方向感。

1.2 安装完Docker默认有哪些网络

安装好 Docker 之后,不新建任何网络,直接执行docker network ls,你会看到三个内建网络。不同版本可能略有差异,但大体是这些:

网络名称驱动用途
bridgebridge默认的网络模式,容器会连接在 docker0 网桥
hosthost容器共享宿主机的网络命名空间
nonenull容器没有对外网络,只有回环接口

其中 bridge 是大部分场景默认使用的那一个。你用docker run启动容器时不写--network参数,它就落在 docker0 网桥的默认网段 172.17.0.0/16 里。需要提醒的是,默认 bridge 网络虽然方便,但不建议把生产容器长期挂在它上面,因为它不支持容器名自动DNS解析,也没有办法在启动时直接指定静态IP,跨主机容器都依赖宿主机端口映射,时间一长维护成本很高。

我自己的习惯是:只要容器的数量超过两个,就立刻创建一个自定义 bridge 网络,把容器划到同一个网络里,这样既可以用固定IP,又能用容器名互相访问。

1.3 不同宿主平台上的网络差异

如果你用的是 Docker Desktop,它其实是跑在一个虚拟化环境中,底层可能是 WSL2 或 Hyper-V,因此网卡和路由的呈现方式和 Linux 原生环境会有一些细微差别。最直观的表现是,在 Docker Desktop 上执行ifconfigip addr,你可能看到多个虚拟网卡,而容器网桥的网段也可能不是常见的 172.17。

但这不代表网络模式的逻辑变了。bridge 就是 bridge,端口映射的写法也一样,只是“宿主机”在开发者电脑上其实是个轻量虚拟机,暴露出来的端口通过虚拟化层转发到 Windows 或 macOS 上。我的建议是,本地初步验证可以用 Docker Desktop,但涉及 host 模式、overlay 网络或 iptables 调优时,最好准备一台 Linux 服务器来做最终测试,避免因为平台差异踩到隐形坑。

2. 五种网络模式怎么选:原理与适用场景

2.1 bridge模式:大宅里的独立小单间

bridge 是整个 Docker 网络体系的默认选择,也是最容易理解的一种模式。容器创建时,Docker 会在宿主机上新生成一对 veth 网卡,一端塞进容器的网络命名空间,成为容器里的 eth0,另一端挂载到 docker0 网桥上。docker0 就是一个虚拟二层交换机,所有挂在它下面的容器都能直接通信,数据包在二层通过 MAC 地址转发,走的是同一套 Linux 内核桥接逻辑。

这个模式最大的优势是隔离和安全。每个容器有自己的 IP,有自己的 iptables 链,别人不能随便进入;宿主机上如果跑了很多容器,每个服务像住在独立小单间里,互不干扰。再加上端口映射,外部流量会通过 docker0 上配置的 DNAT 规则转发到容器 IP 上。

但要注意几个细节。一是容器 IP 默认从 docker0 网段动态分配,重启容器后 IP 大概率会变,依赖固定 IP 的服务不能用默认网络。二是默认 bridge 网络里,容器之间不能通过容器名进行 DNS 解析,只能靠--link或 IP 访问。三是性能上有一定损耗,因为流量要从容器 eth0 经过 veth pair 到网桥,再做 NAT 到外部,每走一层都有内核处理开销。

我自己做开发的时候,绝大多数容器都会放到自定义 bridge 网络里,因为自定义网络同样基于 bridge 驱动,却额外提供了内置 DNS 解析、指定子网网段和固定 IP 的能力,基本够日常项目用了。

2.2 host模式:直接住进宿主机的客厅

host 模式会把容器直接放进宿主机的网络命名空间,容器里不生成自己的网卡和 IP,而是直接使用宿主机的 IP、路由和端口表。启动一个 nginx 容器,如果用--network host,它不会有 172.17 之类的地址,而是直接监听宿主机的 80 端口,进程看起来就像在宿主机上直接跑了一样。

这种模式的优点就是性能好,几乎没有虚拟网络带来的转换开销;同时端口管理最省心,不需要写-p 8080:80之类的映射规则,因为容器服务默认就暴露在宿主机的所有网卡上。适合对网络延迟敏感的监控服务、大流量转发服务,以及需要同时绑定很多端口的应用。

但缺点也很明显。由于容器和宿主机共用端口表,一个容器占用了 80 端口,宿主机或者其他容器就不能再用 80 端口了,存在端口冲突风险。同时 Docker 的端口映射功能会失效,如果宿主机上有多套环境,容易造成端口抢占。此外,使用 host 模式时容器内的 /etc/hosts 和 DNS 配置也会继承宿主机的,自定义解析会比较麻烦。

所以 host 模式的使用场景其实很窄,我一般只在调试某些网络应用、做网络性能压测时才会临时用它。

2.3 none模式与container模式:两个容易被忽略的选项

none 模式很好理解,容器启动后不会配置任何网络,只有回环接口 lo。它也不是什么黑魔法,就是容器网络命名空间里只留一个环回接口而已。适合运行离线任务、对安全要求极高的临时工具,或者不想让容器产生流量出入的场景。执行docker run -it --network none alpine sh,进去之后会发现只能看到 lo,没有 eth0。

container 模式则是把新容器加入到另一个容器的网络命名空间里,两者共享同一块网卡、同一个 IP 和端口表。比较典型的用途是 Sidecar 模式,比如启动一个监控抓包容器,和业务容器共用网络命名空间,直接抓取业务容器的请求流量;或者让 Nginx 反向代理容器和前端应用容器共用一个 IP,减少暴露的端口数。

使用 container 模式有一个容易被忘记的注意点:两个容器的生命周期会耦合,一旦被共享网络的那个容器重启,共享方也会受到影响,而且监控容器不能随意移除。所以我在实际项目里用得不多,但偶尔用来调试网络,确实很方便。

2.4 overlay模式:跨主机集群的默认选择

如果你已经进入多机部署阶段,容器落到了不同的宿主机上,还想让它们像在同一个局域网里一样通信,就需要 overlay 网络。Docker Swarm 模式下,docker network create -d overlay会创建一个跨主机的虚拟二层网络,底层数据通过 VXLAN 隧道封装,在三个宿主机之间传输时,并不要求它们的物理网络处于同一个二层网段,只要三层路由能通就行。

overlay 的优点是天然解决了跨主机容器访问的问题,容器之间可以直接使用服务名通信,即使容器漂移到不同节点,服务发现也能跟上。缺点是性能比 host 模式差,因为每个数据包多了一层 VXLAN 封装和解封装开销,而且需要 Swarm 节点之间保持时间同步。备选方案还有接入 Calico、Flannel 等网络插件,但一般不用自己做,Kubernetes 里会帮你管理。

对大多数中小项目,我个人建议不要一上来就上 overlay。先把容器合理地放到同一台宿主机上的自定义 bridge 网络里,通过端口映射对外提供能力,等真的遇到跨主机集群需求了,再考虑 Swarm 或 Kubernetes 自带的网络方案。

下面是一个对比表,方便快速选型:

网络模式是否独立IP跨主机通信端口映射适合场景
bridge不支持(默认)支持单机多容器、标准服务部署
host依赖宿主机不支持高性能、端口直通
none无网络不支持不支持离线任务、安全隔离
container共享其他容器依赖被共享容器不支持Sidecar、调试抓包
overlay支持支持Swarm/Kubernetes集群

3. 网络模式配置实操:命令行与Compose两手抓

3.1 docker run时指定网络模式

最直接的配置方式就是docker run命令的--network参数。举几个常见写法:

docker run -d --name web-host --network host nginx docker run -d --name web-bridge -p 8080:80 nginx docker run -d --name web-none --network none nginx

注意,上面的 web-bridge 虽然没有写--network bridge,但默认就是 bridge;端口映射的写法是-p 宿主端口:容器端口,如果希望端口只暴露在某个网卡上,可以写成-p 10.0.0.5:8080:80,这在公司服务器上有多个网卡时非常实用。

还有一个容易踩的坑:使用 host 模式后,再写-p似乎不会报错,但映射不会生效。因为容器直接用宿主机的端口表,从设计上就不需要再次转发,很多人一开始会在这上面浪费半天时间。另外,如果容器启动时指定的端口在宿主机上已经被占用,会直接报端口冲突错误,解决方案要么换端口,要么改用 bridge 模式。

3.2 自定义bridge网络:固定IP与别名是关键

当你需要让容器通过容器名互相访问时,默认 bridge 网络就不够用了。创建自定义网络的命令是这样的:

docker network create -d bridge --subnet=172.20.0.0/16 --gateway=172.20.0.1 mynet

-d bridge指定驱动,--subnet指定子网,--gateway指定网关,这些参数不加也能创建,但生产环境我建议显式写出来,方便规划网段,避免和公司的物理网络或其他容器网段冲突。

创建好之后,有两种方式把容器加入网络。一种是在启动时直接指定--network mynet

docker run -d --name mysql8 --network mynet --ip 172.20.0.10 -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0

另一种是容器已经在运行了,再动态加入:

docker network connect mynet webapp

如果你想让容器在网络中有一个好记的名字做 DNS 解析,可以给容器起一个别名。同一自定义网络里的容器用容器名就能互相 ping 通,这是最推荐的做法。

注意:固定IP的地址必须落在网络的子网范围内,而且不能和网关、其他容器冲突。容器停止后 IP 通常不会立刻释放给其他容器,影响了服务连续性,这时重启容器即可恢复使用,不需要操心底层网络清理。

3.3 docker compose里的网络配置写法

docker compose 已经成为项目部署的主流方式,网络配置也非常直观。以三个服务为例:

version: "3.8" services: mysql: image: mysql:8.0 ports: - "3306:3306" networks: app-net: aliases: - mysql-db redis: image: redis:7 ports: - "6379:6379" networks: - app-net app: image: my-app:1.0.0 ports: - "8080:8080" networks: - app-net networks: app-net: driver: bridge ipam: config: - subnet: 172.21.0.0/16

在这里,mysql、redis 和 app 都加入了 app-net 这个自定义网络。compose 会在网络内自动做 DNS 解析,app 容器里直接使用mysql-db作为主机名就能连上 MySQL,不需要关心 MySQL 的容器 IP 是多少。通过在 aliases 里配置别名,服务在切换镜像版本时,主机名可以保持不变,改动成本更低。

如果想让 compose 项目使用已经存在的、由命令行创建的网络,可以在 network 定义里加 external 参数:

networks: mynet: external: true

这样就不用每次启动项目都去创建一个新网络,多个项目之间也能共享同一套容器网络,对于需要跨 compose 项目通信的场景特别有用。

4. 实战:一个应用容器连上MySQL和Redis

4.1 创建自定义网络并部署数据库

假设我手上有一个 Java 应用,需要连接 MySQL 和 Redis。这个场景在部署容器化应用时非常典型,很多人直接用 localhost 连接,结果发现容器里根本没有 MySQL 服务,报出 connection refused。正确做法是让所有相关容器处在同一个用户自定义 bridge 网络里。

先创建一个名叫 app-net 的网络:

docker network create -d bridge --subnet=172.22.0.0/16 --gateway=172.22.0.1 app-net

然后启动 MySQL 和 Redis,都指定加入这个网络:

docker run -d --name mysql8 \ --network app-net \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=root123 \ mysql:8.0 docker run -d --name redis7 \ --network app-net \ -p 6379:6379 \ redis:7

这一步做的关键事情是:把两个服务的容器 IP 规划到 app-net 网段,同时通过-p把数据库端口暴露到宿主机,方便本机调试用工具连接。如果公司服务器安全要求高,端口映射可以只绑定内网 IP,例如-p 192.168.1.20:3306:3306,避免数据库接口直接暴露到公网。

4.2 应用容器通过服务名连接数据库

应用容器的启动命令其实不需要写 IP,直接用服务名:

docker run -d --name myapp \ --network app-net \ -p 8080:8080 \ -e MYSQL_HOST=mysql8 \ -e REDIS_HOST=redis7 \ my-app:1.0.0

配置里的jdbc:mysql://mysql8:3306/xxx就能直接解析到 MySQL 容器,即使容器重启导致 IP 变了,连接串也不用改。这就是自定义网络内置 DNS 带来的最大价值:让容器之间通过稳定的逻辑名称通信,而不是依赖随时可能变化的 IP。

也许你会问:为什么容器能被 mysql8 这个名字解析到?因为 Docker 内置了一个 DNS 服务,运行在 127.0.0.11,当容器启动时,Docker 会把容器名和 IP 注册进这个 DNS,并把容器内的 resolv.conf 指向它。但默认的 bridge 网络并没有启用这个内置 DNS 服务,只有用户创建的自定义 bridge 网络才支持,这也是官方一直鼓励大家用自定义网络的原因。

4.3 宿主机和外部客户端如何访问容器

容器之间的通信解决了,宿主机要访问容器,也有几种办法。最常用的是端口映射,上面已经写过多次。第二种是直接用容器 IP 访问,前提是宿主机和容器处于同一个网络,而且能路由到 docker0 或自定义网桥,通过docker inspect -f '{{.NetworkSettings.IPAddress}}' mysql8可以拿到容器 IP。

第三种是我私藏的调试大招:用docker exec直接进入容器,在容器内用curl测试服务。比如要确认应用容器是否能访问 MySQL,可以执行docker exec -it myapp sh,然后在里面执行:

mysql -h mysql8 -P 3306 -uroot -p

如果网络有问题,在容器内用pingtelnet验证连接性,配合tcpdump抓包,基本能把问题定位到具体环节。宿主机访问容器是宿主机去访问 docker0 网桥地址,容器访问宿主机是直接访问宿主机的物理网卡 IP,理解这几个流向对排查很有帮助。

比如一边在容器里执行curl http://你的宿主机IP:8080,另一边在宿主机上执行tcpdump -i any port 8080,能非常直观看到流量从容器进出时的路径。

5. 踩过的坑:网络问题排查命令与经验

5.1 容器访问外网失败的排查思路

容器内部需要访问外网(比如拉取 Maven 依赖、调用第三方 API)时,如果发现不通,我一般按这个顺序排查。第一步看宿主机的 IP 转发是否开启,执行sysctl net.ipv4.ip_forward,如果输出是 0,容器内部的数据包根本没被宿主机转发出去,需要临时设置sysctl -w net.ipv4.ip_forward=1,并写入 /etc/sysctl.conf 防止重启失效。

第二步检查容器里的 DNS 配置,进入容器后看 /etc/resolv.conf,如果 DNS 指向了宿主机无法访问的地址,容器解析域名就会超时。常规做法是把宿主机 /etc/docker/daemon.json 里的 dns 配置成公共 DNS,例如 114.114.114.114 或 223.5.5.5,重启 Docker 后生效。

第三步检查 iptables POSTROUTING 链里有没有对应的 MASQUERADE 规则。Docker 默认通过 MASQUERADE 做 SNAT,让容器流量伪装成宿主机 IP 出去。如果防火墙软件把它清了,流量就出不去,这时候重新设置 DOCKER 的 iptables 链,或者重启 docker 服务等待它自动补齐。

5.2 端口映射不生效的iptables问题

端口映射做了却访问不到服务,首先要确认容器本身是否正常运行:docker ps查状态,在容器内部验证服务端口。如果容器正常而宿主机访问不了,多半是 iptables 规则出问题。

Docker 的端口映射依赖两条核心 iptables 链:DOCKER 链负责 DNAT,把宿主机端口流量转给容器 IP;docker0 所在网桥上还有一条 FORWARD 链路来控制转发。很多公司服务器会放一套自己的安全加固脚本,顺手清掉了 DOCKER 链,于是端口映射立刻失败。这时可以执行iptables -t nat -L -n -v查看 NAT 表,重点看 DOCKER 链里有没有 DNAT 记录。

如果确定规则被清了,最专业的做法是别自己手补,直接systemctl restart docker让 Docker 重建规则链。需要注意的是,重启 Docker 会把所有容器一起重启,生产环境操作前先评估影响。这类问题我在容器编排平台和安全管理软件共存的服务器上遇到过很多次,建议优先排查顺序是:容器状态、docker logs、宿主机 netstat 端口监听、iptables NAT 链。

5.3 容器间通信超时:DNS与网络隔离

容器 A 访问容器 B 的服务名,有时会解析出一个奇怪的 IP,连接超时。排查思路是先看两个容器是否真的在同一个自定义网络里:docker network inspect mynet查看 Connected 容器列表。如果容器在不同网络里,即使 IP 能 ping 通,端口也可能访问不了,因为它们路由路径不一样。

然后检查容器内的 DNS,执行cat /etc/resolv.conf,看到 nameserver 127.0.0.11 说明是 Docker 内置 DNS,这时用nslookup 服务名测试解析结果。如果解析出的是目标容器 IP,再在源容器里用curl -v测试目标端口,观察是超时还是拒绝,超时大概率是路由或防火墙问题,拒绝则是目标服务端口没起来。

还有一个容易被忽略的坑:在自定义网络中给容器手动指定固定 IP 后,如果容器被删除重建,旧 IP 可能在一段时间内还出现在 Docker DNS 缓存里,导致新的容器解析到旧 IP,连接失败。这种情况等缓存过期或重启相关容器就能恢复正常,不用纠结太久。

5.4 跨主机容器互通:从默认网段聊起

一旦容器分布在多台宿主机上,默认 bridge 网络的容器 IP 基本等于废的,因为 172.17.x 只在当前宿主机内部有意义。除非你在宿主机路由表里自己写静态路由,否则跨主机的容器 IP 是互不可达的。这也是很多人从单机部署转向集群部署时遇到的第一个认知转折点。

跨主机的正规做法是用 overlay 网络,以 Swarm 为例会创建一个跨节点共享的二层网络,容器在这个网络里拥有全局唯一 IP,节点之间用 VXLAN 封装通信。普通用户如果没有大规模集群需求,最简单的办法就是不要追求容器 IP 跨机互通,而是把容器提供的服务通过宿主机端口映射出来,让客户端统一走“宿主机 IP + 端口”的访问路径。这是很多中小型项目的务实选择。

5.5 我常用的几个网络调试小命令

最后一个实操小节,把我工具箱里的调试命令分享给大家。除了上面说过的docker network inspectdocker exectcpdump,还有几个命令非常常用:

  • docker network ls:定期查看网络层面的变化,确认容器所属网络是否符合预期。
  • ip addr show:在宿主机上查网卡和网桥地址,确认 docker0 是否存在,veth 接口是否绑上。
  • iptables -t nat -L -n -v:排查端口映射和 SNAT 问题,尤其是看到链为空时立刻警惕。
  • ip route:查看路由表,确认容器网段是否被正确路由。
  • nsenter -t <容器PID> -n:进入容器的网络命名空间,查看容器自己的路由表和iptables规则。

如果真的要在容器里抓包,推荐启动一个临时容器,加上--network container:业务容器参数,共享业务容器网络,然后用tcpdump -i eth0 -nn 'tcp port 3306'抓数据库请求。这样不用事先在业务容器里安装任何工具,也不会影响业务容器现有的网络环境。

最后说一句我个人的体会。Docker 网络模式其实不复杂,核心无非是“容器到底用谁的网卡”和“数据包怎么转发”这两件事。刚接触时,不用急着研究 overlay 和 VXLAN,先把自定义 bridge 网络玩熟练,把端口映射、容器名解析、固定 IP 这几个日常能力吃透,就能覆盖绝大多数项目需求。我踩过最深的坑,就是习惯用 IP 去连接服务,容器一重启就全断了;后来改成用服务名和别名通信,整个系统一下子稳定很多。做网络配置时,也别只在 Docker Desktop 上验证,最后一定在 Linux 主机上再跑一遍,两边行为有细微差别。希望这篇经验能帮你少走一点弯路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 4:02:45

TransXNet图像分类实战:混合架构原理与PyTorch实现

简介&#xff1a;面向图像分类实战需求的TransXNet完整工程包&#xff0c;以transxnet_t为例演示如何将Transformer风格网络应用于植物分类。相比Swin-T&#xff0c;TransXNet在ImageNet-1K上以更低计算成本实现更高精度&#xff0c;此工程在植物数据集上达到96%以上的识别准确…

作者头像 李华
网站建设 2026/9/11 4:02:36

2026年AI开发技能生态与云原生实践

1. 2026年AI开发技能生态全景观察最近整理2026年Q2的AI技能安装量数据时&#xff0c;发现整个开发者生态正在经历显著变化。根据Vercel和Microsoft Azure平台的最新统计&#xff0c;排名前十的AI技能呈现出三个明显特征&#xff1a;低代码化、垂直场景化和云原生优先。这些技能…

作者头像 李华
网站建设 2026/9/11 3:59:16

30B模型跑在24GB显卡上:本地Agent常驻部署全指南

Muse Glimmer 发布后&#xff0c;我所在的技术群里第一反应不是“又多了一个模型”&#xff0c;而是“30B 参数又能跑在 24GB 显卡上&#xff0c;这回本地 Agent 真可以常驻了”。如果你最近也在关注 Muse Glimmer、30B 参数、24GB 显卡、本地 Agent 这几个关键词&#xff0c;说…

作者头像 李华
网站建设 2026/9/11 3:58:14

LeetCode链表题精讲:移除元素、设计链表、反转链表

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 3:57:54

开源数字人工具 Duix.Avatar 本地部署完整实战教程

开源数字人工具 Duix.Avatar 本地部署完整实战教程 【免费下载链接】Duix-Avatar &#x1f680; Truly open-source AI avatar(digital human) toolkit for offline video generation and digital human cloning. 项目地址: https://gitcode.com/GitHub_Trending/he/Duix-Ava…

作者头像 李华