07. Docker 网络模式详解:bridge、host、none、overlay 一网打尽

07. Docker 网络模式详解:bridge、host、none、overlay 一网打尽 07. Docker 网络模式详解bridge、host、none、overlay 一网打尽上一篇你学会了用数据卷让容器数据不丢失。现在来想一个新问题多个容器之间怎么互相通信比如你的 Flask 应用容器要访问 MySQL 容器它们之间怎么找到对方Docker 的网络机制就是解决这个问题的。本文带你把 Docker 的四种网络模式全部搞明白每种模式都用实际例子跑一遍。▲ 容器之间怎么找到彼此答案就藏在 bridge、host、none、overlay 四种网络模式里。一、先看现象容器默认就能上网你可能已经发现了容器启动后自动就能访问外网docker run --rm alpine ping -c 3 baidu.com输出PING baidu.com (220.181.38.251): 56 data bytes 64 bytes from 220.181.38.251: seq0 ttl52 time4.231 ms 64 bytes from 220.181.38.251: seq1 ttl52 time4.102 ms 64 bytes from 220.181.38.251: seq2 ttl52 time4.089 ms这是怎么做到的答案是 Docker 在你安装它的时候就自动创建了一个虚拟网络。我们先来看看 Docker 到底管了哪些网络。二、查看 Docker 的所有网络docker network ls输出NETWORK ID NAME DRIVER SCOPE a1b2c3d4e5f6 bridge bridge local b2c3d4e5f6a1 host host local c3d4e5f6a1b2 none null local默认就有三个网络每个对应一种网络模式网络名模式一句话解释bridge桥接模式默认模式容器有自己的独立 IP通过虚拟网桥和宿主机通信host主机模式容器直接使用宿主机的网络没有隔离none无网络容器没有任何网络连接完全隔离还有一种不在默认列表里的网络名模式一句话解释overlay覆盖网络跨主机通信用于 Docker Swarm 或 K8s 集群三、Bridge 模式默认容器们的小区局域网3.1 工作原理这是 Docker 的默认网络模式。安装 Docker 后它会自动创建一个叫docker0的虚拟网桥可以理解为一个虚拟交换机所有容器都接入这个交换机形成一个小区局域网。┌─────────── 宿主机 ───────────┐ │ │ │ ┌─── docker0 虚拟网桥 ───┐ │ │ │ 172.17.0.1 │ │ │ │ │ │ │ │ ┌─────────┐ ┌─────────┐ │ │ │ │ │ 容器 A │ │ 容器 B │ │ │ │ │ │ .17.0.2 │ │ .17.0.3 │ │ │ │ │ └─────────┘ └─────────┘ │ │ │ └───────────────────────────┘ │ │ ↕ NAT 端口映射 │ │ 宿主机网卡192.168.1.x │ └───────────────────────────────┘▲ 容器 A 和容器 B 都插在 docker0 这块虚拟交换机上天然就能互相访问网关统一走 172.17.0.1。3.2 实际操作两个容器互通启动两个容器看看它们怎么通信# 启动容器 A docker run -d --name web-app --network bridge nginx:1.24 ​ # 启动容器 B docker run -d --name api-server --network bridge nginx:1.24查看它们的 IP 地址# 查看 web-app 的 IP docker inspect web-app --format {{.NetworkSettings.IPAddress}} ​ # 查看 api-server 的 IP docker inspect api-server --format {{.NetworkSettings.IPAddress}}输出172.17.0.2 172.17.0.3从容器 A ping 容器 Bdocker exec web-app ping -c 2 172.17.0.3PING 172.17.0.3 (172.17.0.3): 56 data bytes 64 bytes from 172.17.0.3: seq0 ttl64 time0.072 ms 64 bytes from 172.17.0.3: seq1 ttl64 time0.061 ms✅两个容器通过 IP 互通了3.3 但是默认 bridge 网络有个大坑在默认的bridge网络下容器之间只能用 IP 地址通信不能用容器名# 试试用容器名 ping docker exec web-app ping -c 1 api-serverping: bad address api-server❌容器名解析不了IP 地址会变你不可能在代码里写死 IP。怎么办3.4 解法自定义 Bridge 网络创建一个自定义网络就能用容器名自动 DNS 解析了# 创建自定义网络 docker network create my-network # 查看网络列表多了一个 docker network lsNETWORK ID NAME DRIVER SCOPE a1b2c3d4e5f6 bridge bridge local b2c3d4e5f6a1 host host local c3d4e5f6a1b2 none null local d4e5f6a1b2c3 my-network bridge local把两个容器挂到自定义网络上# 先清理旧容器 docker rm -f web-app api-server # 在自定义网络上启动 docker run -d --name web-app --network my-network nginx:1.24 docker run -d --name api-server --network my-network nginx:1.24再试容器名解析docker exec web-app ping -c 2 api-serverPING api-server (172.18.0.3): 56 data bytes 64 bytes from 172.18.0.3: seq0 ttl64 time0.085 ms 64 bytes from 172.18.0.3: seq1 ttl64 time0.059 ms✅容器名直接当域名用了这就是自定义 Bridge 网络最大的优势。3.5 默认 bridge vs 自定义 bridge 对比特性默认 bridge自定义 bridge容器间用 IP 通信✅✅容器间用容器名通信DNS❌✅容器间网络隔离❌全互通✅不同网络不互通自动分配 IP✅✅容器停止后保留网络连接❌✅推荐用于生产❌✅结论生产环境永远用自定义 Bridge 网络别用默认的。一条docker network create命令的事儿。四、Host 模式容器和宿主机合体4.1 工作原理Host 模式下容器直接使用宿主机的网络栈没有网络隔离没有 NAT没有端口映射——容器就像宿主机上的一个普通进程。┌─── 宿主机 ─────────────────┐ │ │ │ 宿主机网络192.168.1.100 │ │ ↑ │ │ 直接使用不隔离 │ │ │ │ │ ┌─────┴─────┐ │ │ │ 容器 A │ │ │ │ (无独立IP) │ │ │ └───────────┘ │ │ │ └─────────────────────────────┘4.2 实际操作# 用 host 模式启动 Nginx docker run -d --name nginx-host --network host nginx:1.24注意这里没有用-p端口映射因为根本不需要——容器直接监听宿主机的 80 端口。验证一下# 在宿主机上直接 curl localhost:80 curl http://localhost:80!DOCTYPE html html head titleWelcome to nginx!/title ...✅ 直接就能访问无需端口映射。4.3 Host 模式的优缺点优点缺点性能最好无 NAT 开销没有网络隔离安全性差不需要端口映射配置简单端口冲突——宿主机 80 被占就完了适合对延迟敏感的应用Windows/Mac 的 Docker Desktop 不支持不能同时跑两个监听相同端口的容器4.4 适用场景对网络性能要求极高的场景高频交易、实时音视频容器需要监听大量端口的场景明确知道不会端口冲突的单机部署注意如果你在 Windows 或 Mac 上用 Docker Desktop--network host不生效。这个模式只在 Linux 上才有完整支持。五、None 模式容器的小黑屋5.1 工作原理None 模式下容器没有任何网络连接。它就像一台没插网线的电脑——完全孤立。┌─── 宿主机 ─────────────────┐ │ │ │ ┌───────────────┐ │ │ │ 容器 A │ │ │ │ (无网络接口) │ │ │ │ 只有 loopback │ │ │ └───────────────┘ │ │ │ └─────────────────────────────┘5.2 实际操作docker run -it --name isolated --network none alpine /bin/sh进去之后看看网络/ # ifconfig lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1只有一个lo本地回环没有eth0。/ # ping baidu.com ping: bad address baidu.com❌ 完全断网。5.3 这玩意儿有什么用看起来没用但在安全场景下非常有用场景说明处理敏感数据的批处理任务比如加密/解密不需要网络安全沙箱防止恶意代码外联手动配置网络先创建容器再手动接入指定网络考试/面试验证你对 Docker 网络的理解六、自定义网络的高级玩法6.1 指定子网和网关docker network create \ --driver bridge \ --subnet 10.0.0.0/24 \ --gateway 10.0.0.1 \ my-custom-net# 查看网络详情 docker network inspect my-custom-net --format {{.IPAM.Config}}[{10.0.0.0/24 10.0.0.1 map[]}]6.2 给容器指定固定 IPdocker run -d --name db \ --network my-custom-net \ --ip 10.0.0.10 \ mysql:8.0自定义子网 固定 IP这在内网服务编排中非常实用——比如你有一堆微服务每个都需要固定的内网地址。6.3 网络之间是隔离的# 创建两个网络 docker network create net-a docker network create net-b # 容器 A 在 net-a docker run -d --name app-a --network net-a nginx:1.24 # 容器 B 在 net-b docker run -d --name app-b --network net-b nginx:1.24 # 从 A ping B docker exec app-a ping -c 1 app-bping: bad address app-b❌不同网络之间的容器天然隔离互不可见。这个特性天然适合做多租户隔离或开发/测试/生产环境隔离。6.4 一个容器可以接入多个网络# 创建一个网关容器同时接入两个网络 docker run -d --name gateway --network net-a nginx:1.24 docker network connect net-b gateway # 现在 gateway 同时在 net-a 和 net-b 里 docker inspect gateway --format {{json .NetworkSettings.Networks}} | python -m json.tool{ net-a: { IPAddress: 172.19.0.2 }, net-b: { IPAddress: 172.20.0.2 } }这个容器有两个 IP分别属于两个网络——就像一台有两个网卡的服务器可以做网桥/代理/网关。七、Overlay 模式跨主机的容器通信7.1 什么时候需要 Overlay前面讲的 bridge、host、none 都是单机模式——容器必须在同一台宿主机上才能通信。当你的应用跑在多台服务器上时就需要 Overlay 网络了。┌── 服务器 A ──┐ ┌── 服务器 B ──┐ │ │ │ │ │ ┌─────────┐ │ │ ┌─────────┐ │ │ │ 容器 1 │ │ │ │ 容器 3 │ │ │ └────┬────┘ │ │ └────┬────┘ │ │ │ │ 网络 │ │ │ │ ┌────┴────┐ │ │ ┌────┴────┐ │ │ │ 容器 2 │ │◄─Overlay─►│ │ 容器 4 │ │ │ └─────────┘ │ │ └─────────┘ │ │ │ │ │ └───────────────┘ └───────────────┘7.2 快速体验Overlay 网络需要 Docker Swarm 模式后面文章会详细讲这里先简单演示一下# 初始化 Swarm docker swarm init # 创建 Overlay 网络 docker network create --driver overlay my-overlay-net # 查看 docker network lsNETWORK ID NAME DRIVER SCOPE a1b2c3d4e5f6 bridge bridge local b2c3d4e5f6a1 host host local c3d4e5f6a1b2 none null local d4e5f6a1b2c3 my-overlay-net overlay swarm注意 SCOPE 列是swarm而不是local——这意味着这个网络跨越多台主机。Overlay 的完整实战会放在第 16 篇 Docker Swarm 文章中详细讲解这里先混个脸熟。八、端口映射从外面访问容器不管用什么网络模式从宿主机外部访问容器都需要端口映射Bridge 模式下docker run -d --name web \ -p 8080:80 \ -p 8443:443 \ nginx:1.24写法含义-p 8080:80宿主机 8080 → 容器 80-p 8443:443宿主机 8443 → 容器 443-p 8080:80/udp指定 UDP 协议默认 TCP-p 127.0.0.1:8080:80只监听本机回环地址外部不能访问-p 8080随机分配宿主机端口映射到容器 80查看端口映射docker port web80/tcp - 0.0.0.0:8080 443/tcp - 0.0.0.0:8443九、四种网络模式一图总结模式隔离性性能DNS 解析适用场景是否支持端口映射bridge默认✅ 强中NAT 开销✅自定义网络绝大多数场景✅-phost❌ 无✅ 最好N/A高性能/低延迟❌ 不需要none✅ 最强N/A❌安全沙箱/批处理❌ 没网overlay✅ 强中✅多主机集群✅ 通过服务发布十、实战Flask Redis 多容器通信来一个完整的例子用自定义网络让 Flask 容器访问 Redis 容器10.1 创建网络docker network create app-network10.2 启动 Redisdocker run -d --name redis \ --network app-network \ redis:7-alpine10.3 启动 Flask 应用先写一个简单的 Flask 应用app.pyfrom flask import Flask import redis import os app Flask(__name__) # 直接用容器名 redis 作为主机名 r redis.Redis(hostredis, port6379, decode_responsesTrue) app.route(/) def hello(): count r.incr(visits) return f这个页面被访问了 {count} 次\n if __name__ __main__: app.run(host0.0.0.0, port5000)写一个DockerfileFROM python:3.11-slim WORKDIR /app COPY app.py . RUN pip install flask redis CMD [python, app.py]构建并运行# 构建镜像 docker build -t my-flask . # 在同一个网络上启动 docker run -d --name flask-app \ --network app-network \ -p 5000:5000 \ my-flask10.4 验证# 多次访问 curl http://localhost:5000 curl http://localhost:5000 curl http://localhost:5000这个页面被访问了 1 次 这个页面被访问了 2 次 这个页面被访问了 3 次✅Flask 容器通过容器名redis成功连接到了 Redis 容器现在删掉 Flask 容器重建看看 Redis 的数据还在不在docker rm -f flask-app docker run -d --name flask-app \ --network app-network \ -p 5000:5000 \ my-flask curl http://localhost:5000这个页面被访问了 4 次✅ 计数继续因为 Redis 容器还在运行数据没丢Redis 数据存在内存里只要容器没被 rm 就不会丢持久化到磁盘还需要数据卷那是第 06 篇的内容了。十一、常见网络问题排查问题原因解决方案容器间 ping 不通不在同一个网络检查docker network inspect net容器名无法解析用了默认 bridge 而非自定义网络创建自定义网络容器挂到自定义网络上宿主机无法访问容器端口没做端口映射-p启动时加-p参数外部机器无法访问防火墙拦截检查 iptables/firewalld 规则容器能 ping 通但不能用域名DNS 配置问题启动时加--dns 8.8.8.8网络排查三板斧# 1. 看容器在哪个网络上 docker inspect 容器名 --format {{json .NetworkSettings.Networks}} # 2. 看网络上有哪些容器 docker network inspect 网络名 # 3. 进容器 ping 一下 docker exec -it 容器名 ping 目标十二、本文要点回顾Bridge 模式是默认模式容器有独立 IP通过虚拟网桥通信。生产环境要用自定义 Bridge 网络才能享受容器名 DNS 解析。Host 模式性能最好但没隔离仅限 Linux适合极端性能场景。None 模式完全断网适合安全沙箱。Overlay 模式跨主机通信用于集群环境。端口映射-p是让外部访问容器的标准方式。不同自定义网络之间天然隔离一个容器可以接入多个网络。十三、下集预告到现在你已经能管镜像、管容器、管数据、管网络了。但如果一个应用需要 5 个容器Nginx Flask MySQL Redis RabbitMQ每次都要敲 5 条docker run下一篇讲Docker Compose——用一个 YAML 文件编排所有容器一条命令启动整个应用。