ブラウザは使えるのに docker pull だけ失敗する理由
ブラウザでウェブサイトを開けるのに、Docker Hub からイメージを取得できない場合、最初に確認すべきなのは Clash のノードではなく、通信を開始しているプロセスです。ブラウザは通常、OS のシステムプロキシ設定を参照します。一方、docker pull は多くの Linux 環境で、ユーザーが実行する Docker CLI から直接インターネットへ接続しているように見えても、実際の HTTP リクエストを送信しているのはバックグラウンドの Docker デーモン(dockerd)です。
この違いにより、Clash のシステムプロキシを有効にしていても、Docker デーモンがその設定を知らなければ通信はプロキシを経由しません。ブラウザだけが正常に動作し、docker pull nginx や docker login がタイムアウトする場合は、まず「どのプロセスが接続しているか」「そのプロセスのネットワーク名前空間とルーティングはどうなっているか」を分けて考える必要があります。
Docker Hub へのアクセスは、単一のホストだけで完結しないことにも注意してください。最初にレジストリへ問い合わせた後、認証サービスからトークンを取得し、実際のイメージマニフェストやレイヤーを別のエンドポイントからダウンロードすることがあります。そのため、registry-1.docker.io だけを許可しても、認証やレイヤー取得の段階で停止する場合があります。
Docker の通信経路と Clash の担当範囲を整理する
Docker をホスト上で利用する場合、通信経路はおおむね「Docker CLI → Docker デーモン → ホストのネットワークスタック → Clash → プロキシノード → Docker Hub」という順番になります。CLI にプロキシ環境変数を設定しただけでは、すでに起動しているデーモンの環境変数は変わりません。反対に、デーモンへプロキシを設定しても、実行中のコンテナから外部へ出る通信が同じ経路を通るとは限りません。
ここでは、次の3種類の通信を分けて確認します。
- イメージ取得:
docker pull、docker push、docker loginに関係する Docker デーモン自身の通信です。 - ビルド中の通信:
docker buildのRUN apt updateやパッケージ取得など、ビルドコンテナ内部から発生する通信です。 - 稼働中コンテナの通信:アプリケーションコンテナが API や外部サービスへ接続する通信です。Docker デーモンのプロキシ設定だけでは、この通信は自動的に設定されません。
この3つを混同すると、Docker デーモンに設定を追加したのにコンテナ内の curl が失敗する、または TUN をオンにしたのに docker pull が変わらない、といった判断ミスが起こります。目的がイメージ取得だけならデーモンのプロキシ設定を優先し、コンテナを含むホスト全体の通信を扱うなら TUN や透過プロキシの設計まで確認します。
Docker Desktop と Linux 上の Docker Engine では内部構造が異なります。Docker Desktop は専用の仮想環境内でデーモンを動かすため、ホスト側の Clash 設定がそのまま内部デーモンへ伝わるとは限りません。まず使用環境が native Linux、Docker Desktop、仮想マシンのどれなのかを確認してください。
まず Docker デーモンへ明示的にプロキシを設定する
Docker Hub からのイメージ取得だけを安定させたい場合、最も再現性が高い方法は Docker デーモンに HTTP プロキシを明示することです。Clash の混合ポートがローカルで 7890 だとすると、Docker Engine を動かしているホスト上でプロキシの待受アドレスとポートを確認します。Clash の「LAN 接続を許可」が無効な場合、別のマシンや仮想マシンから 192.168.x.x:7890 へ接続することはできません。
systemd 管理の Linux では、Docker サービス用の drop-in ファイルを作成する方法が分かりやすく、設定の適用範囲も明確です。
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo tee /etc/systemd/system/docker.service.d/proxy.conf > /dev/null <<'EOF'
[Service]
Environment="HTTP_PROXY=http://127.0.0.1:7890"
Environment="HTTPS_PROXY=http://127.0.0.1:7890"
Environment="NO_PROXY=127.0.0.1,localhost,::1"
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
sudo systemctl show --property=Environment docker
Clash が HTTPS プロキシポートではなく混合ポートを提供している場合でも、Docker デーモン側の URL は通常 http:// で指定します。これは「HTTPS の宛先へ接続するか」と「ローカルプロキシへ接続する方式」が別だからです。設定後は、現在のシェルで export HTTPS_PROXY=... を実行するだけでは不十分です。systemd サービスを再起動し、デーモンの環境に値が入っていることを確認してください。
環境によっては /etc/docker/daemon.json の proxies セクションを利用できますが、既存の JSON を壊さないように注意が必要です。すでに registry-mirrors やログ設定がある場合、単純にファイル全体を上書きしてはいけません。また、利用中の Docker Engine のバージョンやディストリビューションによって対応範囲が異なるため、systemd の drop-in が利用できる環境では、まずそちらを採用すると切り分けやすくなります。
TUN と透過プロキシを組み合わせるときの考え方
TUN モードはホストのルーティングを仮想インターフェースへ向け、アプリケーションがシステムプロキシを理解していなくても IP トラフィックを Clash へ渡す方式です。しかし、TUN を有効にしただけで Docker のすべての通信が必ず捕捉されるとは限りません。Docker はブリッジネットワーク、NAT、転送チェーンを使用するため、コンテナから出たパケットがどのインターフェースを通り、Clash の TUN 自動ルートに含まれるかを確認する必要があります。
特に Linux では、ホスト自身の通信とコンテナブリッジから転送される通信が異なる経路になることがあります。ホスト上のブラウザが TUN 経由で正常に動作していても、docker0 から出るパケットが除外されていれば、コンテナだけが直接接続を試みます。逆に、Docker ブリッジを無条件に TUN へ送り込むと、Clash 自身の接続や Docker の DNS、ホスト内の管理通信までループする可能性があります。
mihomo 系の設定では、TUN の auto-route、auto-detect-interface、DNS の処理方式、IPv6 の扱いを一つずつ確認します。透過プロキシを使う構成では、TCP のリダイレクトに redir、TCP と UDP の転送を意識する構成に tproxy が使われることがあります。ただし、クライアントの GUI がこれらをどのように実装しているかは製品ごとに異なるため、設定項目の名称だけで判断せず、実際のログとルーティングテーブルを確認してください。
TUN、別の VPN、iptables のリダイレクト、他の透過プロキシを同時に有効化すると、同じパケットが複数回変換されることがあります。最初は Docker デーモンへの明示的なプロキシ設定だけでテストし、その後に TUN を追加してください。複数の仕組みを一度に変更すると、原因の特定が困難になります。
Docker Hub 向けルールと NO_PROXY を設計する
ルールモードでは、Docker Hub 関連のドメインが意図したプロキシグループへ送られているかを確認します。サブスクリプションのルールが先に GEOIP,CN,DIRECT のような広い条件を置いている場合、ドメインルールより前に IP 条件へ一致し、Docker Hub の接続が直接通信になることがあります。Clash のルールは上から順番に評価され、最初に一致した行で処理が止まるため、広すぎるルールを上部に置かないことが重要です。
最低限の確認対象として、レジストリ、認証、コンテナイメージの配信先をログで確認します。環境によって接続先は変わるため、次の名前を無条件に固定するのではなく、Clash の接続ログに実際に現れたホスト名をルールへ反映してください。
DOMAIN-SUFFIX,docker.io,Proxy
DOMAIN-SUFFIX,docker.com,Proxy
DOMAIN-SUFFIX,auth.docker.io,Proxy
DOMAIN-SUFFIX,registry-1.docker.io,Proxy
MATCH,DIRECT
上の例ではグループ名 Proxy が設定内に存在している必要があります。実際の設定でグループ名が「Auto」や「自動選択」になっている場合は、その名前へ置き換えます。Docker Hub だけをプロキシへ送り、その他を直接接続したい場合には、このように対象を限定できます。一方、配信 CDN のホスト名が別ドメインとしてログに表示される場合は、そのドメインを追加しなければなりません。
NO_PROXY はプロキシを使わない宛先の一覧です。localhost、127.0.0.1、::1、社内レジストリのホスト名、Docker の内部ネットワークを必要に応じて登録します。ただし、NO_PROXY=* を設定するとすべての接続が直接通信になり、原因に気づきにくくなります。Docker Hub をプロキシ経由にしたい場合、docker.io や認証ホストを誤って NO_PROXY に含めないようにしてください。
ログとコマンドで通信経路を段階的に切り分ける
設定を変更した後は、いきなり複雑なイメージの取得を試すのではなく、名前解決、プロキシポート、デーモン、レジストリの順番で確認します。まず Clash のログ画面を開き、テスト中に docker.io、auth.docker.io、registry-1.docker.io などの接続が記録されるかを見ます。ログに何も出ない場合は、Docker の通信が Clash へ到達していません。
getent hosts registry-1.docker.io
curl -I -x http://127.0.0.1:7890 https://registry-1.docker.io/v2/
docker info
docker pull hello-world
curl の結果が 401 Unauthorized になっても、必ずしも失敗ではありません。Docker Registry の /v2/ エンドポイントは認証トークンを要求するため、プロキシ経由でサーバーまで到達できていることを示す場合があります。重要なのは、接続拒否、名前解決失敗、TLS ハンドシェイクのタイムアウトなどが出ていないかです。
次に Docker サービスのログを確認します。
sudo journalctl -u docker --since "10 minutes ago" --no-pager
sudo systemctl show --property=Environment docker
ip route
ip rule
ログにプロキシ接続エラーがある場合は、Clash の待受アドレス、ポート、プロキシ URL のスキームを確認します。Docker デーモンが再起動されていない場合は古い環境変数のまま動作します。TUN を使う場合は、ip route と ip rule でデフォルトルートやポリシールーティングを確認し、Docker ブリッジの経路が意図せず除外されていないかを調べます。
- Clash ログに接続がない:デーモンのプロキシ設定、TUN のルート、Docker のネットワーク名前空間を確認します。
- Clash に接続はあるがタイムアウトする:選択中のノード、DNS、TLS、Docker Hub 側の認証エンドポイントを確認します。
- 認証だけ失敗する:
auth.docker.ioが異なるルールで直接接続になっていないか、時刻ずれやログイン情報に問題がないかを調べます。 - pull は成功するが build が失敗する:ビルド時の HTTP/HTTPS プロキシを別途設定します。デーモンの設定だけでは
RUN内の通信は変わりません。
安定運用のために設定を分離し、変更を一つずつ検証する
日常的に Docker を使う環境では、最終的に「Docker デーモンの明示的なプロキシ」と「ホスト全体を扱う TUN」を同じ目的で重ねないことが大切です。イメージ取得だけが目的なら、systemd のプロキシ設定を基本経路にすると構成が小さくなります。ホスト上の複数アプリや、システムプロキシを認識しない通信まで統一したい場合は TUN を追加しますが、Docker ブリッジ、DNS、IPv6、ループ防止の設計を確認してから有効化します。
設定を変更するときは、次の順番で進めると安全です。
- Clash で動作確認済みのノードを選択し、ホストのブラウザまたは
curlでプロキシ接続を確認します。 - Docker デーモンへプロキシを設定し、サービスを再起動して
docker pull hello-worldを実行します。 - Clash のログで Docker Hub の各ホストが想定したルールへ一致していることを確認します。
- コンテナ内の通信も必要な場合は、ビルド引数、コンテナ環境変数、Docker ネットワークの経路を別に設定します。
- 最後に TUN や透過プロキシを有効化し、ホストとコンテナの両方で同じテストを繰り返します。
この順番なら、Docker Hub の障害、ノードの問題、Docker デーモンの環境変数、Clash のルール、TUN のルーティングを個別に判定できます。「ブラウザが動くから Docker も動くはず」と考えるのではなく、実際の接続元プロセスとログに記録された経路を基準に判断してください。
Clash をダウンロード