Docker 容器网络原理与常见问题
Docker 的网络模型是很多人初学时最容易感到困惑的部分之一:为什么容器之间可以互相 ping 通?为什么容器访问宿主机是 172.17.0.1?为什么有时候容器可以访问外网,有时候却突然不行了?排错时到底应该从哪里下手?
这篇文章将从原理层面系统梳理 Docker 的网络实现机制,并结合生产环境中最常见的网络问题,给出清晰的排查思路和解决方案。希望看完之后,你能对 Docker 网络有一个相对完整的认知,并在遇到问题时知道“应该先看什么”。
一、Docker 网络基础架构
Docker 默认提供了几种网络驱动(Network Driver),每种驱动决定了容器如何联网、如何与其他容器/宿主机/外部通信。
Docker 默认支持的网络模式
| 网络模式 | 驱动名称 | 容器是否有独立 IP | 是否有端口映射 | 是否隔离 | 典型使用场景 |
|---|---|---|---|---|---|
| bridge | bridge | 是(默认172.17.0.0/16) | 需要 | 网络隔离 | 生产环境最常用模式 |
| host | host | 无(共享宿主机网络) | 无需 | 无隔离 | 需要极高网络性能的场景 |
| none | none | 无网络 | — | — | 自定义网络栈(如 sidecar) |
| overlay | overlay | 是(跨主机) | 需要 | 隔离 | Swarm 集群或 Kubernetes(flannel/calico) |
| macvlan | macvlan | 是(与宿主机同网段) | 无需 | 物理隔离 | 传统物理网络环境、需要独立 MAC |
| ipvlan | ipvlan | 是(与宿主机同网段) | 无需 | 逻辑隔离 | 类似 macvlan 但共享 MAC |
最重要的一句话: 生产环境中 90% 以上的单机 Docker 部署都使用默认的 bridge 模式。
二、Docker bridge 网络原理(最核心内容)
当你执行 docker run 且不指定 --network 时,默认使用 bridge 模式。
核心组件
- docker0 网桥
- 默认创建的虚拟网桥,地址通常是 172.17.0.1/16
- 作用相当于一台虚拟交换机,所有容器都挂在这个“交换机”上
- veth pair(虚拟网卡对)
- 每启动一个容器,Docker 都会在宿主机和容器内部各创建一端 veth
- 宿主机端:vethxxxx(通常随机命名)
- 容器端:eth0(容器内部看到的网卡)
- 容器网络命名空间
- 每个容器都有独立的网络命名空间(Network Namespace)
- 容器内部的 eth0、lo、路由表、iptables 都是独立的
- iptables NAT 规则
- Docker 会自动在 POSTROUTING 链添加 MASQUERADE 规则,让容器访问外网时源地址被 NAT 成宿主机的外网 IP
数据流向示例
容器 A → 容器 B(同主机)
- 容器 A 发出 IP 包,目的 IP 为容器 B 的 172.17.0.2
- 包到达容器 A 的 eth0 → veth pair 另一端(宿主机)
- 宿主机上的 docker0 网桥收到包
- docker0 根据目的 MAC 地址转发到容器 B 的 veth 端
- 进入容器 B 的网络命名空间 → 到达容器 B 的 eth0
容器 → 外网
- 容器发出目的为 8.8.8.8 的包
- 容器路由表默认走 172.17.0.1(docker0)
- 到达 docker0
- docker0 将包转发到宿主机的默认网卡(eth0)
- POSTROUTING 链执行 SNAT,把源 IP 改为宿主机的公网 IP
- 外网返回的包再通过 DNAT 回到容器
三、Docker 网络常见问题与排查思路
1. 容器无法访问外网
最常见现象:容器内 ping 8.8.8.8 超时,但 ping 172.17.0.1 正常
排查顺序(按概率从高到低)
- 宿主机本身能否访问外网?Bash
ping 8.8.8.8 curl -I https://www.google.com - 检查 iptables NAT 规则是否存在Bash
iptables -t nat -L -n -v | grep MASQUERADE # 应该看到类似: # MASQUERADE all -- 172.17.0.0/16 !172.17.0.0/16 - 检查 FORWARD 链是否 ACCEPTBash
iptables -L FORWARD -n -v # Docker 默认会把 FORWARD 策略设为 DROP,然后靠具体规则放行 - 是否误删了 docker0 网桥或 iptables 规则?
- 重启 docker 服务通常能恢复:systemctl restart docker
- 是否使用了自定义 iptables 规则把 FORWARD 链清空了?
- 宿主机是否开启了 ip_forward?Bash
sysctl net.ipv4.ip_forward # 应该为 1
2. 容器之间无法互相通信
现象:同一个 bridge 网络下容器 ping 不通
快速检查:
# 查看容器网络
docker network inspect bridge
# 确认容器是否在同一个网络
docker inspect 容器名 | grep NetworkMode
# 查看容器 IP
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 容器名常见原因:
- 容器不在同一个网络(默认 bridge 除外)
- 容器内部防火墙(iptables / ufw / firewalld)阻断了流量
- 自定义网络驱动配置错误
3. 宿主机访问容器端口失败(宿主机 ping 容器 IP 正常)
原因:Docker 默认不会在宿主机上暴露端口,除非做了端口映射
# 正确做法
docker run -p 8080:80 nginx
# 错误做法
docker run -p 8080 nginx # 这样不会映射宿主机直接访问容器 IP:理论上是可以的,但不推荐(因为容器 IP 是动态的)
4. 容器启动失败:network not found / failed to create NAT chain
常见于:
- Docker 服务异常重启
- iptables 被清空或被其他工具(如 firewalld、nftables)接管
- Docker 版本与内核不兼容(尤其是 4.x 内核上的新版 Docker)
快速修复:
systemctl restart docker
# 或
docker network prune
docker network create bridge5. 使用 host 网络模式后的问题
host 模式下容器与宿主机共享网络命名空间,常见坑:
- 端口冲突(容器监听的端口宿主机已经在用了)
- 多个容器监听同一端口会失败
- 无法使用 -p 做端口映射(因为没有独立网络栈)
6. DNS 解析异常(容器内 curl 域名失败)
常见表现:
- 容器内 ping 8.8.8.8 成功,但 ping www.google.com 失败
原因及解决:
- 检查 /etc/docker/daemon.json 是否配置了 dnsJSON
{ "dns": ["8.8.8.8", "1.1.1.1"] } - 容器是否被设置了无效的 DNS(比如 127.0.0.1 但没有 dnsmasq)
- 宿主机本身的 /etc/resolv.conf 被污染
- 使用 --dns 参数启动容器
四、生产环境推荐配置
// /etc/docker/daemon.json
{
"bip": "172.18.0.1/16", // 更改默认网段,避免冲突
"fixed-cidr": "172.18.0.0/24",
"default-address-pools": [
{"base":"172.19.0.0/16","size":24},
{"base":"172.20.0.0/16","size":24}
],
"dns": ["8.8.8.8", "1.1.1.1", "114.114.114.114"],
"icc": true, // 是否允许容器间通信
"ip6tables": false, // 通常关闭 IPv6
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "5"
}
}五、快速排查清单
当容器网络出问题时,按顺序执行:
- 宿主机网络是否正常?
- docker network ls 和 docker network inspect 查看网络信息
- docker inspect 容器ID 查看 NetworkSettings
- iptables -t nat -L -n -v 检查 NAT
- iptables -L -n -v 检查 FORWARD
- sysctl net.ipv4.ip_forward
- 重启 docker 前先 docker network prune
结语
Docker 的网络本质上就是Linux 网络命名空间 + 虚拟网桥 + iptables NAT + veth pair 的组合。理解了这个底层原理,再遇到网络问题时,你就不再是盲目试各种参数,而是有逻辑地去定位“到底是哪一层断了”。
生产环境中建议:
- 能用 bridge 就尽量用 bridge
- 提前规划好网段,避免冲突
- 把 DNS 写死到 daemon.json
- 不要随意清空 iptables
- 重要业务建议使用 overlay 或 macvlan + 外部负载均衡
欢迎在留言区分享你遇到过的最坑的 Docker 网络问题,或者你当前生产环境的网络模型,我们可以一起讨论更优的方案。
