|
| 1 | +# WSL2 网络与代理 |
| 2 | + |
| 3 | +## 目录 |
| 4 | + |
| 5 | +- [推荐判断](#推荐判断) |
| 6 | +- [Mirrored 模式](#mirrored-模式) |
| 7 | +- [NAT 模式](#nat-模式) |
| 8 | +- [Docker daemon 代理](#docker-daemon-代理) |
| 9 | +- [排查命令](#排查命令) |
| 10 | +- [常见结论](#常见结论) |
| 11 | + |
| 12 | +## 推荐判断 |
| 13 | + |
| 14 | +优先使用新版 WSL 的 mirrored 网络;不能使用或行为异常时,再回退到 NAT 模式的 Windows 宿主机地址。 |
| 15 | + |
| 16 | +| 场景 | 代理地址优先级 | 说明 | |
| 17 | +|---|---|---| |
| 18 | +| mirrored 网络 | `127.0.0.1:<port>`,其次 `host.docker.internal:<port>` | 不写死局域网 IP;换 Wi-Fi、路由器或 VPN 时更稳 | |
| 19 | +| NAT 网络 | `/etc/resolv.conf` 里的 `nameserver:<port>` | 该地址通常是 WSL 访问 Windows 宿主机的网关 | |
| 20 | +| Docker daemon 拉取镜像 | 写 systemd drop-in,不只写 shell env | `docker pull` 由 dockerd 发起,不继承普通 shell 的临时代理 | |
| 21 | +| 容器内访问外网 | 写 `~/.docker/config.json` 或 compose `environment` | 只影响新建容器,不影响 daemon 拉镜像 | |
| 22 | + |
| 23 | +Windows 代理软件必须真实监听 WSL 可达地址。若只监听 `127.0.0.1`,mirrored 下通常可用;NAT 下通常需要开启 `Allow LAN` / `允许局域网连接`,并确认 Windows 防火墙允许专用网络入站。 |
| 24 | + |
| 25 | +## Mirrored 模式 |
| 26 | + |
| 27 | +确认 WSL 版本和配置: |
| 28 | + |
| 29 | +```powershell |
| 30 | +wsl --version |
| 31 | +wsl --status |
| 32 | +Get-Content "$env:USERPROFILE\.wslconfig" |
| 33 | +``` |
| 34 | + |
| 35 | +推荐 `.wslconfig` 关键项: |
| 36 | + |
| 37 | +```ini |
| 38 | +[wsl2] |
| 39 | +networkingMode=mirrored |
| 40 | +dnsTunneling=true |
| 41 | +autoProxy=true |
| 42 | +firewall=true |
| 43 | +localhostForwarding=true |
| 44 | +``` |
| 45 | + |
| 46 | +修改后必须重启 WSL: |
| 47 | + |
| 48 | +```powershell |
| 49 | +wsl --shutdown |
| 50 | +``` |
| 51 | + |
| 52 | +代理测试: |
| 53 | + |
| 54 | +```bash |
| 55 | +curl -x http://127.0.0.1:7890 https://registry-1.docker.io/v2/ -I |
| 56 | +curl -x http://host.docker.internal:7890 https://registry-1.docker.io/v2/ -I |
| 57 | +``` |
| 58 | + |
| 59 | +`HTTP/2 401` 或 `401 Unauthorized` 是 Docker Registry 未带 token 时的正常探活结果,说明代理链路能访问 registry。 |
| 60 | + |
| 61 | +`autoProxy=true` 依赖 Windows 系统代理配置;代理软件开着但 Windows 系统代理关闭时,WSL 不一定会自动注入 `HTTP_PROXY` / `HTTPS_PROXY`。 |
| 62 | + |
| 63 | +## NAT 模式 |
| 64 | + |
| 65 | +传统 NAT 模式中,WSL 内的 `127.0.0.1` 是 WSL 自己,不是 Windows。通过 `/etc/resolv.conf` 获取 Windows 宿主机地址: |
| 66 | + |
| 67 | +```bash |
| 68 | +WIN_HOST="$(awk '/nameserver/ {print $2; exit}' /etc/resolv.conf)" |
| 69 | +echo "$WIN_HOST" |
| 70 | +curl -x "http://${WIN_HOST}:7890" https://registry-1.docker.io/v2/ -I |
| 71 | +``` |
| 72 | + |
| 73 | +NAT 模式通常要求代理软件开启: |
| 74 | + |
| 75 | +```yaml |
| 76 | +allow-lan: true |
| 77 | +bind-address: "*" |
| 78 | +mixed-port: 7890 |
| 79 | +``` |
| 80 | +
|
| 81 | +不同代理软件名称不同,重点是代理端口要监听 Windows 的非 loopback 地址,且防火墙允许 WSL 访问。 |
| 82 | +
|
| 83 | +## Docker daemon 代理 |
| 84 | +
|
| 85 | +当前 shell 的代理只影响当前进程: |
| 86 | +
|
| 87 | +```bash |
| 88 | +export http_proxy="http://127.0.0.1:7890" |
| 89 | +export https_proxy="$http_proxy" |
| 90 | +export HTTP_PROXY="$http_proxy" |
| 91 | +export HTTPS_PROXY="$http_proxy" |
| 92 | +``` |
| 93 | + |
| 94 | +Docker daemon 拉取镜像必须写 systemd drop-in: |
| 95 | + |
| 96 | +```bash |
| 97 | +sudo mkdir -p /etc/systemd/system/docker.service.d |
| 98 | +sudo tee /etc/systemd/system/docker.service.d/http-proxy.conf >/dev/null <<'EOF' |
| 99 | +[Service] |
| 100 | +Environment="HTTP_PROXY=http://127.0.0.1:7890" |
| 101 | +Environment="HTTPS_PROXY=http://127.0.0.1:7890" |
| 102 | +Environment="NO_PROXY=localhost,127.0.0.1,::1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,.ts.net,100.64.0.0/10" |
| 103 | +EOF |
| 104 | +sudo systemctl daemon-reload |
| 105 | +sudo systemctl restart docker |
| 106 | +``` |
| 107 | + |
| 108 | +验证 daemon 是否读到代理: |
| 109 | + |
| 110 | +```bash |
| 111 | +systemctl show --property=Environment docker |
| 112 | +docker info | sed -n '/HTTP Proxy/,+4p' |
| 113 | +docker pull hello-world:latest |
| 114 | +``` |
| 115 | + |
| 116 | +若 mirrored 模式下 `127.0.0.1` 不通但 `host.docker.internal` 通,可以把 drop-in 中的代理地址改成 `http://host.docker.internal:<port>`。不要写死 `192.168.x.x`,除非只是临时排障。 |
| 117 | + |
| 118 | +新建容器默认代理写入 Docker CLI 配置: |
| 119 | + |
| 120 | +```bash |
| 121 | +mkdir -p ~/.docker |
| 122 | +cat >~/.docker/config.json <<'EOF' |
| 123 | +{ |
| 124 | + "proxies": { |
| 125 | + "default": { |
| 126 | + "httpProxy": "http://127.0.0.1:7890", |
| 127 | + "httpsProxy": "http://127.0.0.1:7890", |
| 128 | + "noProxy": "localhost,127.0.0.1,::1" |
| 129 | + } |
| 130 | + } |
| 131 | +} |
| 132 | +EOF |
| 133 | +``` |
| 134 | + |
| 135 | +这只影响之后创建的容器,不会修复 `docker pull`。 |
| 136 | + |
| 137 | +## 排查命令 |
| 138 | + |
| 139 | +优先使用 skill 自带脚本,避免 PowerShell 与 bash 嵌套引号导致误判: |
| 140 | + |
| 141 | +```powershell |
| 142 | +.\scripts\Test-WslDockerProxy.ps1 -Distro Ubuntu -HttpPort 7890 |
| 143 | +.\scripts\Test-WslDockerProxy.ps1 -Distro Ubuntu -HttpPort 7890 -TestDockerPull |
| 144 | +``` |
| 145 | + |
| 146 | +脚本默认只诊断,不修改配置。`-TestDockerPull` 会额外执行 `docker pull hello-world:latest` 和 `docker run --rm hello-world`。 |
| 147 | + |
| 148 | +Windows 侧检查代理监听地址: |
| 149 | + |
| 150 | +```powershell |
| 151 | +Get-NetTCPConnection -LocalPort 7890 -ErrorAction SilentlyContinue | |
| 152 | + Select-Object LocalAddress,LocalPort,State,OwningProcess |
| 153 | +``` |
| 154 | + |
| 155 | +WSL 侧检查名称解析和端口: |
| 156 | + |
| 157 | +```bash |
| 158 | +getent hosts host.docker.internal || true |
| 159 | +for hp in 127.0.0.1:7890 host.docker.internal:7890; do |
| 160 | + h="${hp%:*}" |
| 161 | + p="${hp##*:}" |
| 162 | + printf '%s ' "$hp" |
| 163 | + timeout 3 bash -lc "</dev/tcp/$h/$p" >/dev/null 2>&1 && echo ok || echo fail |
| 164 | +done |
| 165 | +``` |
| 166 | + |
| 167 | +区分 shell 代理和 daemon 代理: |
| 168 | + |
| 169 | +```bash |
| 170 | +env | grep -i proxy |
| 171 | +systemctl show --property=Environment docker |
| 172 | +docker info | sed -n '/HTTP Proxy/,+4p' |
| 173 | +``` |
| 174 | + |
| 175 | +查看 Docker 拉取错误: |
| 176 | + |
| 177 | +```bash |
| 178 | +docker --debug pull hello-world:latest |
| 179 | +journalctl -u docker --since '10 minutes ago' --no-pager -l |
| 180 | +``` |
| 181 | + |
| 182 | +## 常见结论 |
| 183 | + |
| 184 | +- `curl -x ... https://registry-1.docker.io/v2/` 能返回 401,只说明当前 shell 代理可用;`docker pull` 仍需要 daemon 代理。 |
| 185 | +- `docker info` 显示了 HTTP Proxy,但仍出现 `unexpected EOF`,通常是代理软件、规则、TLS 中间链路或 Registry 规则问题;继续用 `journalctl -u docker` 和代理客户端日志定位。 |
| 186 | +- mirrored 模式下可能出现 `localhostForwarding ignored` 提示,这是因为 mirrored 网络已有自己的 localhost 行为,不代表代理一定不可用。 |
| 187 | +- `external-controller` 不是代理端口,不要把它写进 `HTTP_PROXY`。 |
| 188 | +- 本机开发默认避免把数据库、Portainer 等服务暴露到局域网;端口绑定优先使用 `127.0.0.1:hostPort:containerPort`。 |
0 commit comments