Docker Hub だけが遅い・失敗する理由

Docker Hub のイメージ取得で「Pull が終わらない」「数分待った後に i/o timeout が出る」「context deadline exceededconnection reset by peer が表示される」といった問題は、Clash のノードが単純に遅い場合だけに起きるものではありません。ブラウザで Docker Hub の Web ページを開けても、Docker daemon が同じ経路を使っているとは限らないためです。ブラウザは OS のシステムプロキシや Clash の TUN を利用していても、Docker Engine は独立したサービスとして動作し、別の DNS、別のプロキシ設定、別の権限環境を使うことがあります。

さらに、Docker のイメージ取得は一つのホストだけで完結しません。最初に registry-1.docker.io へ認証やマニフェストを問い合わせ、その後に認証トークンの発行先、イメージレイヤーを配信する CDN、場合によっては署名やメタデータ関連のホストへ接続します。そのため、Docker Hub のトップページだけを確認して「Clash は正常」と判断すると、実際の Pull でだけ失敗することがあります。本稿では、Clash の接続ログ、Docker 側のプロキシ、ルールの優先順位、DNS、ノード品質を順番に確認します。

前提:Docker Hub の利用規約、勤務先や学校のネットワークポリシー、取得するコンテナイメージのライセンスを確認したうえで設定してください。プロキシ設定は通信経路を変えるだけであり、信頼できないイメージを安全にするものではありません。

最初に症状を分類する

設定ファイルをいきなり書き換える前に、どの段階で止まっているかを記録します。同じタイムアウトでも、認証に失敗しているのか、レイヤーのダウンロードが遅いのかで対処が変わります。次のコマンドを実行した時刻と、Clash の接続一覧に表示されたホスト名・ポリシー名を控えておくと、原因を追いやすくなります。

  • 名前解決で失敗する:no such hostserver misbehaving、または DNS 関連のエラーが出る場合です。Docker の DNS と Clash の DNS が分離している可能性があります。
  • 認証段階で止まる:ログイン済みでも unauthorized や認証トークン取得のタイムアウトが出る場合です。レジストリ本体だけでなく認証用ホストも確認します。
  • レイヤー取得だけ遅い:マニフェストは取得できるのに、数百 MB のレイヤーで止まるパターンです。CDN への接続、ノードの帯域、接続数制限が関係します。
  • Clash のログに何も出ない:Docker daemon が Clash のポートを使っていない、または TUN がその通信を捕捉していない可能性が高いです。

まず公開イメージの小さなタグで再現します。巨大なイメージだけを使うと、ネットワーク障害と単純な転送時間を区別しにくくなります。

docker pull hello-world:latest
docker pull alpine:latest
docker info

失敗した直後に Clash の「Connections」や「Logs」を開き、docker.iodocker.comregistry-1.docker.io などの行を検索します。表示された実ホストが想定したプロキシグループを通っているか、ルール欄が DIRECT や広すぎる GEOIP になっていないかを確認してください。

Clash 側の動作を先に確認する

Clash Verge Rev、Clash Verge、Mihomo Party などを使っている場合は、最初に現在のプロファイルが読み込まれ、コアが稼働していることを確認します。システムプロキシをオンにしただけでは、Docker daemon の通信まで自動的にプロキシ化されるとは限りません。GUI の「General」や「Settings」で、Mixed Port または HTTP/SOCKS の待受ポートを確認してください。典型的には 789078979090 などですが、固定値だと決めつけず、実際の画面に表示された値を使います。

同じ PC 上の通常のターミナルから、Clash の混合ポート経由でレジストリへ到達できるかを確認します。以下の 127.0.0.1:7890 は、実際のポート番号に置き換えてください。

curl -I -x http://127.0.0.1:7890 https://registry-1.docker.io/v2/
curl -I -x http://127.0.0.1:7890 https://auth.docker.io/

401 Unauthorized が返っても、必ずしも失敗ではありません。Docker Registry が認証を要求していることを示すレスポンスであり、TLS 接続と HTTP 応答まで到達した証拠になります。一方、接続が長時間戻らない、名前解決エラーになる、Clash のログに該当行が現れない場合は、Clash のポート、プロファイル、ローカルファイアウォールを先に直します。

確認のコツ:ブラウザで Docker Hub を開くテストと、curl でレジストリ API を呼ぶテストは分けてください。前者が成功しても、後者のホストが別ルールで処理されていることがあります。

Docker daemon にプロキシを設定する

