Docker 网络深入
Docker 网络深入
什么是 Docker 网络
Docker 网络是容器化架构中至关重要的基础设施层。在容器运行时,每个容器都拥有自己独立的网络命名空间(network namespace),这意味着每个容器都有独立的 IP 地址、路由表、防火墙规则和端口空间。Docker 网络子系统负责在这些隔离的网络命名空间之间建立通信通道,同时也管理容器与宿主机以及外部网络之间的连接。
理解 Docker 网络不仅仅是为了让容器之间能互相通信。在生产环境中,网络设计直接影响到:
- 安全性:不同业务域的容器是否能够互相访问?敏感服务是否被隔离?
- 性能:容器间通信是否经过不必要的 NAT 转换?跨主机通信的延迟和吞吐是否满足需求?
- 可观测性:网络流量是否可以被监控和追踪?
- 故障排查效率:当网络问题发生时,能否快速定位根因?
Docker 提供了多种内置的网络驱动,每种驱动都有其特定的适用场景和底层实现原理。选择正确的网络模式,是构建可靠容器化系统的第一步。
Docker 网络架构底层原理
网络命名空间(Network Namespace)
Linux 网络命名空间(netns)是 Docker 网络隔离的基石。每个网络命名空间提供了完全独立的网络栈:
# 查看宿主机的网络命名空间
ip netns list
# 查看容器对应的网络命名空间
# Docker 将容器的网络命名空间文件放在 /var/run/docker/netns/ 下
ls /var/run/docker/netns/
# 进入某个容器的网络命名空间执行命令
docker exec -it <container_id> ip addr show
docker exec -it <container_id> ip route show
docker exec -it <container_id> iptables -L -n每个容器启动时,Docker 会为其创建一个独立的网络命名空间。容器内的进程只能看到这个命名空间中的网络接口、路由规则和 iptables 规则,完全看不到宿主机或其他容器的网络配置。
veth pair 与网桥
当容器连接到 bridge 网络时,Docker 的底层实现依赖两个核心技术:
- veth pair(虚拟以太网对):这是一种特殊的网络设备,它总是成对出现,数据从一个端进入后从另一个端出来。veth pair 的一端放在容器的网络命名空间中(通常命名为 eth0),另一端放在宿主机的网络命名空间中。
- 网桥(bridge):Docker 创建的虚拟网桥(docker0)工作在数据链路层,类似于物理交换机。所有连接到该 bridge 网络的容器的 veth pair 宿主机端都插在这个网桥上。
容器 A (eth0) ←→ veth pair ←→ docker0 (bridge) ←→ veth pair ←→ 容器 B (eth0)# 查看 Docker 默认网桥配置
ip link show docker0
bridge fdb show br docker0
# 查看网桥上的所有端口
bridge link show
# 查看某个容器的 veth pair
docker exec <container_id> ethtool -S eth0 | grep peer_ifindexiptables 与 NAT
Docker 大量使用 iptables 来实现网络地址转换(NAT)和端口映射:
# 查看 Docker 自动创建的 NAT 规则
iptables -t nat -L DOCKER -n
iptables -t nat -L POSTROUTING -n
iptables -t nat -L PREROUTING -n
# 查看 Docker 的 filter 规则(包含 FORWARD 链)
iptables -L DOCKER-USER -n
iptables -L FORWARD -n当你在 docker run 时使用 -p 8080:80 参数,Docker 实际上会在 iptables 的 PREROUTING 链中添加 DNAT 规则,将宿主机 8080 端口的流量转发到容器的 80 端口。在 POSTROUTING 链中添加 MASQUERADE 规则,实现从容器访问外部网络时的源地址转换。
五种网络驱动详解
1. Bridge 网络模式
Bridge 是 Docker 的默认网络模式,也是单机容器部署中最常用的网络方案。
默认 bridge 网络
每个 Docker 安装都会自动创建一个名为 bridge(内部名称 docker0)的默认网络:
# 查看默认网络
docker network ls
# NETWORK ID NAME DRIVER SCOPE
# a1b2c3d4e5f6 bridge bridge local
# 查看默认网络详情
docker network inspect bridge默认 bridge 网络有一个重要的限制:不支持容器名 DNS 解析。在默认 bridge 网络中,容器之间只能通过 IP 地址通信,如果容器被重建,IP 地址会发生变化,这导致容器间的通信极不稳定。
# 默认 bridge 网络中的容器无法通过名称通信
docker run -d --name container-a nginx:alpine
docker run -d --name container-b nginx:alpine
# 以下命令会失败,因为默认 bridge 不支持 DNS 解析
docker exec container-a ping container-b
# ping: container-b: Name or service not known
# 只能通过 IP 地址通信
docker exec container-a ping 172.17.0.3自定义 bridge 网络(推荐)
自定义 bridge 网络解决了默认网络的所有缺陷,是生产环境的推荐选择:
# 创建自定义 bridge 网络,指定子网和网关
docker network create --driver bridge \
--subnet 172.20.0.0/16 \
--gateway 172.20.0.1 \
--opt com.docker.network.bridge.name=br-app \
--opt com.docker.network.bridge.enable_icc=true \
--opt com.docker.network.bridge.enable_ip_masquerade=true \
--internal \
my-app-network
# --internal 选项创建内部网络,容器无法访问外部网络(适用于安全隔离场景)
# 将容器加入自定义网络
docker run -d --name api-server --network my-app-network \
-e DB_HOST=db-server \
nginx:alpine
docker run -d --name db-server --network my-app-network \
mysql:8.0
# 同一网络内通过容器名互相访问
docker exec api-server ping db-server
# PING db-server (172.20.0.3): 56 data bytes
# 64 bytes from 172.20.0.3: icmp_seq=0 ttl=64 time=0.087 ms自定义 bridge 网络内置了嵌入式 DNS 服务器(127.0.0.11),自动为同一网络中的容器提供名称解析。此外,容器可以同时连接多个网络:
# 将一个容器连接到多个网络
docker network connect frontend-net api-server
docker network connect backend-net api-server
# 查看容器的所有网络连接
docker inspect --format '{{json .NetworkSettings.Networks}}' api-server | python3 -m json.tool
# 断开某个网络连接
docker network disconnect frontend-net api-server网络别名
# 为容器在网络中设置别名
docker run -d --name mysql-prod --network my-app-network \
--network-alias mysql \
mysql:8.0
# 其他容器可以通过别名访问
docker exec api-server ping mysql2. Host 网络模式
Host 网络模式下,容器直接共享宿主机的网络命名空间,不创建任何网络隔离:
# 使用 host 网络模式
docker run -d --name nginx-host --network host nginx:alpine
# 容器直接监听宿主机的 80 端口,无需 -p 映射
curl http://localhostHost 模式的典型应用场景:
# docker-compose.yml 中使用 host 网络的典型场景
services:
# 网络监控工具需要直接访问宿主机网络接口
prometheus:
image: prom/prometheus:v2.48.0
network_mode: host
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
# Node Exporter 需要采集宿主机指标
node-exporter:
image: prom/node-exporter:v1.7.0
network_mode: host
pid: host
volumes:
- /:/host:ro,rslave重要警告:Host 网络模式会带来严重的安全风险。容器内的进程可以访问宿主机上的所有网络服务和端口,一旦容器被攻破,攻击者可以直接访问宿主机的网络栈。生产环境应尽量避免使用 host 网络。
3. Overlay 网络模式
Overlay 网络是 Docker Swarm 集群中实现跨主机容器通信的核心技术。它基于 VXLAN(Virtual Extensible LAN)隧道协议,在底层物理网络之上构建一个虚拟的二层网络。
# 在 Swarm 模式下创建 overlay 网络
docker swarm init
docker network create --driver overlay \
--subnet 10.0.9.0/24 \
--attachable \
--opt encrypted \
cross-host-net
# --attachable 允许独立容器(非 Swarm Service)加入 overlay 网络
# --encrypted 启用 VXLAN 层加密(AES-GCM),增加安全性但有性能开销VXLAN 的工作原理:
Node A (Container) Physical Network Node B (Container)
┌──────────────┐ ┌──────────────────┐ ┌──────────────┐
│ Container │ │ │ │ Container │
│ 10.0.9.2 │ veth │ VXLAN Tunnel │ veth │ 10.0.9.3 │
│ │ ←──→ │ (UDP Port 4789) │ ←──→ │ │
│ eth0 │ │ │ │ eth0 │
└──────────────┘ └──────────────────┘ └──────────────┘# 验证 overlay 网络的 VXLAN 接口
docker run --rm --network cross-host-net alpine ip link show
# 应该能看到 vxlan 类型的接口
# 查看跨主机通信的 VXLAN 封装
tcpdump -i eth0 udp port 4789 -nnMTU 注意事项:VXLAN 封装会增加 50 字节的额外开销(14 字节以太网头 + 8 字节 UDP 头 + 8 字节 VXLAN 头 + 20 字节 IP 头)。如果底层网络的 MTU 是 1500,overlay 网络的 MTU 应设为 1450:
# 创建指定 MTU 的 overlay 网络
docker network create --driver overlay \
--opt com.docker.network.driver.mtu=1450 \
overlay-mtu-aware4. Macvlan 网络模式
Macvlan 网络模式让容器直接获得物理网络中的 MAC 地址和 IP 地址,容器就像物理网络中的一台独立主机:
# 创建 macvlan 网络
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
--ip-range=192.168.1.200/28 \
-o parent=eth0 \
macvlan-net
# 启动容器,获得物理网络 IP
docker run -d --network macvlan-net \
--ip 192.168.1.201 \
--name web-app \
nginx:alpine
# 验证容器获得物理网络 IP
docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' web-app
# 192.168.1.201重要限制:宿主机无法直接访问同一 macvlan 网络中的容器。这是因为 macvlan 的工作原理——当流量从物理网卡到达宿主机时,macvlan 驱动会丢弃目的地址为容器 MAC 的帧。解决方法是创建一个 macvlan 子接口给宿主机使用:
# 在宿主机上创建 macvlan 子接口,用于与容器通信
ip link add macvlan-shim link eth0 type macvlan mode bridge
ip addr add 192.168.1.199/32 dev macvlan-shim
ip link set macvlan-shim up
ip route add 192.168.1.200/28 dev macvlan-shimMacvlan 的典型使用场景包括:
- 遗留系统集成:需要与物理网络中的设备直接通信
- 网络监控工具:需要在物理网络层面抓包和分析流量
- SDN 集成:需要容器直接参与物理网络策略
5. None 网络模式
None 网络模式为容器提供完全隔离的网络环境,没有任何网络接口(除了 loopback):
# 创建无网络连接的容器
docker run -d --name isolated-worker --network none \
alpine sleep infinity
docker exec isolated-worker ip addr
# 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536
# inet 127.0.0.1/8 scope host loNone 网络适用于安全性要求极高的场景,如处理敏感数据的批处理任务,或者不需要网络访问的后台工作进程。
Docker 网络实战场景
微服务多网络拓扑设计
在实际的微服务项目中,通常需要按照业务域划分多个网络:
# docker-compose.yml — 微服务多网络拓扑
version: "3.8"
services:
frontend:
image: myapp/frontend:v2.0
networks:
- frontend-net
# 通过 shared-net 访问 API Gateway
- shared-net
ports:
- "80:80"
api-gateway:
image: myapp/gateway:v2.0
networks:
- shared-net
- backend-net
ports:
- "8080:8080"
depends_on:
- user-service
- order-service
user-service:
image: myapp/user-service:v2.0
networks:
- backend-net
environment:
- DB_HOST=user-db
order-service:
image: myapp/order-service:v2.0
networks:
- backend-net
environment:
- DB_HOST=order-db
user-db:
image: postgres:15
networks:
- db-net
environment:
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
- user-db-data:/var/lib/postgresql/data
order-db:
image: postgres:15
networks:
- db-net
environment:
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
- order-db-data:/var/lib/postgresql/data
networks:
frontend-net:
driver: bridge
internal: true
shared-net:
driver: bridge
backend-net:
driver: bridge
internal: true
db-net:
driver: bridge
internal: true
volumes:
user-db-data:
order-db-data:这个设计实现了三层网络隔离:
| 网络层 | 网络 | 可访问范围 | 说明 |
|---|---|---|---|
| 前端层 | frontend-net | 仅前端服务 | 完全内部网络 |
| 共享层 | shared-net | 前端 + Gateway | 服务间通信通道 |
| 后端层 | backend-net | Gateway + 后端服务 | 内部网络 |
| 数据层 | db-net | 后端服务 + 数据库 | 内部网络,最严格隔离 |
CI/CD 构建网络隔离
# docker-compose.ci.yml — CI/CD 构建环境
version: "3.8"
services:
builder:
image: node:20-alpine
networks:
- build-net
volumes:
- .:/app
command: npm ci && npm run build
tester:
image: node:20-alpine
networks:
- build-net
volumes:
- .:/app
depends_on:
- test-db
command: npm test
test-db:
image: postgres:15
networks:
- build-net
environment:
POSTGRES_DB: test_db
POSTGRES_PASSWORD: test_pass
networks:
build-net:
driver: bridge
internal: true # 构建网络完全隔离,无法访问外部网络网络性能优化
调整 MTU
# 创建指定 MTU 的自定义网络
docker network create \
--driver bridge \
--opt com.docker.network.driver.mtu=9000 \
jumbo-frame-net
# 在 docker-compose.yml 中配置 MTU
# networks:
# default:
# driver_opts:
# com.docker.network.driver.mtu: 9000禁用容器间的连接(安全加固)
# 创建禁用容器间通信的网络
docker network create --driver bridge \
--opt com.docker.network.bridge.enable_icc=false \
isolated-net
# 容器间无法互相通信,但可以通过 -p 端口映射从外部访问DNS 配置
# 自定义 DNS 服务器
docker run -d --name app \
--dns 8.8.8.8 \
--dns 8.8.4.4 \
--dns-search example.com \
nginx:alpine
# 在 docker-compose.yml 中配置
# dns:
# - 8.8.8.8
# - 8.8.4.4
# dns_search:
# - example.com网络故障排查
系统化排查流程
# 第 1 步:确认容器所在网络
docker inspect --format '{{json .NetworkSettings.Networks}}' <container>
# 第 2 步:检查容器 IP 地址和路由
docker exec <container> ip addr show
docker exec <container> ip route show
# 第 3 步:测试 DNS 解析
docker exec <container> nslookup google.com
docker exec <container> cat /etc/resolv.conf
# 第 4 步:测试网络连通性
docker exec <container> ping -c 3 8.8.8.8
docker exec <container> curl -I https://www.google.com
# 第 5 步:容器间通信测试
docker exec container-a ping -c 3 container-b
# 第 6 步:检查 iptables 规则
iptables -t nat -L -n -v
iptables -t filter -L FORWARD -n -v
# 第 7 步:抓包分析
docker exec <container> tcpdump -i eth0 -nn port 80 -c 10常见网络问题诊断
# 问题 1:端口映射不生效
# 诊断:检查宿主机端口是否被占用
ss -tlnp | grep :8080
# 检查 iptables NAT 规则
iptables -t nat -L -n | grep 8080
# 问题 2:容器无法访问外部网络
# 诊断:检查 FORWARD 链
iptables -L FORWARD -n
# 确保没有 DROP 规则阻止转发
# 临时修复
iptables -P FORWARD ACCEPT
# 问题 3:overlay 网络跨主机不通
# 诊断:检查 VXLAN 端口
firewall-cmd --list-ports
# 确保 4789/udp 和 7946/tcp 已开放
firewall-cmd --add-port=4789/udp --permanent
firewall-cmd --add-port=7946/tcp --permanent
firewall-cmd --reload
# 问题 4:DNS 解析失败
# 诊断:检查嵌入式 DNS
docker exec <container> cat /etc/resolv.conf
# 应该包含 nameserver 127.0.0.11
# 如果不是,可能需要重建网络安全最佳实践
网络安全加固清单
# 1. 禁止容器间通信(默认 bridge 网络)
docker network create --opt com.docker.network.bridge.enable_icc=false secure-net
# 2. 使用用户定义网络替代默认网络
# 不要使用默认的 bridge 网络,始终创建自定义网络
# 3. 限制容器可以绑定的端口
docker run -d --name app --network my-net \
-p 127.0.0.1:8080:80 nginx:alpine
# 127.0.0.1: 前缀限制只监听 localhost,不对外暴露
# 4. 启用 overlay 网络加密
docker network create --driver overlay \
--opt encrypted \
secure-overlay
# 5. 使用 DOCKER-USER 链添加自定义防火墙规则
# 这些规则在 Docker 的规则之前生效,且不会被 Docker 重启覆盖
iptables -I DOCKER-USER -s 10.0.0.0/8 -d 172.20.0.0/16 -j DROP网络监控
# 查看容器的网络 I/O 统计
docker stats --no-stream --format "table {{.Name}}\t{{.NetIO}}"
# 使用 nsenter 进入容器网络命名空间
PID=$(docker inspect -f '{{.State.Pid}}' <container>)
nsenter -t $PID -n ip -s link show
nsenter -t $PID -n ss -tlnp
# 使用 cgroup 统计容器网络流量
cat /sys/fs/cgroup/net_cls/docker/<container_id>/net_cls.classid优点
缺点
总结
Docker 网络是容器化部署的核心基础设施。选择合适的网络模式需要权衡隔离性、性能和运维复杂度:
- 开发/测试环境:自定义 bridge 网络,配置简单,支持 DNS
- 单机生产部署:自定义 bridge 网络,按业务域划分多个网络
- 集群部署(Swarm):overlay 网络,实现跨主机通信
- 遗留系统集成:macvlan 网络,容器直接出现在物理网络中
- 安全隔离场景:none 或 internal 网络,完全切断网络访问
关键知识点
- 容器网络命名空间(netns)是 Docker 网络隔离的底层机制
- veth pair 连接容器网络命名空间与宿主机网桥
- Docker 内置 DNS 仅在用户自定义网络中生效,默认 bridge 网络不支持容器名解析
- overlay 网络依赖 key-value store(如 Consul)或 Swarm 进行服务发现
- VXLAN 封装增加 50 字节开销,overlay 网络 MTU 应设为 1450
- iptables 的 DOCKER-USER 链是添加自定义防火墙规则的推荐位置
项目落地视角
- 微服务项目优先使用自定义 bridge 网络,按业务域划分网络,避免所有容器共享默认网络
- 跨环境部署时,网络配置应通过 docker-compose 声明式管理,而非手动 docker network create
- 生产环境需要配合防火墙规则和网络安全策略,不能仅依赖 Docker 默认网络隔离
- 网络拓扑设计应作为架构评审的重要内容,包含网络分段、访问控制和监控策略
常见误区
- 在默认 bridge 网络中使用容器名通信失败,误以为是网络故障,实际是不支持 DNS
- 过度使用 host 网络模式,牺牲隔离性换取所谓性能提升
- 忽略 overlay 网络的 MTU 问题,导致大包传输失败
- 认为容器间网络隔离是默认开启的,实际上默认 bridge 网络中所有容器互通
- 使用 macvlan 后发现宿主机无法访问容器,未配置 macvlan-shim 子接口
进阶路线
- 学习 CNI(Container Network Interface)规范,理解 Kubernetes 网络插件(Calico、Flannel、Cilium)
- 深入 Linux 网络虚拟化技术:network namespace、veth、bridge、iptables、eBPF
- 掌握 service mesh(Istio/Linkerd)中 sidecar 代理的网络拦截与流量管理
- 学习 eBPF 网络可观测性工具(Hubble、Cilium)实现 L7 网络策略和流量可视化
适用场景
- 微服务架构中多容器需要按业务域隔离和互通
- 跨主机集群部署需要容器间直接通信
- 遗留系统集成需要容器获得物理网络可达 IP
- 安全敏感场景需要严格控制容器间的网络访问
落地建议
- 使用 docker-compose 定义网络拓扑,网络配置随应用代码版本管理
- 为每个环境(dev/staging/prod)使用独立的网络命名,避免跨环境污染
- 监控容器网络 I/O 和丢包率,将网络指标纳入可观测性体系
- 建立网络变更的审批和测试流程,避免网络配置变更导致生产事故
- 文档化所有服务的网络依赖关系,包括出站依赖(外部 API、数据库等)
排错清单
- 容器间无法通信时,先确认是否在同一网络,再检查 DNS 解析和防火墙规则
- overlay 网络不通时,检查 Swarm 状态、VXLAN 端口(4789)是否开放
- 端口映射失败时,检查宿主机端口占用和 iptables NAT 规则
- DNS 解析失败时,检查容器的 /etc/resolv.conf 和 Docker 内嵌 DNS 是否正常
- 跨主机通信延迟异常时,检查 MTU 设置和 VXLAN 封装开销
复盘问题
- 当前项目的容器网络划分是否合理?是否存在所有服务共享默认网络的情况?
- 跨主机通信是否遇到性能瓶颈?MTU 设置是否经过验证?
- 网络变更是否有完整的回滚方案和测试流程?
- 是否有网络监控和告警?网络异常是否能及时发现?
