4_Docker网络实战指南
Docker 网络是容器通信的基础设施层。六个场景串联核心知识:单容器上网、两容器互通、多服务隔离、跨主机通信,直到特殊需求与生产排障。
1. 场景一:跑第一个容器
没有网络,容器就是一座孤岛——它无法访问外部服务,外部也无法访问它。Docker 的网络模型解决的第一个问题,就是让容器既能上网,又不干扰宿主机和其他容器。
1.1 Docker 怎么给容器配网络
每次启动容器,Docker 自动完成三件事:
| 概念 | 类比 | 对应 Linux 原语 |
|---|---|---|
| Sandbox | 容器独立的网络房间 | Network Namespace |
| Endpoint | 房间里的网口 | veth pair 的容器侧 |
| Network | 连接各房间的交换机 | Linux Bridge |
这三个概念构成 CNM(Container Network Model),是 Docker 网络管理的统一规范:
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#2563EB', 'lineColor': '#60A5FA', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart LR
subgraph S1 [Sandbox: 容器 A]
E1["Endpoint 1"]
E2["Endpoint 2"]
end
subgraph S2 [Sandbox: 容器 B]
E3["Endpoint 3"]
end
subgraph N1 [Network: frontend]
direction LR
end
subgraph N2 [Network: backend]
direction LR
end
E1 -->|"接入"| N1
E3 -->|"接入"| N1
E2 -->|"接入"| N2
classDef sandbox fill:#3B82F6,stroke:#2563EB,color:#fff
classDef endpoint fill:#10B981,stroke:#059669,color:#fff
classDef network fill:#F59E0B,stroke:#D97706,color:#fff
class S1,S2 sandbox
class E1,E2,E3 endpoint
class N1,N2 network容器 A 拥有两个 Endpoint,同时接入 frontend 和 backend 两个 Network。一个 Sandbox 可以连接多个 Network——这是容器多网卡能力的基础。
1.2 Bridge 网络:默认模式
不指定 --network 时,容器自动加入默认的 bridge 网络,通过 docker0 虚拟网桥上网。数据包的完整路径:
容器 eth0(172.17.0.2)→ veth pair → docker0 网桥 → iptables POSTROUTING(MASQUERADE,将源地址改为宿主机 IP)→ 宿主机 eth0 → 外网
返回路径由 conntrack 自动处理:响应到达宿主机后,内核查找连接跟踪表,将目标地址还原为容器 IP,再通过 docker0 和 veth pair 送回容器。整个过程对容器透明。
Docker 启动时创建 docker0 网桥,为每个容器创建一对 veth(虚拟网线),一头放进容器的 Network Namespace,一头接到网桥:
1 | # 查看宿主机上 Docker 创建的 veth |
容器能上网了,但外部无法主动访问它——这是端口映射要解决的问题。
1.3 端口映射:让外部访问容器
-p 参数通过 iptables DNAT 规则将宿主机端口的流量转发到容器端口,本质是把 “ 访问宿主机 8080” 改写为 “ 访问容器 IP:80”。
| 命令/指令 | 作用 | 示例 |
|---|---|---|
EXPOSE 80(Dockerfile) | 纯文档声明,不打开端口 | 告诉使用者 “ 我监听 80” |
-p 8080:80 | 宿主机 8080 → 容器 80 | docker run -p 8080:80 nginx |
-p 127.0.0.1:8080:80 | 仅本机可访问 | 生产环境推荐 |
-p 8080:80/udp | UDP 协议映射 | DNS、日志收集等场景 |
-p 8080-8090:8080-8090 | 范围映射 | 需要多端口的服务 |
-P | 自动映射所有 EXPOSE 端口到随机高端口 | 快速测试 |
⚠️
-p直接操作 iptables,会绕过 UFW / firewalld 等系统防火墙。即使 UFW 只开放了 22 和 443,-p 3306:3306仍会让 MySQL 暴露在公网。生产环境务必绑定具体 IP(-p 127.0.0.1:8080:80),并在云平台安全组做二次防护。
1.4 本节验收
1 | # 启动一个 nginx,映射到本机 8080 |
2. 场景二:两容器互通
容器能上网了,但两个容器怎么通信?直接用 IP?容器重启 IP 就变,硬编码会导致服务间连接断裂——这是生产环境的事故高发区。
2.1 默认 Bridge 的痛点
默认 bridge 网络(docker0)没有内置 DNS。两个容器无法通过容器名互相访问,只能用 IP 或已废弃的 --link。
2.2 自定义 Bridge:内置 DNS
自定义 bridge 网络内置了 DNS 服务(监听 127.0.0.11:53),接入同一网络的容器直接用容器名通信:
1 | # 创建自定义网络 |
| 维度 | 默认 bridge(docker0) | 自定义 bridge |
|---|---|---|
| DNS 解析 | 不支持容器名,只能用 IP 或废弃的 --link | 内置 DNS(127.0.0.11),按容器名解析 |
| 隔离性 | 所有未指定网络的容器共享同一网桥 | 按网络分组,不同网络默认互不可达 |
| 子网配置 | 只能通过 daemon.json 修改,需重启 Docker | 支持自定义子网、IP 范围、网关 |
只要有多容器通信需求,始终使用自定义 bridge,不要依赖默认 bridge。
2.3 本节验收
1 | docker network create net-a |
3. 场景三:多服务隔离
两容器互通解决了,但真实项目有十几个微服务。所有容器同在一个网络,API 网关能直连数据库——一旦网关被攻破,数据库直接暴露。需要按职责分层隔离。
3.1 Compose 网络分层
Docker Compose 自动为每个项目创建独立的 bridge 网络。在 docker-compose.yml 中声明多个网络,按角色分组:
1 | # docker-compose.yml |
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'lineColor': '#60A5FA'}}}%%
flowchart TD
EXT["外部请求 :80"] --> GW["api-gateway"]
subgraph FN [frontend 网络]
GW
US["user-service"]
OS["order-service"]
end
subgraph BN [backend 网络 - internal]
US2["user-service"]
OS2["order-service"]
PG["postgres"]
RD["redis"]
end
US -.->|"同一容器, 双网卡"| US2
OS -.->|"同一容器, 双网卡"| OS2
classDef external fill:#EF4444,stroke:#DC2626,color:#fff
classDef frontend fill:#3B82F6,stroke:#2563EB,color:#fff
classDef backend fill:#10B981,stroke:#059669,color:#fff
class EXT external
class GW,US,OS frontend
class US2,OS2,PG,RD backend设计要点:
- api-gateway 只接入 frontend,不在 backend 网络中,无法路由到 postgres 和 redis
- backend 设置
internal: true,容器无法主动发起出网连接,避免数据库层外泄数据 - user-service 和 order-service 同时接入两个网络,各有 “ 双网卡 “,充当 frontend 与 backend 的桥梁
- 端口映射绑定
127.0.0.1,外部流量须经过反向代理
3.2 本节验收
1 | docker compose up -d |
4. 场景四:跨主机通信
单机问题解决了。容器分布到多台宿主机后,Bridge 网络无能为力——它只是本机的虚拟交换机,无法跨越物理边界。
4.1 Overlay 网络与 VXLAN
Overlay 网络通过 VXLAN 技术在宿主机之间建立隧道:容器发出的完整二层以太网帧,被封装进 UDP 数据包(目标端口 4789),通过宿主机物理网络传输,到达目标宿主机后解封装还原。对容器而言,仿佛和对方在同一个局域网里。
封装使用 UDP 而非 TCP,是因为内层流量可能已经是 TCP。外层再用 TCP 会形成 TCP-over-TCP:两层重传定时器相互干扰,丢包时产生指数级重传风暴。UDP 只负责 “ 送达 “,可靠性交给内层协议处理。
每个 VXLAN 帧增加约 50 字节的头部开销。大量小包场景下需关注 MTU 配置,避免封装后超出 MTU 导致分片。
4.2 Swarm Overlay 实战
1 | # 初始化 Swarm(在 manager 节点) |
使用 Kubernetes 时,跨主机网络通常由 CNI 插件(Flannel、Calico 等)替代 Docker 原生 overlay,原理相同但实现更灵活。
4.3 本节验收
1 | # 验证 overlay 网络创建成功 |
5. 场景五:特殊需求
前四个场景覆盖了绝大多数情况。剩下 10% 的特殊需求——极致性能、完全断网、固定物理 IP——需要对应的网络模式。先用决策树找准方向:
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'lineColor': '#60A5FA'}}}%%
flowchart TD
Q1{"容器需要访问网络吗?"}
Q2{"容器在多台机器上?"}
Q3{"对网络延迟极端敏感?"}
Q4{"需要容器拥有物理网络独立 IP?"}
Q1 -->|"不需要"| NONE["none: 完全隔离"]
Q1 -->|"需要"| Q2
Q2 -->|"是,跨主机"| OVERLAY["overlay: 自动建隧道"]
Q2 -->|"否,单机"| Q3
Q3 -->|"是"| HOST["host: 零 NAT 开销"]
Q3 -->|"否"| Q4
Q4 -->|"是"| MACVLAN["macvlan: 直连物理网络"]
Q4 -->|"否"| BRIDGE["bridge(自定义): 最通用"]
classDef decision fill:#3B82F6,stroke:#2563EB,color:#fff
classDef result fill:#10B981,stroke:#059669,color:#fff
class Q1,Q2,Q3,Q4 decision
class NONE,OVERLAY,HOST,MACVLAN,BRIDGE result5.1 Host 网络:零 NAT 开销
容器直接共享宿主机的 Network Namespace,没有 veth pair,没有 NAT,也没有网络隔离。
场景:Prometheus Node Exporter 需要采集宿主机真实的 CPU、内存、网络指标。Bridge 模式下,容器看到的是 veth 虚拟网卡,采集到的是容器自身数据而非宿主机数据。
1 | docker run -d --network host --name node-exporter \ |
代价:容器端口直接占用宿主机端口,两个容器无法监听同一端口。适用于监控 Agent、高吞吐代理等无需隔离的场景。
5.2 None 网络:完全断网
容器只有 loopback 接口,彻底断网。
场景:金融风控模型处理征信数据,监管要求数据不能有任何网络外传的可能。即使容器被攻破,攻击者也无法将数据发出。
1 | # 数据通过卷挂载进去,结果通过卷取出来,全程无网络 |
适用场景:纯 CPU 计算、离线数据处理、安全沙箱。
5.3 Macvlan 与 IPvlan:固定物理网络 IP
两者都让容器直接出现在物理网络上,拥有独立 IP,完全绕过 NAT。
场景:某制造企业的 ERP 系统,IP 地址(192.168.1.100)硬编码在几十台设备配置中,容器化后必须保持原 IP 不变。Bridge 模式无法实现——容器在 172.17.x.x 子网里,必须走 NAT。
1 | # 创建 Macvlan 网络 |
| 维度 | Macvlan | IPvlan |
|---|---|---|
| MAC 地址 | 每个容器独立 MAC | 共享父接口 MAC |
| 交换机兼容 | 需要混杂模式,部分交换机限制 MAC 数量 | 无需混杂模式 |
| 云平台 | 标准云虚拟机不可用(AWS EC2/GCP 默认禁用混杂模式) | 兼容性更好 |
| 宿主机通信 | 宿主机无法直接访问容器(内核限制) | L3 模式下添加静态路由后可达 |
需要物理网络独立 IP 时,优先选 IPvlan(兼容性更好);仅在需要独立 DHCP 地址且交换机支持时使用 Macvlan。
5.4 本节验收
1 | # 验证 Host 网络:容器和宿主机共享端口空间 |
6. 排障指南
网络问题排查的核心思路:从最近的地方开始,一层一层往外推。
6.1 分层排查
容器自身网络正常吗?
docker exec <c> ip addr确认有 eth0 和正确 IP。没有则重启容器。能 ping 通对方容器名吗?
docker exec <c1> ping -c 1 <c2>。DNS 解析失败(bad address)→ 跳到第 3 步;能 ping 通 → 跳到第 4 步。两个容器在同一自定义网络吗?
docker network inspect <net>确认两个容器都在列表中。用的是默认 bridge → 改用自定义 bridge。应用层能连通吗?
docker exec <c1> curl <c2>:<port>。不通 → 检查服务是否绑定0.0.0.0(而非127.0.0.1)。外部能访问吗?
iptables -t nat -L DOCKER -n确认 DNAT 规则存在。规则存在仍不通 → 检查云平台安全组和宿主机防火墙(-p会绕过 UFW,参见第 1.3 节安全警告)。仍然不通?
tcpdump -i docker0 -n port <port>抓包,定位包在哪一层丢失。
6.2 工具速查
| 排查目标 | 工具 | 命令 |
|---|---|---|
| 容器网卡和 IP | ip | docker exec <c> ip addr |
| 容器间通信 | ping / curl | docker exec <c1> ping <c2> |
| DNS 解析 | nslookup | docker exec <c> nslookup db |
| 网桥状态 | bridge | bridge link show |
| iptables 规则 | iptables | iptables -t nat -L -n -v |
| 抓包分析 | tcpdump | tcpdump -i docker0 -n port 80 |
| 查容器所在网络 | docker | docker network inspect <net> |
6.3 容器访问宿主机
从容器内访问宿主机服务(如本地数据库),优先使用 host.docker.internal:
1 | docker run --rm -it mysql mysql -h host.docker.internal -u root -p |
| 环境 | 支持方式 |
|---|---|
| Docker Desktop(Mac/Windows) | 默认支持,开箱即用 |
| Linux Docker Engine 20.10+ | 需加 --add-host=host.docker.internal:host-gateway |
| Docker Compose(Linux) | 在 extra_hosts 中配置 |
1 | services: |
不要用 172.17.0.1——该地址仅在默认 bridge 网络中有效,自定义网络的网关地址不同。