開発と AI · Clash 技術ブログ

VS Code Dev Containers で Clash を使用する方法

Dev Container のビルド、コンテナ内 Git、拡張機能の取得は異なるプロセスが行う場合があります。エラーの場所に応じてホストのプロキシ、ビルド変数、コンテナ環境を確認し、NO_PROXY で内部アクセスを維持して、取得と実行を別々に検証します。

  • VS Code
  • Dev Containers
  • Docker
  • プロキシ
目次

ホストのブラウザーで接続できても、開発コンテナーのイメージ取得、依存パッケージ導入、拡張機能のダウンロードは止まることがあります。コンテナーにプロキシを書いても直らない場合、リクエストを出すプログラムに設定が届いていない可能性があります。失敗したコマンドの実行場所とホストのプロキシへの接続を確認し、データベースなど内部サービスへの直接接続は維持します。

Dev Containers が止まるのは、イメージ取得、ビルド、コンテナー内のダウンロード?

Dev Containers の出力またはコンテナーのターミナルで、失敗したコマンドと実行場所を記録します。ベースイメージの取得、Dockerfile によるビルド、コンテナー内の Git、リモート拡張機能のダウンロードを区別してください。VS Code のこの機能は開発環境をコンテナーに置きますが、リクエストの実行元は異なります。下の表に沿って失敗箇所のプロキシを設定します。

ダウンロード操作ごとのプロキシ設定

失敗した操作実際にリクエストを送るコンポーネント設定箇所
docker pull / ベースイメージの取得Docker daemonDocker Desktop または daemon のプロキシ
Dockerfile RUN apt/npmイメージのビルド段階build args または BuildKit の設定
コンテナ内の curl/Git/pip実行中のコンテナcontainerEnv、remoteEnv、またはツールの設定
VS Code リモート拡張機能のダウンロードローカルの VS Code とコンテナー側のサービスローカルとコンテナーのネットワーク設定、および拡張機能自体の設定

エラーが「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 はコンテナ自身を指します。Windows と macOS の Docker Desktop では host.docker.internal を利用できます。Linux Docker では host-gateway を使い、同じ名前を割り当てられます。

Clash がホストのループバックアドレスだけを待ち受けている場合、コンテナから接続できないことがあります。ローカルネットワークからの接続を許可するか、mixed-port をコンテナから到達できるインターフェースで待ち受ける場合は、同時にシステムのファイアウォールで接続元を制限してください。7890 をインターネットへ直接公開してはいけません。

以下の前半は、起動済みコンテナーのターミナルで実行します。最後の docker run は Linux ホストのターミナル用で、一時的なテストコンテナーを作成します。開発コンテナーの中で繰り返し実行しないでください。二か所の 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"] を設定します。二か所へ重複して追加しないでください。

コンテナーからホストのポートにアクセスできたら、containerEnv と remoteEnv を設定する

コンテナー内の curl がプロキシ経由で HTTP 応答を受け取ってから、ツールの環境を設定します。次の例は Dockerfile または image の構成用で、ツールごとの差に合わせて大文字・小文字の変数を指定します。ポートは実際の mixed-port に合わせてください。Compose ではコンテナーの変数をサービスの environment に書き、devcontainer.json には remoteEnv だけを残します。

プロジェクト単位の 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",
    "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}",
    "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 のビルドは個別に設定する

イメージの取得は Docker Desktop またはビルダー/daemon 側で設定し、Dockerfile の RUN には build.args を使います。Linux では前述の runArgs のホスト設定は実行時のコンテナーだけに適用されるため、ビルドには下の build.options の設定も必要です。個人用プロキシや認証情報を 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",
      "http_proxy": "http://host.docker.internal:7890",
      "https_proxy": "http://host.docker.internal:7890",
      "no_proxy": "localhost,127.0.0.1"
    },
    "options": [
      "--add-host=host.docker.internal:host-gateway"
    ]
  }
}

curl の成功後も、Git、apt、拡張機能をそれぞれ一度テストする

コンテナのターミナルで HTTP_PROXY、HTTPS_PROXY、NO_PROXY が設定されていることを確認し、Git とパッケージマネージャーのコマンドを個別に実行します。通常 Git は環境変数を読み込みます。まだ以前のアドレスを参照する場合は、Git 固有の設定も確認してください。

拡張機能ビューや devcontainer.json から拡張機能をインストールする場合、ローカルのマシンとコンテナー側の VS Code Server の両方から Marketplace とダウンロード用 CDN にアクセスできる必要があります。remoteEnv を変更したら再接続して新しいターミナルを開き、出力パネルで実際に失敗したリクエストを確認してください。remoteEnv の変更だけならコンテナー全体を再ビルドする必要はありません。

コンテナ内で 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 -c http.proxy=http://host.docker.internal:7890 ls-remote "$REPO_URL"

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・パッケージ管理、ローカルサービスへの接続を個別に確認します。失敗したコマンドとログを保存し、この記事の対応する設定箇所を調べてください。すべてのプロキシ設定を一度に変える必要はありません。

参考資料