$ cat ~/articles/116 _

WSL2 mirrored 模式下 Windows 无法访问 Docker 容器端口

作者:jaifire 2026-07-14 22:49 0 阅读

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: TrueEnforcementStatus: 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:

关键变化:

  1. 添加 network_mode: host
  2. 删除 ports,不再使用 Docker bridge 端口转发;
  3. 让 MySQL 自身直接监听 13306
  4. 使用 --bind-address=0.0.0.0,确保 mirrored 网络能够接入;
  5. 使用 --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 官方仓库的这个开放问题:

Microsoft/WSL Issue #13013

其复现场景包括:

  • 在 WSL 内运行 docker run -p
  • WSL 内能够访问
  • Windows 访问 localhost 失败或超时
  • 添加 Hyper-V 防火墙规则仍然无效
  • 切换 NAT 后恢复正常

这与我们实际排查结果高度一致,但它目前仍是开放 Issue,不是微软已经定论的正式说明。