开发与 AI · Clash 技术博客

Docker 与 WSL2 怎么走 Clash 代理?镜像拉取超时与网络配置

Windows、WSL2、Docker 引擎和容器各有自己的网络环境,所以浏览器能用并不能证明 docker pull 会用代理。按请求发起位置逐层设置即可。

  • Docker
  • WSL2
  • 开发环境
本文目录

docker pull、WSL 命令和容器请求来自不同位置

Docker 与 WSL2 要走 Windows 上的 Clash,不能只在一个终端里写代理。docker pull、WSL 里的 apt、镜像构建中的 RUN 和启动后的容器,分别由不同组件发出请求;哪一步报错,就配置哪一层。

Windows 上的 Clash 监听 127.0.0.1,只保证 Windows 本机能访问。WSL2 默认运行在 NAT 虚拟网络里,容器还有自己的网络命名空间;它们看到的 127.0.0.1 通常是自己,不是宿主机。

Docker 拉镜像、docker build 里的下载、运行中容器访问外网,也由不同组件发起。只在 WSL shell 里 export HTTP_PROXY,未必能让 Docker Desktop 的镜像拉取走代理。

报错发生在哪一层

失败动作真正发起请求的组件
curl / apt / git 在 WSL 里超时WSL 发行版中的进程
docker pull 超时Docker Desktop 或 Docker Engine 守护进程
RUN apt-get 在 build 时超时BuildKit / 构建容器
启动后的应用请求失败运行中的容器
Windows、WSL2 与容器之间的代理路径
  1. Windows 上的 Clash监听局域网可达的 mixed-port
  2. WSL2使用宿主机地址访问该端口
  3. Docker 构建显式接收 HTTP_PROXY 和 HTTPS_PROXY
  4. 容器内工具再处理 Git、npm 或包管理器

127.0.0.1 在每一层都指向自己。代理地址必须填写上一层真实可达的宿主机地址。

宿主代理端口要能被虚拟网络访问

先在 Clash 查看实际的 HTTP 或 mixed-port,常见示例是 7890,但你的客户端可能不同。WSL 使用宿主 IP 访问时,这个连接会被当作局域网连接,客户端需要允许 LAN 访问,并监听 WSL 能到达的地址。

允许 LAN 会扩大端口可达范围。只在可信的家庭或单位私有网络使用,并让 Windows 防火墙限制来源;不要把无认证代理暴露到公共 Wi-Fi 或公网。完成测试后若不再需要,可以关闭 LAN 访问。

WSL 默认 NAT 用宿主 IP,镜像网络可直接试 127.0.0.1

默认 NAT 模式下,可以在 WSL 读取默认路由的下一跳,它通常就是 Windows 宿主在 WSL 虚拟网络里的地址。Windows 11 22H2 及后续版本启用 mirrored networking 后,WSL 可以直接通过 127.0.0.1 访问 Windows 服务。

WSL 默认 NAT 测试,7890 是待替换的 Clash 端口
HOST_IP=$(ip route show | grep -i default | awk '{ print $3 }')
CLASH_PORT=7890
echo "Windows host: $HOST_IP"
curl -I -x "http://${HOST_IP}:${CLASH_PORT}" https://example.com

这条 curl 得到 HTTP 响应后,才有必要设置 WSL 环境变量。若代理端口直接返回 Connection refused,问题在 Clash 监听、允许 LAN 或防火墙;若端口可达但外部请求 timeout,再检查节点和规则。

端口可达后,再给 WSL 命令行设置环境变量

当前 WSL shell 临时生效,7890 是端口占位值
HOST_IP=$(ip route show | grep -i default | awk '{ print $3 }')
CLASH_PORT=7890
export HTTP_PROXY="http://${HOST_IP}:${CLASH_PORT}"
export HTTPS_PROXY="http://${HOST_IP}:${CLASH_PORT}"
export NO_PROXY="localhost,127.0.0.1,::1"
curl -I https://example.com

