Windows 11 + WSL2 + Docker Desktop 端口无法访问问题完整排查与解决
关键词:Windows 11 24H2 / WSL 2.4.x / Docker Desktop / localhost 访问失败 / 端口映射 / mirrored networking / NAT / Excluded Port Range
一、问题背景
在 Windows 11(24H2)+ WSL2 + Docker Desktop 的开发环境中,启动容器后经常会遇到一种非常“反直觉”的问题:
- ✅ 容器内可以访问服务(如 Nginx / MySQL / Redis)
- ✅ WSL 终端中可以访问服务
- ❌ Windows 宿主机(CMD / PowerShell / 浏览器)无法访问
即使已经正确配置了 Docker 的 -p 或 ports: 端口映射,问题依旧存在。
本文将完整记录一次从现象 → 原因 → 系统级根因 → 最终解决方案的排查过程。
二、典型环境
Windows 版本: 10.0.26100.x (Windows 11 24H2)
WSL 版本: 2.4.13.0
Linux Kernel: 5.15.x
Docker Desktop: 4.55.0
三、典型错误现象
1. Nginx
docker run -d --name nginx-web -p 8085:80 nginx
- 容器内:
curl localhost✅ - WSL 中:
curl localhost:8085✅ - Windows 浏览器:
http://localhost:8085❌(无法访问)
2. MySQL
ports:
- "3306:3306"
- 容器内 MySQL 正常启动
- Windows 无法 telnet / 连接 3306
四、第一层根因:WSL 2.4 的 mirrored networking
1. 问题根源
在 WSL 2.4 + Windows 11 24H2 中,WSL 默认启用了 mirrored networking(镜像网络) 。
该模式下:
- WSL 与 Windows 共用网络栈
- Docker Desktop 的端口转发机制无法正确工作
- 表现为:WSL 可访问,Windows localhost 不可访问
这是当前 Docker Desktop 与 WSL 新网络栈之间的已知不兼容问题。
2. 正确解决方式:强制切回 NAT
编辑(或创建)文件:
C:\Users<用户名>.wslconfig
推荐配置如下:
[wsl2]
networkingMode=nat
localhostForwarding=true
[experimental]
autoMemoryReclaim=gradual
然后严格按顺序重启:
wsl --shutdown
- 完全退出 Docker Desktop
- 重新启动 Docker Desktop (以管理员身份启动)
- 再启动 WSL
此时:
- Windows
localhost访问 Docker 容器端口恢复正常
五、WSL 启动时的提示说明(非错误)
wsl: 检测到 localhost 代理配置,但未镜像到 WSL。
NAT 模式下的 WSL 不支持 localhost 代理。
解释:
- Windows 中存在代理配置(HTTP / SOCKS / PAC)
- NAT 模式下 WSL 不会继承 localhost 代理
- 不影响 Docker 端口映射与服务访问
如需消除提示,确保 .wslconfig 中未启用:
autoProxy=true
六、第二层根因:为什么 MySQL 不像 Redis 那样“自动可访问”?
1. 关键事实
Docker 只负责端口转发,不负责服务监听地址。
是否显示 0.0.0.0:端口,取决于容器内服务本身监听在哪个地址。
| 服务 | 默认监听 |
|---|---|
| Redis | 0.0.0.0:6379 |
| MySQL | 127.0.0.1:3306 |
2. MySQL 的安全设计
MySQL 官方镜像默认:
bind-address = 127.0.0.1
这是出于安全考虑,防止数据库被意外暴露。
3. 正确的 MySQL 启动方式
docker-compose 示例(推荐)
services:
mysql:
image: mysql:latest
ports:
- "13306:3306"
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_ROOT_HOST: "%"
command:
--bind-address=0.0.0.0
七、最终隐藏大坑:Windows Excluded Port Range
1. 现象
即使:
- 没有任何进程占用 3306
netstat查不到
Docker 仍然报错:
listen tcp 0.0.0.0:3306: bind: An attempt was made to access a socket in a way forbidden by its access permissions
2. 真正原因
3306 被 Windows 内核预留(Excluded Port Range)
验证方式(管理员 CMD):
netsh int ipv4 show excludedportrange protocol=tcp
只要 3306 落在某个区间内:
- ❌ 无法 bind
- ❌ netstat 看不到
- ❌ Docker / MySQL 无解
八、最终结论与最佳实践
1. 不要再执着于 3306
在 Windows + Docker 环境中,推荐:
| 服务 | 宿主机端口 |
|---|---|
| MySQL | 13306 / 23306 |
| Redis | 16379 |
| Nginx | 18080 / 808x |
2. 核心总结
- ❌ 不是 Docker 配错
- ❌ 不是 MySQL / Nginx 的锅
- ❌ 不是 WSL 使用姿势问题
- ✅ 是 WSL 新网络栈 + Windows 内核端口策略 + Docker Desktop 适配问题的叠加结果
九、一句话总结
真他妈的操蛋
本文适用于:Windows 11 + WSL2 + Docker Desktop 开发环境排错参考