开发与 AI · Clash 技术博客

VS Code Dev Containers 怎么走 Clash?

Dev Container 的构建、容器内 Git 和扩展下载不一定使用同一个代理。先找到每次失败发生在哪一层,再把宿主机地址和 NO_PROXY 传到那一层。

  • VS Code
  • Dev Containers
  • Docker
  • 代理
本文目录

先确定超时发生在 Docker、构建还是已经运行的容器

VS Code Dev Containers 走 Clash 不是只填一个代理地址。拉基础镜像、执行 Dockerfile、容器内运行 Git,以及远程扩展下载分别由不同进程发起。

在“Dev Containers”输出里找到失败命令所属的阶段,再把代理传给对应进程。否则 containerEnv 写得再完整,也修不了尚未创建的容器。

四个网络位置

失败动作实际发请求的组件配置入口
docker pull / 拉基础镜像Docker daemonDocker Desktop 或 daemon 代理
Dockerfile RUN apt/npm镜像构建阶段build args 或 BuildKit 配置
容器内 curl/Git/pip正在运行的容器containerEnv、remoteEnv 或工具配置
VS Code 远程扩展下载Dev Containers 远程服务容器环境与扩展自己的网络请求

错误出现在“Starting Dev Container”之前,通常不能靠 .devcontainer/devcontainer.json 里的 containerEnv 修复,因为容器尚未运行。先记下失败命令和 VS Code 输出面板中的阶段。

Dev Containers 的四段网络
  1. VS Code 宿主机负责拉取扩展与启动 Docker
  2. Docker 构建阶段读取 build args 或 Docker 代理设置
  3. 运行中的容器读取 containerEnv
  4. 远程扩展与终端读取 remoteEnv 或工具自己的配置

失败发生在哪一段,就只给那一段代理。不要把宿主机、构建阶段和容器运行时当成同一套环境变量。

容器里的 127.0.0.1 不是宿主机

Clash 运行在宿主机时,容器中的 http://127.0.0.1:7890 指向容器自己。Docker Desktop 在 Windows 与 macOS 提供 host.docker.internal;Linux Docker 可以通过 host-gateway 映射同名地址。

Clash 若只监听宿主机回环地址,容器仍可能连不到。开启允许局域网或把 mixed-port 监听到容器可达接口时,同时用系统防火墙限制来源;不应把 7890 直接暴露给公网。

先从容器验证端口,CLASH_PORT 按客户端实际 mixed-port 修改
CLASH_HOST=host.docker.internal
CLASH_PORT=7890
getent hosts "$CLASH_HOST" || true
curl -v -x "http://$CLASH_HOST:$CLASH_PORT" https://www.example.com/

# Linux Docker 临时测试
docker run --rm --add-host=host.docker.internal:host-gateway curlimages/curl:latest -v -x http://host.docker.internal:7890 https://www.example.com/

Linux 宿主机把 host-gateway 写进 devcontainer.json

.devcontainer/devcontainer.json 片段
{
  "runArgs": [
    "--add-host=host.docker.internal:host-gateway"
  ]
}

修改后执行 Dev Containers: Rebuild Container,旧容器不会自动获得新的 hosts 映射。

Docker Compose 项目则在服务下使用 extra_hosts: ["host.docker.internal:host-gateway"]。不要同时在两个地方重复添加。

端口可达后,再给运行中的容器设置代理

上一步只有在 curl 收到 HTTP 响应后才算通过。接下来把同一个地址交给容器内的终端和 VS Code 远程进程;如果 mixed-port 不是 7890,下面三处端口必须一起修改。

项目级 devcontainer.json 示例
{
  "containerEnv": {
    "HTTP_PROXY": "http://host.docker.internal:7890",
    "HTTPS_PROXY": "http://host.docker.internal:7890",
    "NO_PROXY": "localhost,127.0.0.1,::1,host.docker.internal"
  },
  "remoteEnv": {
    "HTTP_PROXY": "${containerEnv:HTTP_PROXY}",
    "HTTPS_PROXY": "${containerEnv:HTTPS_PROXY}",
    "NO_PROXY": "${containerEnv:NO_PROXY}"
  }
}

HTTPS_PROXY 使用 http:// 开头通常是正确的:它表示通过 HTTP CONNECT 代理访问 HTTPS,并不是把代理端口本身变成 HTTPS。NO_PROXY 还应加入 Compose 服务名、公司内网与本地开发域名,否则容器访问数据库或 API 时可能绕去 Clash。

保存后 Rebuild Container,再在新终端运行 env | grep -i proxy。已有终端进程不会自动刷新 remoteEnv。

镜像拉取与 Dockerfile 构建要单独配置

FROM 镜像拉取由 Docker daemon 发起,应在 Docker Desktop 的 Proxies 页面或 daemon 配置中处理。Dockerfile 的 RUN 命令发生在构建容器里,可以用 build.args 传入临时代理,但不要把个人端口或凭据写成 ENV 留在镜像层。

devcontainer 构建参数片段,7890 按实际 mixed-port 修改
{
  "build": {
    "dockerfile": "Dockerfile",
    "args": {
      "HTTP_PROXY": "http://host.docker.internal:7890",
      "HTTPS_PROXY": "http://host.docker.internal:7890",
      "NO_PROXY": "localhost,127.0.0.1"
    }
  }
}

curl 成功后,Git、apt 和扩展仍要各测一次

在容器终端确认 HTTP_PROXY、HTTPS_PROXY 和 NO_PROXY 已经出现,然后分别执行 Git 和包管理器命令。Git 通常会读取环境变量;若它仍指向旧地址,再检查 Git 自己的配置。

远程扩展由容器侧 VS Code Server 下载。修改 remoteEnv 后必须 Rebuild Container,再从输出面板观察 marketplace 请求。

容器内检查 Git;REPO_URL 可换成项目自己的公开仓库
REPO_URL='https://github.com/octocat/Hello-World.git'
env | grep -i '_proxy'
git config --show-origin --get-regexp 'http.*proxy' || true
git ls-remote "$REPO_URL"

# 只有 Git 不读取环境变量时才临时写入
git config --global http.proxy http://host.docker.internal:7890
git config --global --unset-all http.proxy

curl 成功,Git 仍连接旧端口

运行 git config --show-origin --get-regexp 'http.*proxy',清理用户或仓库残留。

apt update 失败

查看实际仓库域名、证书与 /etc/apt/apt.conf.d 中的独立代理。

项目能运行,远程扩展装不上

在 Dev Containers 日志中找 marketplace 请求,确认远程服务继承了 remoteEnv。

容器能联网,本地数据库连接失败

把数据库服务名和私有网段加入 NO_PROXY。

Rebuild 阶段仍失败

回到 daemon 拉镜像或 Dockerfile build args,不继续改运行容器。

共享仓库不要写死个人代理

团队项目可以在 devcontainer.json 使用 ${localEnv:HTTP_PROXY} 读取开发者本机环境,或提供不含真实地址的 .env.example。

README 里写清楚 HTTP_PROXY 应是完整地址,例如 http://host.docker.internal:7890。提交前检查代理 URL 中没有用户名、密码和 token。

验证完成后应能分别完成 docker pull、Rebuild Container、容器内 Git/包管理器和本地服务访问。哪一项失败,就保留该阶段的日志,不把四层网络再次混成“Dev Container 没网”。

参考资料