【踩坑记录】Docker 容器访问宿主机服务,在 UFW 开启情况下无法连接的解决方案

一、问题背景

环境说明:

  • 宿主机系统:Ubuntu / Debian
  • 防火墙:UFW(Status: active
  • Docker:已安装并运行
  • 场景: Docker 容器需要访问 宿主机上运行的服务端口 (例如数据库、Web 服务、面板服务等)

示例需求:

Docker 容器  →  宿主机 IP:端口

但实际情况是:

宿主机本机访问正常, Docker 容器内访问却一直超时 / 卡住。


二、典型症状

:one: 宿主机访问正常

curl http://127.0.0.1:PORT

或:

curl http://宿主机IP:PORT

返回正常(200 / 302 / 404 均表示服务存在)。


:two: Docker 容器访问宿主机失败

docker exec -it 容器名 curl http://宿主机IP:PORT

表现为:

Trying xxx.xxx.xxx.xxx:PORT...
(一直卡住,直到 Ctrl+C)

:warning: 不是 Connection refused,而是直接超时


:three: 已经放行端口,但依然无效

sudo ufw allow PORT/tcp

:backhand_index_pointing_right: 容器访问仍然失败。


三、问题根因(核心)

:red_exclamation_mark: 根本原因不是 Docker,也不是服务本身,而是 UFW 的默认行为

UFW 默认策略:

Default: deny (incoming), allow (outgoing), deny (routed)

关键点在于:

Docker 容器访问宿主机 ≠ 本机访问 Docker 容器 → 宿主机 的流量会经过 Docker bridge 网卡

而 UFW:

  • 会拦截来自 bridge 接口(br-xxxx) 的入站流量
  • 即使你已经 ufw allow PORT

:backhand_index_pointing_right: 只放行端口 ≠ 放行 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 接口入站

五、一步步排查方法

:one: 查看容器所在网段和宿主机网关

docker inspect 容器名 | grep -E '"IPAddress"|"Gateway"'

示例结果:

"IPAddress": "172.18.0.4"
"Gateway": "172.18.0.1"

:two: 确认宿主机 bridge 网卡名

ip link show | grep br-

或根据 NetworkID:

docker network inspect 网络名

对应的 bridge 通常为:

br-<NetworkID前12位>

:three: 验证宿主机 bridge 地址可访问

curl http://172.18.0.1:PORT

如果宿主机能访问,说明:

  • 服务监听正常
  • 问题只在防火墙层

六、正确解决方案(关键)

:white_check_mark: 正确做法:放行 Docker bridge 接口,而不是只放端口

示例:

sudo ufw allow in on br-xxxxxxxxxxxx to any port PORT proto tcp
sudo ufw reload

将其中:

  • br-xxxxxxxxxxxx 替换为你的 Docker bridge 网卡名
  • PORT 替换为宿主机服务端口

:locked_with_key: 这条规则的安全性

  • :check_mark: 只允许 Docker 内部访问
  • :check_mark: 不暴露端口到公网
  • :check_mark: 不影响其他 UFW 防护规则
  • :check_mark: 符合最小权限原则

七、验证是否成功

:one: 在容器内测试

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 入站默认被拦截

:backhand_index_pointing_right: 接口维度的放行才是关键


九、推荐的最终规则模型

外网  ❌  无法直接访问宿主机端口
Docker 容器 ✅  可以访问宿主机端口

通过:

ufw allow in on br-xxxx to any port PORT

实现精确控制。


十、一句话记住这个坑

Docker 容器访问宿主机 + UFW 开启时, 一定要放行 Docker bridge 接口(br-xxxx)