临时变量关闭终端就会消失,适合排障。用 apt、git 或 curl 重做同一个请求,确认成功后,再按 shell 类型写进自己的启动文件。

Windows 支持的 WSL 版本也可以在 .wslconfig 使用 autoProxy=true,让 WSL 导入 Windows 的 HTTP 代理信息。启用后要重启 WSL,并检查实际变量值。

NO_PROXY 应包含只在 WSL 内部提供的服务,避免访问 localhost 时绕到 Windows 代理。公司内部域名和私有网段是否加入,要按实际网络决定。

docker pull 失败,要到 Docker Desktop 的代理设置

Docker Desktop 拉取镜像和 WSL shell 命令由不同进程发起。打开 Docker Desktop 的 Settings,在 Resources / Proxies(不同版本标签可能略有变化)里填写宿主可用的 HTTP、HTTPS 代理,并应用设置。

Docker 官方文档明确说明,Desktop 不读取 daemon.json 里的守护进程代理配置。不要因为 WSL 终端已经能访问,就跳过 Docker Desktop 自己的代理设置。

若使用的是独立 Linux Docker Engine,而不是 Docker Desktop,才按 Engine 文档在 daemon.json 或 systemd 环境中设置守护进程代理,并重启 Docker 服务。先确认自己运行的形态,避免改了一个根本不被读取的文件。

WSL curl 成功,docker pull 仍 timeout

检查 Docker Desktop 的 Proxies 设置和重启后的状态。

Docker Desktop 改 daemon.json 没效果

移到 Desktop 图形设置;该产品会忽略 daemon proxy 配置。

原生 Linux Engine 拉取失败

查看 daemon 的代理环境与服务日志,而不是 Windows 系统代理。

镜像能拉下来,构建步骤和运行容器仍可能没代理

Docker 的客户端配置可以为新容器和构建自动注入代理环境,也可以在单次命令里显式传入。构建时用 --build-arg,运行时用 --env;不要在 Dockerfile 里用 ENV 固化带凭据的代理地址,镜像历史和配置可能把它留下。

Docker Desktop 单次测试,7890 是待替换的 Clash 端口
CLASH_PROXY=http://host.docker.internal:7890

docker build \
  --build-arg HTTP_PROXY="$CLASH_PROXY" \
  --build-arg HTTPS_PROXY="$CLASH_PROXY" \
  -t demo-app .

docker run --rm \
  --env HTTP_PROXY="$CLASH_PROXY" \
  --env HTTPS_PROXY="$CLASH_PROXY" \
  --env NO_PROXY="localhost,127.0.0.1" \
  demo-app

Docker Desktop 通常提供 host.docker.internal 指向宿主机。原生 Linux Engine 是否可用取决于环境,需要用明确的宿主网关或额外 host-gateway 配置。命令执行前,用容器内的 curl 或应用日志验证这个主机名和端口确实可达。

NO_PROXY 写漏了,容器之间的请求也会绕远

数据库名、Compose 服务名、localhost 和内部网段通常不该交给外部代理。把实际内部域名加入 NO_PROXY,能避免应用访问 db:5432、redis:6379 时先去 Windows 兜一圈。不同工具对通配符和 CIDR 的支持不完全相同,配置后要在目标进程里打印变量并做实际请求。

代理地址若含用户名或密码,不要写进公开的 compose.yaml、Dockerfile 或镜像。使用本地环境文件或项目的 secret 管理方式,并确保日志不会把完整 URL 打出来。

按实际使用完成四次验证

  • WSL 中 curl 通过 Windows Clash 得到响应
  • docker pull 可以拉取一个小镜像
  • docker build 的网络步骤不再超时
  • 运行容器既能访问外网,也能直连内部服务

四项结果可以分别对应到 WSL、Docker 引擎、构建过程和运行容器。哪一项仍失败,就回到那一层的设置,不必重新改已经通过的其他三层。

参考资料