Docker CLI の環境変数と Docker daemon のプロキシ設定は別物です。ターミナルで HTTP_PROXY を設定しても、バックグラウンドでイメージを取得する daemon がその値を継承するとは限りません。特に Linux の systemd 管理下では、daemon 用の環境変数を明示的に登録する必要があります。プロキシを公開ネットワークへバインドせず、原則としてローカルホストからだけ利用してください。

  1. Clash の待受アドレスとポートを確認:127.0.0.1 で受け付ける HTTP 混合ポートを確認します。Docker が別の仮想マシンや WSL2 内で動く場合、ゲスト側の 127.0.0.1 はホスト側を指さないことがあるため、構成を先に整理します。
  2. daemon 用の設定を作成:Linux では /etc/systemd/system/docker.service.d/http-proxy.confHTTP_PROXYHTTPS_PROXY を登録します。URL のスキームは Clash のポート種別に合わせてください。
  3. 除外先を指定:NO_PROXYlocalhost127.0.0.1、Docker の内部ネットワーク、社内レジストリなどを登録します。内部サービスまで外部ノードへ送ると、認証や名前解決が壊れることがあります。
  4. 設定を反映して状態を確認:systemctl daemon-reloadsystemctl restart docker を実行し、再起動後に systemctl show --property=Environment docker で値が反映されているか確認します。
  5. 小さなイメージで再試行:docker pull hello-world を実行し、同時に Clash の接続ログで Docker 関連の通信を確認します。
[Service]
Environment="HTTP_PROXY=http://127.0.0.1:7890"
Environment="HTTPS_PROXY=http://127.0.0.1:7890"
Environment="NO_PROXY=localhost,127.0.0.1,.local,registry.local"

Docker Desktop では、Linux の systemd ファイルを編集するのではなく、アプリの設定画面にある Docker Engine や Resources、Proxies の項目を使います。バージョンによって表示名や設定方法が異なるため、GUI にプロキシ項目がある場合はそちらを優先してください。設定を変更した後は Docker Desktop 自体を再起動し、daemon が新しい環境で起動したことを確認します。

Docker Hub 向けの分流ルールを整える

Clash のルールは上から評価されるため、Docker Hub 用のルールを広い GEOIPMATCH、広告ブロック、地域別ルールより前に置く必要があります。まずはドメイン単位で対象をまとめ、動作確認後に必要な範囲へ絞ります。ホスト名はサービス変更で増える可能性があるため、ログに実際に現れたドメインを基準に更新してください。

用途 確認するホスト例 初期設定の考え方
Registry API registry-1.docker.io 安定した Docker 用プロキシグループへ送る
認証 auth.docker.io Registry と同じグループで挙動を揃える
Docker Hub Web hub.docker.com ブラウザ閲覧が必要な場合だけ追加する
レイヤー CDN 接続ログに表示された配信ホスト 実測したホストを個別に確認する
rules:
  - DOMAIN,registry-1.docker.io,DOCKER_HUB
  - DOMAIN,auth.docker.io,DOCKER_HUB
  - DOMAIN,hub.docker.com,DOCKER_HUB
  - MATCH,FINAL

上の例で使っている DOCKER_HUB は、実際に設定ファイル内で定義したプロキシグループ名へ変更します。DOMAIN-SUFFIX,docker.io のように広く指定すると便利な反面、不要な通信まで同じノードへ送ることがあります。まずは registry-1.docker.ioauth.docker.io を明示し、Pull 中に現れた CDN を追加する方法が安全です。

DNS、TUN、Docker Desktop の境界を確認する

Docker が WSL2、仮想マシン、リモート Linux、Docker Desktop の内部 VM で動いている場合、Clash と Docker は同じネットワーク名前空間にいません。ホスト OS のブラウザが TUN を使えていても、ゲスト側の Docker daemon がホストの 127.0.0.1 に接続できるとは限りません。まず Docker を実行している環境内で getent hosts registry-1.docker.iocurl を使い、名前解決と TCP 接続を別々に確認します。

TUN を使う場合は、Clash が DNS を処理し、Docker の通信を仮想インターフェース経由で捕捉できているかを確認します。TUN の導入直後にすべての通信を強制すると、Docker の内部ブリッジ、社内レジストリ、ホストとの管理通信まで巻き込むことがあります。最初は Docker Hub への Pull だけで試し、正常になった後に必要な範囲へ拡大してください。

