IT策士 10余年一线大厂经验专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章助你少走弯路。在前面几篇文章中我们已经能把 Flask Redis 计数器应用跑起来也学会了用 Volume 持久化数据。但有一个问题我们一直没有深究Flask 容器是怎么找到 Redis 容器的你在app.py里写的hostredis是怎么解析成 Redis 容器的 IP 地址的如果你同时起了三个 Flask 容器它们能互相通信吗默认情况下它们是否彼此隔离端口映射-p 8080:80在网络上到底发生了什么这些就是本篇要回答的问题。在 Docker 的世界里网络不仅仅是一条“连接线”更是容器之间、容器与外界交互的“神经系统”。理解 Docker 网络模型是理解 Kubernetes 中 Pod 网络、Service 发现和 Ingress 路由的关键基石。读完这一篇你会发现 K8s 的网络设计有着和 Docker 一脉相承的逻辑。一、Docker 网络全景CNM 模型与驱动类型在开始动手之前我们先对 Docker 网络的整体架构有一个鸟瞰式了解这能帮助你在后续实验中每一步都知道“自己在操作什么”。Docker 的网络架构遵循CNMContainer Network Model容器网络模型规范。CNM 定义了三个核心概念Sandbox沙盒容器的网络栈包含网卡、路由表、DNS 配置等。每个容器至少拥有一个沙盒同一 Pod 中的多个容器可以共享同一个沙盒Kubernetes 的设计正是基于此。Endpoint端点容器通过端点连接到网络一个沙盒可以有多个端点连接到不同的网络。端点是 veth pair虚拟以太网对中位于容器侧的一端。Network网络一组可以互相通信的端点集合相当于一个广播域。同一个网络内的容器默认可以直接通信不同网络间则默认隔离。从驱动层面来看Docker 提供了六种网络驱动日常开发中你会频繁接触到的是bridge和host两种驱动。Swarm 场景下会用到overlay而macvlan和ipvlan则更多出现在网络拓扑复杂的企业环境中。对于本系列来说bridge 和 host 已经足够覆盖 90% 的学习和实战场景。二、默认桥接网络Docker 自带的“局域网”2.1 bridge 网络原理当你启动 Docker 服务后Docker 会在宿主机上自动创建一个虚拟网桥docker0。这个docker0像一个虚拟交换机每个连接到这个桥的容器都会被分配一个172.17.0.0/16网段的 IP 地址。宿主机(192.168.1.100)│ ├── docker0(172.17.0.1)← 虚拟网桥也是容器访问宿主机的默认网关│ │ │ ├── veth-aaa ←→ eth0容器A(172.17.0.2)│ ├── veth-bbb ←→ eth0容器B(172.17.0.3)│ └── veth-ccc ←→ eth0容器C(172.17.0.4)Docker 通过veth pair虚拟以太网对技术将容器连接到网桥——veth pair 是一对虚拟网卡一端插在容器的网络命名空间中容器内看到的eth0另一端插在宿主机的docker0网桥上。这样一来容器 A 发往容器 B 的数据包就走了一条清晰的路径A 的eth0→veth-aaa→docker0网桥 →veth-bbb→ B 的eth0。# 查看 docker0 网桥ipaddr show docker0输出示例3: docker0:BROADCAST,MULTICAST,UP,LOWER_UPmtu1500link/ether 02:42:a1:b2:c3:d4 brd ff:ff:ff:ff:ff:ff inet172.17.0.1/16 brd172.17.255.255 scope global docker0这里172.17.0.1就是 docker0 网桥的 IP也是所有连接到这个桥的容器的默认网关。容器想访问外网时数据包会先到这里然后通过宿主机的 NAT网络地址转换转发到物理网络。2.2 默认 bridge 网络 vs 自定义 bridge 网络需要注意Docker 的“默认 bridge 网络”名称就叫bridge和我们自己创建的“自定义 bridge 网络”虽然底层都是 bridge 驱动但行为有本质区别核心结论默认 bridge 网络仅适合零散测试任何多容器协作场景都应使用自定义 bridge 网络。下面动手验证这个结论。2.3 动手验证默认 bridge 的局限性# 启动两个测试容器连接到默认 bridge 网络dockerrun-d--nametest1 nginx:alpinedockerrun-d--nametest2 alpinesleep3600# 查看它们的 IPdockerinspect test1--format{{.NetworkSettings.IPAddress}}# 输出172.17.0.2dockerinspect test2--format{{.NetworkSettings.IPAddress}}# 输出172.17.0.3# 通过 IP 通信✅ 可以dockerexectest2ping-c2172.17.0.2# 输出64 bytes from 172.17.0.2: seq0 ttl64 time0.1ms# 通过容器名通信❌ 无法解析dockerexectest2ping-c2test1# 输出ping: bad address test1 ← 默认 bridge 不支持 DNS 解析结论默认 bridge 网络中的容器只能通过 IP 通信无法使用容器名。这在多容器场景下是致命的——你不可能每次重启容器后手动更新所有 IP 地址。解决这个问题的方法就是创建自定义 bridge 网络。# 清理测试容器dockerrm-ftest1 test2三、自定义 Bridge 网络容器互联的正确姿势3.1 创建自定义网络dockernetwork create my-net此时 Docker 会在宿主机上创建一个新的虚拟网桥名称类似br-network_id与docker0完全独立。连接到这个自定义网络的容器拥有自己的子网默认172.18.0.0/16等并且内置了DNS 解析服务——容器可以通过容器名直接访问同网络下的其他容器。# 查看已创建的网络dockernetworkls输出NETWORK ID NAME DRIVER SCOPE a1b2c3d4e5f6 bridge bridgelocalf6a7b8c9d0e1hosthostlocalb2c3d4e5f6a7 my-net bridgelocalc3d4e5f6a7b8 none nulllocal3.2 将容器连接到自定义网络# 在创建容器时指定网络dockerrun-d--nameweb--networkmy-net nginx:alpinedockerrun-d--namedb--networkmy-net alpinesleep3600# 通过容器名访问dockerexecdbping-c2web# 输出64 bytes from web.my-net (172.18.0.2): ... ← 通过 DNS 解析出 IP自定义 bridge 网络内置了一个嵌入式 DNS 服务器地址固定为127.0.0.11。当容器通过容器名发起网络请求时DNS 查询会被转发到这个嵌入式 DNS 服务器它根据容器名返回对应的 IP 地址。这就是hostredis能够工作的底层机制——在第 10 篇的实战中你会反复用到它。3.3 容器连接到多个网络一个容器可以同时连接到多个网络这在网络隔离场景中非常有用# 创建两个隔离网络frontend前端和 backend后端dockernetwork create frontenddockernetwork create backend# Redis 只属于 backend 网络不暴露给前端dockerrun-d--nameredis--networkbackend redis:alpine# Flask 同时连接两个网络前端入口 后端访问 Redisdockerrun-d--nameflask-app--networkfrontend flask-redis-counter:2.0dockernetwork connect backend flask-app# 现在# redis 和 flask-app 在 backend 中互通 ✅# web 用户通过 frontend 访问 flask-app ✅# web 用户无法直接访问 redis ✅redis 不在 frontend 网络中这种“前端-后端”分层隔离模式正是 Kubernetes 中 NetworkPolicy 的设计前身。3.4 动态连接与断开网络# 将运行中的容器连接到网络dockernetwork connect my-net existing-container# 将容器从网络中断开dockernetwork disconnect my-net existing-container小技巧当你发现某个容器突然连不上数据库时先用docker inspect container查看它的网络列表确认它是否连接了正确的网络。日常排查中容器“连不上”的根因有半数以上是网络配置问题。3.5 自定义子网和 IP有时候你需要精确控制容器的 IP 地址比如与外部遗留系统对接# 创建自定义子网的网络dockernetwork create\--subnet192.168.100.0/24\--gateway192.168.100.1\custom-subnet# 启动容器并指定 IPdockerrun-d--namestatic-web\--networkcustom-subnet\--ip192.168.100.10\nginx:alpine# 验证 IPdockerinspect static-web--format{{.NetworkSettings.Networks.custom-subnet.IPAddress}}# 输出192.168.100.10在生产环境中请谨慎使用手动指定 IP——它增加了配置复杂度和管理负担。Kubernetes 彻底放弃了手动 IP 管理全部交由网络插件CNI自动分配和回收。四、Host 网络让容器“裸奔”在宿主机上4.1 什么是 host 网络host网络模式下容器不再拥有独立的网络栈而是与宿主机共享同一个网络命名空间。这意味着容器直接使用宿主机的 IP 地址容器监听某个端口就等同于宿主机监听该端口无需-p映射没有网络隔离容器可以直接访问宿主机的所有网络接口dockerrun-d--namehost-nginx--networkhostnginx:alpine# 此时直接访问宿主机的 80 端口就是 Nginx 的服务curlhttp://localhost:80# 输出Nginx 欢迎页4.2 Host 网络的优缺点host 网络在特定场景下非常有用比如运行高性能网络代理Envoy/Istio Sidecar、网络监控组件Prometheus Node Exporter或者需要从宿主机层面抓取网络数据的工具。但绝大多数应用容器不需要、也不应该使用 host 网络——自定义 bridge 网络在性能开销和隔离安全性之间取得了更好的平衡。五、None 网络完全断网dockerrun-d--nameisolated--networknone alpinesleep3600dockerexecisolatedipaddr# 输出1: lo: LOOPBACK,UP,LOWER_UP ... ← 只有回环接口没有 eth0none网络模式下容器内除了lo回环接口之外没有任何网络接口。适用于安全计算、离线数据处理或自定义网络插件接管网络的场景。六、端口映射的底层原理-p 8080:80我们用了很多次但它背后发生了什么端口映射的底层依赖 Linux iptables 的 NAT网络地址转换规则。Docker 在宿主机 iptables 的DOCKER链中自动添加 DNAT目标地址转换规则。# 启动一个带端口映射的容器dockerrun-d--namenginx-test-p8080:80 nginx:alpine# 查看 Docker 添加的 iptables 规则sudoiptables-tnat-LDOCKER-n|grep8080输出示例DNAT tcp --0.0.0.0/00.0.0.0/0 tcp dpt:8080 to:172.17.0.2:80这条规则的意思是任何访问宿主机 8080 端口的 TCP 流量都将被重写目标地址为172.17.0.2:80即容器内 Nginx 的 IP 和端口。端口映射的本质就是一条 DNAT 规则。这也解释了为什么 host 网络不需要端口映射——它直接使用宿主机 IP数据包走的就是宿主机的标准网络栈不经过 NAT 转换。七、排除网络故障端口冲突排查端口映射最常见的错误就是端口冲突。两个容器或宿主机服务不能同时监听同一个宿主机端口dockerrun-d--nameweb1-p8080:80 nginx:alpine# ✅ 成功dockerrun-d--nameweb2-p8080:80 nginx:alpine# ❌ 失败# Error: driver failed programming external connectivity on endpoint:# Bind for 0.0.0.0:8080 failed: port is already allocated排查步骤# 1. 查看宿主机哪些端口已被占用sudonetstat-tlnp|grep8080# 或sudolsof-i:8080# 2. 查看 Docker 容器占用了哪些端口dockerps--formattable {{.Names}}\t{{.Ports}}# 3. 更换端口或清理冲突容器dockerrm-fweb1dockerrun-d--nameweb2-p8080:80 nginx:alpine# 现在可以了八、命令速查表九、本篇总结这一篇我们系统学习了 Docker 的网络模型CNM 三要素Sandbox容器网络栈、Endpoint连接点、Network广播域六种网络驱动bridge默认单机、host共享宿主机、overlay跨主机、ipvlan/macvlan物理网络对接、none完全断网bridge 网络的核心差异默认 bridge 无 DNS 解析自定义 bridge 内置 DNS 服务器127.0.0.11是容器名互访的基础端口映射的底层本质-p是通过 iptables DNAT 将宿主机端口流量重定向到容器 IP网络隔离模式容器可同时连接多个网络实现前端-后端分层隔离理解 Docker 的网络模型等于为 Kubernetes 的学习铺好了路——K8s 的 Pod 共享同一个网络命名空间类似 host 模式Service 通过标签选择器和虚拟 IP 实现服务发现类似自定义 bridge 的 DNS 解析而 CNI 插件Calico、Flannel则是 Docker CNM 模型在集群层面的扩展。下一篇文章——第 9 篇Docker 网络进阶容器间通信与 DNS 解析我们将深入 DNS 解析的工作原理、容器间通信的全链路数据流并带你动手搭建一个三层微服务网络。想了解更多还可以去各个平台搜索「IT策士」一起升级 IT 思维