
Docker 容器无法访问外网的问题排查与解决指南
在使用 Docker Compose 部署多容器项目时,有时会遇到容器无法访问外网的情况。本文将以一个虚拟示例系统为基础,介绍问题出现的原因、排查思路和最终解决方法。
一、问题描述
假设我们有一个项目 demo-project
,包含多个服务容器,例如:
demo-redis
demo-mysql
demo-app
demo-gateway
在容器中执行:
bash
docker-compose exec demo-app sh
ping 8.8.8.8
发现容器无法 ping 通外网 IP,说明容器不能访问外网。
二、排查思路
排查 Docker 容器网络问题,一般按照以下步骤:
1️⃣ 检查容器 DNS 与 IP 访问
- 先尝试访问 IP:
bash
ping 8.8.8.8
-
结果:
- 如果可以 ping 通 → DNS 配置问题
- 如果不能 ping → NAT 或网桥配置问题
本次案例中,ping 8.8.8.8 失败,说明问题在 NAT 或宿主机路由层。
2️⃣ 查看 Docker 自定义网络
bash
docker network ls
docker network inspect demo_project_network
- 查找自定义网络子网,例如:
172.30.0.0/16
- Docker 会在宿主机上生成对应的 Linux 网桥接口,例如
br-abcdef123456
- 注意网桥 IP 与子网网关配置,例如
172.30.0.1/16
3️⃣ 检查宿主机 IP 转发
bash
sysctl net.ipv4.ip_forward
- 如果值为
0
,容器无法出网,需要开启:
bash
sudo sysctl -w net.ipv4.ip_forward=1
4️⃣ 检查 iptables NAT 规则
bash
sudo iptables -t nat -L -n
- 如果
POSTROUTING
链为空,说明没有 SNAT/MASQUERADE 规则 - 容器子网流量出宿主机时没有被 NAT,就无法访问外网
三、解决办法
1️⃣ 添加 MASQUERADE 规则
在宿主机上执行:
bash
sudo iptables -t nat -A POSTROUTING -s 172.30.0.0/16 ! -o br-abcdef123456 -j MASQUERADE
-s 172.30.0.0/16
:容器子网! -o br-abcdef123456
:排除容器内部通信-j MASQUERADE
:动态 SNAT,把源 IP 改为宿主机公网 IP
规则立即生效,容器可以立即访问外网。
2️⃣ 确认生效
在容器中测试:
bash
docker-compose exec demo-app sh
ping 8.8.8.8
curl http://example.com
如果可以 ping 通 IP 和访问网站,说明配置成功。
3️⃣ 多项目情况
如果宿主机上运行多个 Docker Compose 项目:
-
每个项目自定义网络的子网不同,例如:
- 项目 A:172.30.0.0/16 → br-abcdef123456
- 项目 B:172.31.0.0/16 → br-123456abcdef
-
每个网络都需要添加一条 MASQUERADE 规则:
bash
sudo iptables -t nat -A POSTROUTING -s 172.30.0.0/16 ! -o br-abcdef123456 -j MASQUERADE
sudo iptables -t nat -A POSTROUTING -s 172.31.0.0/16 ! -o br-123456abcdef -j MASQUERADE
或者编写脚本自动扫描所有自定义网络添加规则。
四、知识点扩展
1️⃣ MASQUERADE 与 NAT
-
NAT(Network Address Translation)是网络地址转换
-
MASQUERADE 是一种动态 SNAT,常用于容器或路由器出网
-
容器出网流程:
容器 IP → Docker 网桥 → iptables MASQUERADE → 宿主公网 IP → 外网
2️⃣ br-* 网桥接口
- Docker 自定义网络会在宿主机创建对应网桥,命名规则:
br-<网络ID前缀>
- 容器通过该网桥通信和出网
- NAT 规则里排除这个接口,避免内部通信被误 NAT
五、总结
- 容器无法出网,先确认是 DNS 问题 还是 NAT/路由问题
- 查看 Docker 自定义网络和对应网桥接口
- 确保宿主机开启 IP 转发
- 添加
POSTROUTING MASQUERADE
规则让容器出公网 - 多项目需要针对不同子网分别添加规则
通过上述步骤,可以系统解决 Docker 容器无法访问外网的问题,并适用于多项目、多自定义网络场景。