WSL Docker Desktop Proxy Deployment
当在 WSL2 中通过 Docker Desktop(Windows 端)部署容器化服务时, Docker daemon 往往无法直接拉取 ghcr.io/Docker Hub 镜像, 即使配置了国内镜像源也经常超时。
三步工作流
Step 1: 定位 Windows 宿主 IP
WINDOWS_HOST=$(ip route show default | awk '{print $3}')
# 验证代理可达
HTTP_CODE=$(curl -s --proxy http://$WINDOWS_HOST:7890 --connect-timeout 5 https://www.google.com -o /dev/null -w "%{http_code}")
echo "代理状态码: $HTTP_CODE"
🔴 CHECKPOINT: HTTP_CODE 必须为 200(非 200 则代理不可用,检查 Windows 端代理软件是否运行、端口 7890 是否正确)。确认代理可达后再继续。
Step 2: crane 代理拉取 → docker load
# 安装 crane(单二进制,无依赖)
curl -sL --proxy http://$WINDOWS_HOST:7890 \
https://github.com/google/go-containerregistry/releases/latest/download/go-containerregistry_Linux_x86_64.tar.gz \
-o /tmp/crane.tar.gz
tar xzf /tmp/crane.tar.gz -C /tmp/ crane && chmod +x /tmp/crane
# 拉取 + 加载(对每个需要的镜像重复)
export HTTP_PROXY=http://$WINDOWS_HOST:7890 HTTPS_PROXY=http://$WINDOWS_HOST:7890
/tmp/crane pull ghcr.io/owner/image:tag /tmp/out.tar
docker load -i /tmp/out.tar
# 验证镜像已加载
docker images | grep "ghcr.io/owner/image" | grep "tag"
🔴 CHECKPOINT: 确认 docker images 输出包含目标镜像且 tag 正确。若加载失败(exit code ≠ 0),检查 /tmp 磁盘空间(df -h /tmp)、crane 版本兼容性。所有镜像全部加载成功后再进入 Step 3。
Step 3: docker compose up
docker-compose 需两处修改:
A. 用预构建镜像,不编译源码:
services:
api:
image: ghcr.io/owner/image:latest # ✅
# build: apps/api # ❌ 注释掉
B. 容器内代理指向 Windows 宿主:
PROXY_SERVER=http://host.docker.internal:7890
host.docker.internal 在 Docker Desktop + extra_hosts: host-gateway 下自动解析到 Windows 宿主 IP。
WSL2 网络核心事实
| 事实 | 含义 |
|---|---|
| WSL2 和 Windows 是独立网络命名空间 | 127.0.0.1:7890 从 WSL 不通 Windows |
ip route show default 拿到的 IP |
就是 Windows 宿主网关地址 |
| Docker daemon 跑在 Windows 上 | WSL 的环境变量不影响 daemon |
| crane 走 WSL 的网络栈 | 不受 Docker daemon 网络限制 |
Docker Desktop × SYSTEM 账户限制
当 Hermes Agent 以 Windows Service(SYSTEM 账户)运行时,Docker Desktop 无法通过 Agent 自动启动。
| 症状 | 根因 | Agent 行为 |
|---|---|---|
docker ps → npipe not found |
Docker Desktop 未运行 | 通知用户手动启动(见下方脚本) |
Docker Desktop 进程存在但 docker 管道不存在 |
WSL2 Linux VM 未启动,WSL2 报 LOCAL_SYSTEM_NOT_SUPPORTED |
不要反复重试启动,直接降级 |
sc start com.docker.service 后 docker 仍不可用 |
Docker Desktop 的 Linux 引擎管道 (dockerDesktopLinuxEngine) 只在交互式用户会话中创建 |
同上——只能由用户手动启动 |
Agent 检查清单:
tasklist | grep -i docker→ 进程存在?是:等 30s 重试 docker ps;否:通知用户docker ps30s 后仍失败 → Docker Desktop 在 SYSTEM 下不可恢复- 立即通知用户 + 对依赖 Docker 的服务执行降级方案
- 不要在 SYSTEM 下反复重试
sc start或docker命令
Firecrawl 专用恢复:C:\Users\Aorus\tmp\firecrawl-selfhost\start-firecrawl.bat — 用户在桌面双击即可自动完成:Docker Desktop 启动 → 等待 VM 就绪 → docker compose up -d → 验证 :3002。
Docker Desktop 可用性检测
在 WSL 中执行任何 docker 命令前,务必先验证 Docker Desktop 是否在 Windows 端运行:
# 快速检测
docker ps 2>/dev/null || echo "Docker Desktop 未运行或 WSL 集成未启用"
🛑 STOP — 前置条件:docker ps 必须成功执行(exit code 0)。若失败,所有后续 docker 命令均不可用。必须先启动 Docker Desktop(Windows 端),等待 10-15 秒挂载点恢复后重新检测,通过后再执行任何 docker 操作。
症状:docker: command not found 或 /mnt/wsl/docker-desktop/cli-tools/...: No such file or directory
根因:WSL 中的 docker 是符号链接 → /mnt/wsl/docker-desktop/cli-tools/usr/bin/docker,该挂载点仅在 Docker Desktop 运行且启用 WSL 集成时才存在。Docker Desktop 关闭后,挂载点消失,docker CLI 不可用,所有容器同时退出(exit code 255)。
恢复:启动 Docker Desktop(Windows 端)→ docker start <容器名> 恢复容器。
失败模式速查表
| 触发条件 | 症状 | 一线修复 | 仍失败则 |
|---|---|---|---|
docker pull 超时/EOF(被墙 registry) |
dial tcp: i/o timeout / EOF |
换 crane 代理拉取 → docker load(按 Step 2) |
检查代理端口(默认 7890),确认 HTTP_PROXY 已 export |
docker ps 返回 command not found |
docker: command not found / 挂载点缺失 |
启动 Docker Desktop(Windows 端),等待 15 秒 | 检查 Docker Desktop Settings → Resources → WSL Integration → 当前发行版已勾选 |
curl --proxy 返回非 200 |
HTTP_CODE ≠ 200 | 确认 Windows 端代理软件(Clash/V2Ray)正在运行,端口 7890 监听 | 检查 Windows 防火墙是否拦截入站连接;尝试 ping $WINDOWS_HOST 验证 WSL→Windows 网络通 |
docker load 失败 |
no space left on device / invalid tar |
df -h /tmp 检查空间;重下 crane 最新版 |
换路径(如 /var/tmp)存储 tar 文件 |
docker build 第一步就 EOF/超时 |
EOF / context deadline exceeded |
先 docker pull <base-image> 绕过 build metadata 测试 mirror |
pull 也失败 → registry mirror 不可用,切 crane 路径预拉所有 base image 再 docker load 后 build |
| Docker Desktop 关闭后容器消失 | docker ps 无容器 |
启动 Docker Desktop → docker start <容器名> |
容器 exit code 255 表示被 Docker Desktop 关闭信号终止,docker start 即可恢复(数据在 volume 中不丢失) |
⛔ 反例与禁止
- ❌ 反复重试
docker pull— 超时不会自愈,换 crane(详见速查表) - ❌ 用
127.0.0.1当代理地址 — WSL2 不通 - ❌ 在 daemon.json 加 proxy 后不重启 Docker Desktop — 不生效
- ❌ 从源码
build:而不是用image:— 编译耗时且需要完整工具链 - ❌ compose 里有
build:的 Dockerfile 不预拉 base image —FROM postgres:17等也会走 daemon 拉取超时。用 crane 预拉所有 base image 后 docker load - ❌
docker build第一步 EOF/超时就放弃 — 先docker pull <base-image>测 mirror(详见速查表) - ❌ Docker Desktop 关闭后以为
docker start能恢复容器 —docker命令本身不可用(详见速查表)
参考资源
本地参考
references/firecrawl-selfhost-recipe.md
外部参考
清理
rm -f /tmp/crane.tar.gz /tmp/crane /tmp/out*.tar