注意:Docker daemon のプロキシと TUN を同時に有効にすると、同じ接続が二重にプロキシ化される構成があります。タイムアウトが悪化した場合は、いったんどちらか一方だけを使い、Clash の接続ログで経路を比較してください。

ノードとタイムアウトを検証する

Clash のルールが正しくても、選択したノードが Docker のレイヤー配信に向いていないことがあります。小さな API リクエストは通るのに、大きなレイヤーを数十秒転送すると切れる場合、帯域よりも接続維持、混雑、CDN との相性、出口 IP の評価が問題になります。自動選択グループを使っている場合は、同じ Pull の途中でノードが切り替わっていないかも見てください。長いダウンロード中の切り替えは、再試行や接続リセットの原因になります。

検証では、まず固定ノードを一つ選び、同じイメージを数回取得します。次に別の地域や別のプロバイダーのノードへ変更し、成功率、名前解決時間、最初のバイトまでの時間、レイヤー転送速度を比較します。docker pull の表示だけでは判断しにくいため、Clash の接続履歴と Docker daemon のログを同じ時刻で照合するのが有効です。

  • 固定ノードで安定する:url-test や load-balance の選択結果、または特定ノードの品質を疑います。
  • すべてのノードで同じホストだけ失敗する:ルール、DNS、認証先、CDN の扱いを確認します。
  • ホスト側は成功し、WSL2 や VM だけ失敗する:ゲストから Clash へ到達するアドレスとファイアウォールを確認します。
  • 夜間だけ遅い:共有ノードの混雑や CDN との経路混雑が考えられます。別ノードで時間帯を変えて比較します。

よくある症状と対処

症状 可能性の高い原因 最初に行うこと
Clash にログがない daemon がプロキシを使っていない Docker daemon 用の環境変数または Docker Desktop の設定を確認
401 が返る Registry へ到達済みで認証待ち 認証用ホストと Docker のログイン状態を確認
レイヤーだけ失敗する CDN、ノード混雑、長時間接続の切断 接続ログから配信ホストを確認し、固定ノードで再試行
WSL2 だけ直行する ホストの localhost とゲストの localhost が別 Clash の LAN 接続設定とゲスト側の接続先を確認
設定後も古い挙動が続く daemon が再起動されていない Docker daemon または Docker Desktop を再起動して環境を再確認

エラーメッセージだけで「Docker Hub が落ちている」と決めつけないことも重要です。公開レジストリ側の障害であれば、複数の回線やノードで同時に再現し、ステータスページやコミュニティにも同様の報告が出ます。自分の環境だけで発生しているなら、Clash のルール、daemon のプロキシ継承、DNS 境界を優先して調べる方が短時間で解決できます。

よくある質問

ブラウザで Docker Hub が開けば、Docker も同じように使えますか?

必ずしも使えるとは限りません。ブラウザはシステムプロキシを利用していても、Docker daemon は独自のサービスとして動いています。Docker 側に HTTP/HTTPS プロキシを設定し、再起動後に Clash の接続ログへ通信が現れることを確認してください。

Docker Hub をすべて DIRECT にしてもよいですか?

接続できるネットワークでは可能ですが、地域や ISP によっては Registry、認証、CDN のいずれかが不安定になることがあります。まずは対象ホストを専用グループへ送り、固定ノードと DIRECT の成功率を比較してください。社内レジストリやプライベートなエンドポイントは、必要に応じて NO_PROXYDIRECT を使い分けます。

TUN モードを有効にすれば daemon の設定は不要ですか?

環境によります。ホスト上の Docker が TUN で確実に捕捉される構成なら不要な場合もありますが、WSL2、仮想マシン、リモート Docker daemon では TUN の範囲外になることがあります。TUN を前提にせず、Docker 側のログと Clash の接続履歴で実際の経路を確認するのが安全です。

市販の同類ツールや単純なシステムプロキシ設定は、ブラウザの表示確認までは簡単でも、Docker daemon、WSL2、認証ホスト、レイヤー CDN まで分けて追跡する場面では設定箇所が散らばり、ログも見つけにくいことがあります。Clash 公式サイト なら、Clash Verge Rev や Mihomo 系クライアントで混合ポート、TUN、接続ログ、ドメイン単位の分流を順に確認でき、今回のような「ブラウザは通るのに Docker だけ失敗する」切り分けを一つの手順にまとめられます。Docker Hub のプロキシ設定をこれから試すなら、環境に合うクライアントをダウンロードして、まず小さなイメージで疎通を確認してみてください。