背景描述

在一台位于局域网内的 Linux 服务器(192.168.31.50)上运行多个 Docker 容器,其中包括:

  • napcatmlikiowa/napcat-docker

  • 其他常规服务(RocketMQ、Nacos 等)

遇到一个非常迷惑的问题

Docker 容器端口已映射,iptables 正常,但同一局域网内无法访问容器服务;
而非容器服务、以及通过 frp 从公网访问却一切正常。


问题现象

Docker 容器端口映射看起来完全正常

docker ps
0.0.0.0:3001->3001/tcp

看起来没有任何问题,端口也确实暴露到了宿主机。


同一局域网 Windows 访问容器端口失败(超时)

curl -v http://192.168.31.50:3001/
* connect to 192.168.31.50 port 3001 failed: Timed out

同一局域网访问「非 Docker 服务端口」完全正常

curl -v http://192.168.31.50:2017/
HTTP/1.1 200 OK

服务器本机访问 Docker 映射端口也失败

curl -v http://192.168.31.50:3001/
Failed connect to 192.168.31.50:3001; 拒绝连接

5️⃣ 通过 frp 映射到公网访问却是正常的

  • 外网访问:✅

  • frp 映射端口:✅


初步排查(全部被排除)

在问题初期,重点怀疑过以下方向,但最终全部排除:

  • ❌ Docker 端口映射问题(已是 0.0.0.0

  • ❌ iptables / 防火墙拦截(INPUT / FORWARD 全 ACCEPT)

  • ❌ 路由 / NAT 回环问题(非容器端口局域网可访问)

  • ❌ Docker bridge 网络损坏(frp 能通说明链路存在)


关键对照实验(决定性)

使用 host 网络模式启动容器

docker run --rm --network host mlikiowa/napcat-docker

结果:

立刻可以从局域网访问服务

这是整个排错过程中最关键的一步


最终结论(根因)

问题不在 Docker 网络、不在防火墙、不在路由,
而在于容器内的应用本身只监听了
127.0.0.1(localhost),没有监听 0.0.0.0

为什么会导致这种现象?

Docker bridge + 端口映射的真实访问路径:

局域网客户端
  ↓
宿主机 IP:端口
  ↓
iptables DNAT
  ↓
容器 IP:端口

如果容器内服务只监听:

127.0.0.1:3001

那么:

  • Docker 已成功转发流量 ✅

  • 但容器内 没有进程接受来自外部网卡的连接

  • 表现为:

    • 局域网访问:超时

    • 宿主机访问映射端口:拒绝连接


为什么 frp 可以访问?

因为 frp 的工作方式是:

容器 → 主动连接公网 frp server → 外部访问

这是容器主动向外建立连接,不依赖对外监听:

  • 监听在 127.0.0.1 也完全没问题

  • 这反而掩盖了问题本身


为什么 --network host 能解决?

在 host 网络模式下:

容器的 127.0.0.1 == 宿主机的 127.0.0.1

于是:

  • 服务监听 localhost

  • 实际等同于监听宿主机

  • 从局域网访问自然就通了

这是绕过 Docker 网络隔离的结果,而不是 Docker 网络修好了。


如何确认这个问题?

如果容器内有工具,可以检查监听地址:

ss -lntp
# 或
netstat -lntp

通常会看到类似:

LISTEN 127.0.0.1:3001

而不是:

LISTEN 0.0.0.0:3001

解决方案总结

✅ 推荐方案:让应用监听 0.0.0.0

根据语言 / 框架不同,例如:

  • FastAPI:

    uvicorn app:app --host 0.0.0.0 --port 3001
    
  • Go:

    http.ListenAndServe("0.0.0.0:3001", nil)
    
  • Java / Spring Boot:

    server.address=0.0.0.0
    

或者通过容器环境变量显式指定监听地址。


⚠ 可接受方案:继续使用 --network host

docker run -d --network host xxx

适合:

  • 内网环境

  • 不需要端口隔离

  • 不运行多个同端口服务


经验总结(血泪点)

Docker 端口映射 ≠ 应用对外监听

当你遇到以下组合时,一定要怀疑监听地址

  • Docker 显示端口已暴露

  • iptables 正常

  • frp / 外网能通

  • 局域网访问 Docker 端口却不通

  • host 网络模式一用就好


一句话结论

这是一个“看起来像网络问题,实则是应用监听配置问题”的典型 Docker 排错案例。