一、问题描述
在 Docker 容器内部署了一个 WebSocket 服务器,程序监听的地址为
127.0.0.1:3001。并在 docker compose 中进行了端口映射。
# docker-compose.yml
version: "x"
services:
xxx:
ports:
- 3000:3000
- 3001:3001
- 6099:6099
container_name: xxx
network_mode: bridge
restart: always
image: ...外部(宿主机)无法通过
ws://127.0.0.1:3001连接该服务。
二、解决方案
将 WebSocket 服务器的监听主机(host)从
127.0.0.1修改为0.0.0.0。修改后,外部即可通过
ws://127.0.0.1:3001正常连接。
# 在宿主机上执行
netstat -an | grep "LISTEN" | grep "3001"
# 应该的输出
tcp4 0 0 127.0.0.1.3001 *.* LISTEN三、原因分析
问题的核心在于 127.0.0.1 与 0.0.0.0 在网络绑定范围上存在本质区别,尤其是在 Docker 的网络隔离环境下。
1. 127.0.0.1 (本地回环地址 / localhost)
监听范围:它只监听本地回环接口。在 Docker 容器中,这个“本地”指的是容器内部。
访问限制:因此,只有来自容器内部的请求才能访问到该服务。外部流量(即使经过端口映射)无法访问。
数据流:所有发往
127.x.x.x的数据包都不会通过网卡向外发送,而是在操作系统内核层面直接返回。
2. 0.0.0.0 (通配地址)
监听范围:它会绑定到本机所有可用的网络接口上。在容器中,这既包括了容器的回环接口,也包括了连接 Docker 网络的虚拟网卡(如
eth0)。访问限制:允许来自任何网络接口的连接。因此,从宿主机映射过来的流量可以通过容器的虚拟网卡被服务正确接收。
实际效果:监听
0.0.0.0:3001实际上是监听了所有网卡的3001端口。
四、补充说明
一个常见的场景是:在非容器化的本机开发环境中(例如 php,html 开发),通过浏览器访问服务时,访问 127.0.0.1 和 0.0.0.0 (通常体现为通过本机IP或localhost访问)的效果是等价的。
然而,在 Docker 这种存在网络隔离的环境中,容器的 127.0.0.1 和宿主机的 127.0.0.1 并不是同一个东西,因此必须使用 0.0.0.0 来接收外部连接。