1. 问题场景
- 操作系统:Windows 11 + WSL2 Ubuntu
- WSL 网络模式:
networkingMode=mirrored - Docker:Docker CE 直接安装在 WSL Ubuntu 中,不使用 Docker Desktop
- 数据库:Docker Compose 启动 MySQL
- 端口映射:Windows/WSL 端口
13306映射到容器端口3306
初始 Compose 配置:
services:
mysql:
image: mysql:8.0.39
container_name: mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
TZ: Asia/Shanghai
MYSQL_ROOT_HOST: "%"
volumes:
- mysql_data:/var/lib/mysql
ports:
- "13306:3306"
volumes:
mysql_data:
2. 故障现象
WSL 内可以正常连接 MySQL:
mysql -u root -h 127.0.0.1 -P 13306 -p
Windows 侧连接同一个地址和端口失败:
Test-NetConnection 127.0.0.1 -Port 13306
结果:
TcpTestSucceeded : False
Python TCP 测试也表现为连接超时。此时还没有进入 MySQL 登录阶段,因此问题与用户名、密码、MySQL 用户授权无关。
3. 排查过程
3.1 确认容器运行和端口发布状态
在 WSL 中执行:
docker ps
可以看到类似结果:
0.0.0.0:13306->3306/tcp, :::13306->3306/tcp
这说明:
- MySQL 容器运行正常;
- Docker 已创建端口发布配置;
- WSL 内部可以通过该发布端口连接容器。
但这并不能证明 Windows 的 TCP 流量可以正确进入 Docker bridge 网络。
3.2 确认 WSL 网络配置
Windows 用户目录下的 .wslconfig:
[wsl2]
networkingMode=mirrored
dnsTunneling=true
firewall=true
autoProxy=true
3.3 检查 Hyper-V 防火墙
检查 WSL 的 Hyper-V 防火墙策略:
Get-NetFirewallHyperVVMSetting `
-PolicyStore ActiveStore `
-Name '{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}'
本机默认入站策略为:
DefaultInboundAction : Block
添加了仅允许 TCP 13306 的规则:
New-NetFirewallHyperVRule `
-Name "WSL-MySQL-13306" `
-DisplayName "WSL MySQL 13306" `
-Direction Inbound `
-VMCreatorId '{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}' `
-Protocol TCP `
-LocalPorts 13306
规则状态为 Enabled: True、EnforcementStatus: OK,但 Windows 连接仍然失败。这说明防火墙不是唯一阻断点,单独增加防火墙规则不足以解决本次问题。
4. 根本原因
本次环境中,问题出现在以下组合:
WSL2 mirrored 网络
+
WSL 内直接运行 Docker CE
+
Docker bridge 网络及 ports 发布
Docker 的 ports 发布依赖 Linux 网络命名空间和 iptables/nftables 转发,将 WSL 宿主端口转发到容器 bridge 网络。
在本机的 mirrored 网络环境下:
- WSL 内部发起的连接可以经过 Docker 的转发规则进入容器;
- Windows 发往
127.0.0.1:13306的连接没有正确完成到容器 bridge 网络的转发; - 因而 WSL 内连接成功,Windows 连接却一直超时。
需要注意:docker ps 显示 0.0.0.0:13306->3306/tcp,只代表 Docker 在 Linux 网络栈中创建了发布规则,不等于 Windows 一定能看到一个原生 Windows 监听端口。
5. 最终解决方案
保留 networkingMode=mirrored,让 MySQL 容器使用 WSL 宿主网络:
services:
mysql:
image: mysql:8.0.39
container_name: mysql
restart: always
network_mode: host
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
TZ: Asia/Shanghai
MYSQL_ROOT_HOST: "%"
volumes:
- mysql_data:/var/lib/mysql
command:
- mysqld
- --port=13306
- --bind-address=0.0.0.0
- --mysqlx=0
volumes:
mysql_data:
关键变化:
- 添加
network_mode: host; - 删除
ports,不再使用 Docker bridge 端口转发; - 让 MySQL 自身直接监听
13306; - 使用
--bind-address=0.0.0.0,确保 mirrored 网络能够接入; - 使用
--mysqlx=0,避免额外在宿主网络暴露 MySQL X Protocol 的33060端口。
在 host 网络模式下,容器和 WSL 使用同一个网络命名空间,Windows 流量不再经过 Docker bridge 的 DNAT 路径。
6. 应用配置
在 WSL 中执行:
cd /mnt/d/wsl-data/server
docker compose down
docker compose up -d
docker logs -f mysql
检查监听端口:
ss -lntp | grep 13306
预期 MySQL 直接监听 WSL 的 13306。
7. 验证结果
Windows PowerShell:
Test-NetConnection 127.0.0.1 -Port 13306
预期结果:
TcpTestSucceeded : True
Windows MySQL 客户端连接参数:
Host: 127.0.0.1
Port: 13306
User: root
Password: 配置的密码
也可以通过 Python 验证 MySQL 握手:
py test_mysql_connection.py --host 127.0.0.1 --port 13306
8. 为什么不使用其他方案
切换回 NAT
NAT 模式配合 localhostForwarding=true 通常可以解决 Docker 发布端口问题,但本机其他功能依赖 mirrored 网络,因此没有采用。
只添加 Hyper-V 防火墙规则
本次实测规则创建成功、状态正常,但 TCP 连接仍然失败。防火墙规则可以放行流量,却不能修复 mirrored 网络与 Docker bridge 转发链路之间的问题。
设置 "iptables": false
不推荐。Docker bridge 网络、容器出站 NAT 和端口发布通常依赖 Docker 自动维护的 iptables/nftables 规则。关闭后需要手工管理网络规则,并可能导致更多容器网络故障。
WSL 中增加 TCP 代理
可以运行普通 WSL TCP 代理,将一个 WSL 原生监听端口转发到容器端口,但会增加额外进程、端口和启动管理。对于当前只有一个 MySQL 服务的 Compose,network_mode: host 更简单。
9. host 网络模式的注意事项
ports在 host 网络模式下无效,应当删除;- MySQL 会直接占用 WSL 的
13306,其他进程不能再使用该端口; - 网络隔离程度低于 bridge 模式;
- 如果 Compose 中增加其他业务容器,它们不能再依赖
mysql:3306这种 bridge 网络服务名访问当前 MySQL,需要重新设计连接地址; - mirrored 模式可能使服务暴露到局域网,应结合 Hyper-V/Windows 防火墙限制来源;
- 不要使用弱密码,生产环境不应直接允许
root从任意来源登录; - 数据卷没有改变,切换网络模式不会删除
mysql_data中的数据库数据。
10. 结论
在必须保留 WSL2 mirrored 网络、并且 Docker CE 直接运行于 WSL 内部的情况下,如果 Windows 无法访问 Docker bridge 发布的数据库端口,可以让数据库容器使用 network_mode: host,并让数据库进程直接监听所需宿主端口。
本次最终链路为:
Windows 127.0.0.1:13306
↓
WSL mirrored 网络栈
↓
MySQL(host 网络,直接监听 13306)
该方案保留了 mirrored 网络,并绕开了出现兼容问题的 Docker bridge/iptables 端口转发路径。
参考资料
当前现象接近微软 WSL 官方仓库的这个开放问题:
其复现场景包括:
- 在 WSL 内运行
docker run -p - WSL 内能够访问
- Windows 访问
localhost失败或超时 - 添加 Hyper-V 防火墙规则仍然无效
- 切换 NAT 后恢复正常
这与我们实际排查结果高度一致,但它目前仍是开放 Issue,不是微软已经定论的正式说明。