Docker 被防火墙拦截的排查与修复
背景
在宿主机用 Docker 部署 .NET / Java 应用,宿主机上同时跑着 MySQL、Redis 等中间件。容器启动时报连接错误,例如:
Unable to connect to any of the specified MySQL hosts.
An exception was thrown while activating RedisCache.但宿主机上对应服务明明启动了,bind 也设了 0.0.0.0,从容器内 ping 宿主机 IP 也能通。问题到底出在哪?
根因
UFW(Uncomplicated Firewall)等防火墙的 INPUT 链默认策略是 DROP,会丢弃所有进入宿主机的流量。Docker 容器访问宿主机服务时,源 IP 是 docker 网段(如 172.18.0.x),这些流量会被 INPUT 链默认丢弃,导致容器 TCP 连不上宿主机端口。
ping 能通是因为 ICMP 常被放行;但 MySQL / Redis 是 TCP,会被拦下。这是「能 ping 通但端口连不上」的典型成因之一。
排查步骤
1. 查看本机 IP 段
确认宿主机真实网卡 IP 与 docker 网桥网段,避免把 Server 写成 docker0 的 IP(网桥上没有跑业务服务)。
ip addr | grep "inet " | grep -v 127.0.0.1典型输出:
inet 192.168.1.100/24 ... eth0 # 真实网卡,可作 Server
inet 172.18.0.1/16 ... docker0 # 默认网桥,不能作 Server
inet 172.19.0.1/16 ... br-xxxx # 自定义 docker 网络2. 查看防火墙是否放行 docker 网段
sudo iptables -L INPUT -n --line-numbers | head -20如果看到 Chain INPUT (policy DROP),说明默认拒绝所有进入流量,docker 容器流量也会被丢弃。
3. 确认目标服务监听 0.0.0.0
ss -tlnp | grep 6379127.0.0.1:6379→ 只监听本地,容器连不进来,需改bind 0.0.0.0。0.0.0.0:6379或*:6379→ 监听所有网卡,符合预期。
4. 容器内验证 TCP 连通
ping 走 ICMP,不能代表 TCP 可达。要用 TCP 探测:
docker exec <容器名> sh -c "(echo > /dev/tcp/<宿主机IP>/6379) 2>&1 && echo OK || echo FAIL"返回 FAIL 即说明 TCP 被防火墙拦截。
修复:放行 docker 网段
放行 docker 网段到宿主机的流量(按上一步查到的网段填写):
sudo iptables -I INPUT -s 172.18.0.0/16 -j ACCEPT
sudo iptables -I INPUT -s 172.19.0.0/16 -j ACCEPT放行后再次验证:
docker exec <容器名> sh -c "(echo > /dev/tcp/<宿主机IP>/6379) 2>&1 && echo OK || echo FAIL"返回 OK 即网络层已通。
持久化规则
iptables 规则重启后默认丢失,需要持久化:
# Ubuntu / Debian
sudo apt install iptables-persistent -y
sudo netfilter-persistent save连接字符串的 Server 怎么填
放行网段后,连接字符串里的 Server 要写宿主机真实网卡 IP(用 ip addr 查到的 eth0 地址),不要写以下值:
| 错误写法 | 原因 |
|---|---|
localhost / 127.0.0.1 | 容器内的 localhost 指向容器自己,连不到宿主机 |
docker0 的 IP(如 172.18.0.1) | 网桥上没有跑业务服务,连不上 |
host.docker.internal | 部分 Linux docker 环境下解析不稳定,实测可能连不通 |
正确示例(假设宿主机 eth0 IP 为 192.168.1.100):
{
"Redis": {
"Host": "192.168.1.100",
"Port": 6379
}
}选 IP 时务必通过
ip addr确认它绑定在eth0这类真实网卡上,而不是 docker 网桥。
安全说明
放行 172.18.0.0/16 是放行 docker 内部容器到宿主机的流量,不会对公网开放端口。docker 网段是私网,公网流量源 IP 不会是 172.18.x.x,因此不影响对外暴露面。如果仍需收紧,可以把规则限定到具体端口:
sudo iptables -I INPUT -s 172.18.0.0/16 -p tcp --dport 6379 -j ACCEPT
sudo iptables -I INPUT -s 172.18.0.0/16 -p tcp --dport 3306 -j ACCEPT小结
容器访问宿主机服务连不上,按这个顺序排查最快定位:
- 目标服务是否监听
0.0.0.0(ss -tlnp)。 - 容器内 TCP 是否可达(
/dev/tcp探测,不要只用ping)。 - 防火墙 INPUT 链是否放行 docker 网段(
iptables -L INPUT)。 - 连接字符串 Server 是否写了宿主机真实网卡 IP(不是 localhost / docker0 / host.docker.internal)。
- 账号授权是否允许 docker 网段来源(数据库的
user@host)。
五层都通,连接就稳了。
