一、问题背景
环境说明:
- 宿主机系统:Ubuntu / Debian
- 防火墙:UFW(
Status: active) - Docker:已安装并运行
- 场景: Docker 容器需要访问 宿主机上运行的服务端口 (例如数据库、Web 服务、面板服务等)
示例需求:
Docker 容器 → 宿主机 IP:端口
但实际情况是:
宿主机本机访问正常, Docker 容器内访问却一直超时 / 卡住。
二、典型症状
宿主机访问正常
curl http://127.0.0.1:PORT
或:
curl http://宿主机IP:PORT
返回正常(200 / 302 / 404 均表示服务存在)。
Docker 容器访问宿主机失败
docker exec -it 容器名 curl http://宿主机IP:PORT
表现为:
Trying xxx.xxx.xxx.xxx:PORT...
(一直卡住,直到 Ctrl+C)
不是 Connection refused,而是直接超时
已经放行端口,但依然无效
sudo ufw allow PORT/tcp
容器访问仍然失败。
三、问题根因(核心)
根本原因不是 Docker,也不是服务本身,而是 UFW 的默认行为
UFW 默认策略:
Default: deny (incoming), allow (outgoing), deny (routed)
关键点在于:
Docker 容器访问宿主机 ≠ 本机访问 Docker 容器 → 宿主机 的流量会经过 Docker bridge 网卡
而 UFW:
- 会拦截来自 bridge 接口(br-xxxx) 的入站流量
- 即使你已经
ufw allow PORT
只放行端口 ≠ 放行 Docker bridge
四、Docker 网络结构说明(关键理解)
Docker 使用 bridge 网络时,结构类似:
容器(172.18.0.x)
↓
Docker bridge(br-xxxx,172.18.0.1)
↓
宿主机服务(监听 PORT)
172.18.0.1是宿主机在 Docker 网络中的地址- 容器访问宿主机,实际是 通过 br-xxxx 接口入站
五、一步步排查方法
查看容器所在网段和宿主机网关
docker inspect 容器名 | grep -E '"IPAddress"|"Gateway"'
示例结果:
"IPAddress": "172.18.0.4"
"Gateway": "172.18.0.1"
确认宿主机 bridge 网卡名
ip link show | grep br-
或根据 NetworkID:
docker network inspect 网络名
对应的 bridge 通常为:
br-<NetworkID前12位>
验证宿主机 bridge 地址可访问
curl http://172.18.0.1:PORT
如果宿主机能访问,说明:
- 服务监听正常
- 问题只在防火墙层
六、正确解决方案(关键)
正确做法:放行 Docker bridge 接口,而不是只放端口
示例:
sudo ufw allow in on br-xxxxxxxxxxxx to any port PORT proto tcp
sudo ufw reload
将其中:
br-xxxxxxxxxxxx替换为你的 Docker bridge 网卡名PORT替换为宿主机服务端口
这条规则的安全性
只允许 Docker 内部访问
不暴露端口到公网
不影响其他 UFW 防护规则
符合最小权限原则
七、验证是否成功
在容器内测试
docker exec -it 容器名 curl http://172.18.0.1:PORT
如果看到:
Connected to 172.18.0.1
HTTP/1.1 ...
说明链路已完全打通。
八、为什么 ufw allow PORT 不够?
很多教程只写:
sudo ufw allow 8080/tcp
但在 Docker + UFW 场景下,这通常 不够,因为:
- UFW 是按 接口 + 方向 生效
- Docker bridge 不是
eth0 - bridge 入站默认被拦截
接口维度的放行才是关键
九、推荐的最终规则模型
外网 ❌ 无法直接访问宿主机端口
Docker 容器 ✅ 可以访问宿主机端口
通过:
ufw allow in on br-xxxx to any port PORT
实现精确控制。
十、一句话记住这个坑
Docker 容器访问宿主机 + UFW 开启时, 一定要放行 Docker bridge 接口(br-xxxx)