この記事で分かること
制限されたネットワークや不安定な海外回線で Docker Hub に接続すると、ブラウザは利用できるのに docker pull だけがタイムアウトする、認証は通るのにイメージのレイヤー取得で失敗する、といった問題が起こります。Docker Engine はブラウザのシステムプロキシ設定を必ずしもそのまま利用せず、Docker デーモン、コンテナ、ホスト OS の通信がそれぞれ異なる経路を通るためです。
本稿では、Clash の TUN モードを使って Docker Hub への通信を透過的に取り込み、Mihomo 系の YAML 設定で必要な DNS、ルール、仮想インターフェースを整理する方法を解説します。単に TUN をオンにするだけでなく、Docker Engine に明示的なプロキシを設定する構成との違い、docker pull が失敗したときにどの層を確認するか、社内レジストリや LAN 通信を誤ってプロキシへ送らないための除外設定まで、実際の切り分けに使える形でまとめます。
Docker を使った開発では、Docker Hub 本体だけでなく、認証用ホスト、マニフェスト配信、コンテナイメージの CDN、署名やトークンに関係するエンドポイントへ複数の HTTPS 接続が発生します。そのため docker.io だけをルールへ追加しても十分とは限りません。Clash のログと Docker の詳細出力を突き合わせ、実際に接続されたホストを基準に調整することが重要です。
TUN と Docker Engine の通信経路を理解する
TUN モードは、アプリケーションがプロキシ環境変数を読まない場合でも、OS のネットワーク層で通信を仮想インターフェースへ取り込み、Clash のルールに従って転送する仕組みです。ブラウザやターミナルだけでなく、独自の HTTP クライアントを持つバイナリ、GUI のバックグラウンドサービス、Docker Engine のようなデーモンにも適用しやすい点が特徴です。
ただし、Docker の構成では「ホストで動いている Docker CLI」と「バックグラウンドで動く Docker daemon」を分けて考える必要があります。CLI は /var/run/docker.sock や TCP ソケットを通じてデーモンへ命令を渡します。実際に Docker Hub へ HTTPS 接続してイメージを取得するのは、多くの場合デーモン側です。シェルで HTTPS_PROXY を設定しただけでは、デーモンの通信経路が変わらないことがあります。
一方、TUN がホストの外向き通信を正しく取り込めば、Docker daemon のプロセスが明示的なプロキシ設定を持たなくても、宛先 IP と DNS 解決の結果に応じて Clash へ流せます。この方法は設定箇所を減らせる反面、Docker のブリッジネットワーク、ホストの管理用通信、VPN、IPv6 が同時に存在すると経路が複雑になります。最初からすべてを TUN に任せるのではなく、動作確認後に例外を一つずつ戻す進め方が安全です。
基本的な経路は次のように考えると整理しやすくなります。
- Docker CLI:ユーザーが
docker pullやdocker loginを実行する操作面。 - Docker daemon:レジストリへ接続し、認証トークンやイメージレイヤーを取得する実体。
- Clash TUN:ホスト上の外向き通信を仮想インターフェースで受け、ルールとプロキシグループへ渡す層。
- Docker bridge:コンテナ内部の通信を担当する仮想ネットワーク。ホストの TUN とは別に DNS とルーティングを確認する必要があります。
curl、docker pull、Clash の接続ログを順番に確認してください。ブラウザが開くことだけでは Docker daemon の経路が正常だとは判断できません。
Mihomo YAML で TUN と DNS を準備する
Clash Verge Rev や Mihomo 系クライアントで TUN を有効にする場合、画面上のスイッチだけでなく、現在読み込まれているプロファイルの YAML が TUN を許可しているか確認します。クライアントによって項目名や UI は異なりますが、代表的には tun.enable、tun.stack、tun.auto-route、tun.auto-detect-interface などが関係します。バージョンによって利用できるフィールドが違うため、未知の項目を大量に追加するより、使用中の Mihomo の仕様に合わせて最小構成から始めてください。
概念的には、次のような設定を確認します。これはそのまま貼り付ける完成品ではなく、クライアントの YAML 形式と既存プロファイルに合わせて調整するための例です。
tun:
enable: true
stack: mixed
auto-route: true
auto-detect-interface: true
dns:
enable: true
enhanced-mode: fake-ip
nameserver:
- 1.1.1.1
- 8.8.8.8
stack: mixed は TCP と UDP の扱いを含めて互換性を取りやすい設定ですが、環境によっては system のほうが安定することもあります。Linux でカーネルや権限の制約がある場合、TUN デバイスを作成できず、スイッチをオンにしても実際には経路が変わっていないことがあります。管理者権限、ネットワーク拡張、ファイアウォールの許可を確認し、Clash のログに TUN インターフェース作成成功の記録があるか見てください。
DNS は特に重要です。Docker Hub のドメイン名がホスト側では正しく解決できても、コンテナや Docker daemon が別の DNS を使うと、名前解決だけが DIRECT 側へ漏れることがあります。fake-ip を利用する場合は、LAN 内のホスト名、社内ドメイン、Docker の内部名を除外しなければなりません。たとえば *.local、社内 DNS のドメイン、host.docker.internal に相当する環境依存の名前を無条件に外部 DNS へ送らないようにします。
また、IPv6 が有効な環境では A レコードだけでなく AAAA レコードが返され、IPv6 の経路だけが TUN を迂回することがあります。IPv4 では成功するのに取得が不安定な場合は、Clash の IPv6 設定、OS の優先順位、Docker daemon の IPv6 利用状況を比較してください。原因が分からないまま IPv6 を恒久的に無効化するより、短時間だけ無効にして症状が変わるかを確認するほうが切り分けとして有効です。
Docker Hub とレジストリ用のルールを設計する
ルールは「Docker に関係しそうなドメインをすべて PROXY にする」という発想ではなく、用途ごとに分けて考えます。公開レジストリの取得はプロキシへ送り、社内レジストリ、ローカル開発環境、プライベートなストレージは DIRECT に残す構成が扱いやすいです。実際のプロファイルではプロキシグループ名が異なるため、既存のグループに合わせて置き換えてください。
- Docker Hub 本体:
docker.io、registry-1.docker.io、index.docker.ioなど、ログに出たホストを確認します。 - 認証関連:
auth.docker.ioなど、ログインや短時間のトークン発行で現れるホストを確認します。 - イメージ配信 CDN:レイヤー取得時に Docker Hub 以外の CDN ホストへリダイレクトされる場合があります。固定名を推測せず、失敗時のログから追加します。
- プライベートレジストリ:会社や自宅 LAN のレジストリは、IP、ドメイン、ポートを明示して DIRECT または専用ポリシーへ送ります。
- コンテナ内部の通信:Docker bridge のサブネットやホストの管理用アドレスを、外部プロキシへ誤送信しないよう除外します。
ルールの順番も結果を左右します。広い GEOIP、IP-CIDR、MATCH を先に置くと、Docker Hub 用のドメインルールへ到達しません。より具体的な DOMAIN や DOMAIN-SUFFIX を上に配置し、最後の MATCH を既定のプロキシまたは DIRECT として扱います。ルールを変更したら、プロファイルを再読み込みし、既存の Docker プロセスが保持している接続を切るために再試行してください。
特に注意したいのは、ドメインルールと IP ルールの関係です。DNS が先に解決され、接続が IP アドレスだけで記録される場合、画面上で期待したドメインルールに見えないことがあります。fake-ip、redir-host、DNS のモードによってログの見え方は変わるため、「ルールが効かない」と決めつける前に、接続先、プロセス、使用ポート、最終的なポリシーを確認します。
実際の設定手順と動作確認
まず、Docker のバージョン、使用中のコンテキスト、レジストリの情報を記録します。複数の Docker Desktop、リモート daemon、rootless Docker を使い分けている場合、コマンドを実行した端末と実際に通信する daemon が一致しないことがあります。docker context show と docker info で接続先を確認し、検証中はできるだけローカルの単一 daemon に絞ります。
- 他の VPN やプロキシを一時停止する:二つの TUN、企業 VPN、Docker Desktop の内蔵ネットワーク機能が重なると、経路の説明が難しくなります。
- Clash のプロファイルをバックアップする:購読更新で手動編集が消える場合があるため、設定の保存場所と更新方法を確認します。
- TUN を有効にする:仮想インターフェースが作成され、auto-route と自動インターフェース検出が有効になったことをログで確認します。
- まずホストから検証する:
curl -I https://registry-1.docker.io/v2/などで名前解決、TLS、HTTP 応答を確認します。認証がない場合のステータスが返っても、到達性の判断材料になります。 - 小さな公開イメージを取得する:大容量イメージではなく、軽量なテスト用イメージで
docker pullを実行し、Clash のログに複数の接続が出ることを確認します。 - 認証とレイヤー取得を分けて試す:
docker loginが成功しても、レイヤー CDN が別経路で失敗することがあります。ログイン成功を最終判定にしないでください。 - 必要な場合だけ daemon のプロキシを追加する:TUN だけで安定しない場合は、Docker daemon の systemd drop-in、Docker Desktop の設定、または管理環境の正式なプロキシ設定を利用します。
Docker daemon に明示的な HTTP プロキシを設定する方法は、OS と Docker の導入形態で変わります。Linux の systemd 管理では、daemon 用の環境変数を drop-in ファイルへ設定して再起動する方法が一般的です。ただし、TUN と明示的プロキシを同時に使うと二重プロキシになる可能性があります。まずどちらか一方で成功させ、必要性が確認できた場合のみ併用してください。NO_PROXY には localhost、127.0.0.1、Docker ソケット関連の接続先、社内レジストリ、LAN サブネットなどを環境に合わせて登録します。
検証中は、Clash の接続ログで「プロセス名」「宛先ホスト」「選択されたルール」「プロキシグループ」「エラー内容」を見ます。Docker Desktop の場合、daemon が VM 内で動作しているため、ホスト上の TUN だけでは VM の内部経路を完全に説明できないことがあります。Desktop 側のネットワーク設定、ホストプロキシの取り込み、VM から見える DNS を個別に確認してください。
docker pull の対象を小さくし、Clash のログを保存してください。ルール追加のたびに同じ条件で試すと、改善したのか偶然成功したのかを判断しやすくなります。
失敗したときの切り分け
名前解決エラーなら、最初にホストと Docker daemon が同じ DNS を使っているか確認します。ホストの nslookup が成功しても、Docker 内部 DNS、Docker Desktop VM、rootless 環境では結果が異なることがあります。Clash の DNS ログ、OS の DNS 設定、Docker の resolver 設定を順番に比較し、名前が解決できた時点でどの IP が返されたかを記録してください。
TLS handshake timeoutや connection reset が出る場合は、ルールが PROXY でも、選択されたノードが Docker の大きなレイヤー転送や長時間接続に向いていない可能性があります。複数ノードで遅延だけを比較せず、実際にレイヤーを最後まで取得できるかを確認します。MTU、IPv6、企業の TLS 検査、プロキシの接続数制限も候補になります。
認証は成功するが pull が失敗する場合は、トークン取得先とレイヤー配信先が異なる典型例です。Clash のログを認証時とレイヤー取得時に分け、失敗した直前のホストを確認します。新しい CDN 名を見つけても、いきなり広いドメインサフィックスを PROXY にするのではなく、そのホストだけを追加して影響範囲を抑えます。
コンテナから外部 API へ接続できない場合は、Docker Hub の取得問題と混同しないようにします。イメージを取得する通信は daemon、コンテナの実行時通信はコンテナの名前空間から発生します。コンテナへプロキシ環境変数を渡していない、コンテナ DNS が到達できない、bridge のサブネットが Clash の除外に入っている、といった別の原因が考えられます。
TUN をオンにすると LAN が使えない場合は、auto-route と DNS ハイジャックがローカル通信へ影響している可能性があります。管理画面、社内 Git、NAS、プライベートレジストリのアドレスを DIRECT として明示し、Docker の bridge サブネットやルーターの管理 IP も除外します。変更後は Docker を再起動する前に、ホストから LAN の名前解決と TCP 接続を試してください。
よくある質問
TUN だけで Docker Hub は安定しますか?
ホスト上の Docker daemon の通信が TUN へ正しく取り込まれる環境なら、明示的な Docker プロキシなしで安定することがあります。ただし Docker Desktop の VM、rootless Docker、企業 VPN、IPv6、独自 DNS がある場合は、TUN だけでは経路が揃わないことがあります。最初は TUN 単独で確認し、ログに通信が現れない場合だけ daemon 側のプロキシ設定を検討してください。
Docker Desktop でも同じ YAML 設定を使えますか?
Clash 側のルールや DNS の考え方は共通しますが、Docker Desktop の daemon はホスト OS の通常プロセスと同じ場所で動いているとは限りません。ホストの TUN、Desktop のリソース設定、VM 内 DNS、Desktop が提供するプロキシ機能を別々に確認する必要があります。ホストの curl が成功しただけで、Desktop 内の docker pull も成功すると判断しないでください。
NO_PROXY には何を入れるべきですか?
localhost、ループバックアドレス、Docker ソケットの接続先、社内レジストリ、LAN 内の管理用ドメインなど、プロキシを経由させたくない宛先を登録します。会社のネットワークや Docker のサブネットは環境ごとに異なるため、例をそのままコピーせず、docker network inspect や管理者の資料で実際のアドレスを確認してください。
Docker Hub のドメインを全部 PROXY にしても問題ありませんか?
短時間の検証では有効ですが、恒久設定として広いサフィックスを使うと、関係のないサービスや社内ドメインまでプロキシへ送る可能性があります。Clash のログで実際に利用されたホストを確認し、Docker Hub の認証、マニフェスト、レイヤー配信に必要な範囲だけをルールへ追加するほうが、速度と安全性のバランスを取りやすくなります。
ブラウザ設定や環境変数だけに依存する方法は手軽ですが、Docker daemon のように別プロセスで動く通信が設定から漏れたり、Docker Desktop の VM とホストの経路が分かれたりしやすいという弱点があります。専用の固定プロキシだけを使う構成も、ノード変更や DNS 問題のたびに Docker 側を調整しなければなりません。その点、Clash の TUN とルール分流を組み合わせれば、Docker Hub の取得、認証、CDN をログで追跡しながら、社内レジストリや LAN は DIRECT に保てます。Docker を含む複数の開発ツールを同じポリシーで安定させたい場合は、利用環境に合う Clash クライアントを選び、設定を確認してみてください。