先分清資料流:Docker pull 不一定會走系統代理
遇到 docker pull 長時間停在 Pulling from、TLS handshake timeout 或 context deadline exceeded 時,先不要急著修改 Clash 規則。最常見的根本原因是:瀏覽器已經透過 Clash 成功開啟 Docker Hub,但真正執行拉取工作的 Docker daemon 並沒有使用同一個代理。
在一般 Linux 主機上,docker 命令列只是 Docker API 的客戶端,負責把「拉取某個映像」的請求交給背景執行的 dockerd。之後由 daemon 解析映像名稱、向 Registry 取得認證、下載 manifest 和各層 blob,再寫入本機儲存區。這些網路請求通常由 daemon 行程發出,不會自動讀取桌面環境裡 Clash 的系統代理設定,也不一定會讀取你目前 Shell 中的 HTTP_PROXY 或 HTTPS_PROXY。
因此要先區分三條不同的流量路徑:
- 主機上的一般應用程式:可能遵守系統代理,連到 Clash 的 HTTP 或 SOCKS 監聽埠。
- Docker daemon:由 systemd 或其他服務管理器啟動,需要單獨設定代理環境變數,或交給透明代理接管。
- 容器內的應用程式:容器啟動後的流量來自獨立網路命名空間,是否走代理取決於容器環境變數、Docker 網路、主機轉發規則或 TUN/TPROXY 設定。
docker pull 超時通常屬於第二條路徑,而容器啟動後無法存取外部服務則屬於第三條路徑。兩者都可以使用 Clash,但配置位置和驗證方法完全不同。先確定問題發生在「拉映像」還是「容器執行期間的連線」,能避免把大量時間浪費在錯誤的 YAML 欄位上。
先用直連測試建立排障基準
在配置透明代理前,先確認 Docker daemon 的基本狀態、主機 DNS 和 Docker Hub 的直連結果。這一步的目的不是要求直連一定成功,而是把「Docker 本身故障」與「網路出口受限」分開。
- 查看 daemon 是否正常執行,並檢查最近的錯誤記錄。
- 確認映像名稱格式正確,例如
docker.io/library/alpine:latest。 - 在主機上解析 Docker Registry 網域,再測試 HTTPS 連線。
- 用 Docker 自己執行一次拉取,記錄完整錯誤訊息和等待時間。
sudo systemctl is-active docker
sudo journalctl -u docker --since "15 minutes ago" --no-pager
getent hosts registry-1.docker.io
curl -I --connect-timeout 10 https://registry-1.docker.io/v2/
docker pull docker.io/library/alpine:latest
對 /v2/ 發出的未認證請求,正常情況下常會得到 401 Unauthorized 或包含認證提示的回應。這不代表 Registry 掛掉,反而表示 DNS、TCP 和 TLS 基本上已經走通。若是直接出現名稱解析失敗、連線逾時或 TLS 交握逾時,才需要把焦點放到 DNS、出口網路或代理路徑。
同時檢查主機時間是否準確。Docker Hub 使用 HTTPS,系統時間偏差過大可能導致憑證驗證失敗。若錯誤是 no such host,優先查 DNS;若是 i/o timeout 或 context deadline exceeded,則應檢查防火牆、路由和代理設定;若是 x509: certificate signed by unknown authority,則不要直接關閉驗證,應先確認是否有企業 TLS 檢查設備或錯誤的自訂 CA。
最穩定的方案:單獨為 Docker daemon 設定代理
對只需要解決 docker pull 和映像建置下載的主機來說,優先建議使用 Docker daemon 的原生代理設定。這不是透明代理,但路徑清楚、影響範圍可控,也不會把所有容器流量意外送進 Clash。Clash 只需要提供一個可被主機存取的 HTTP 代理或混合代理埠。
如果 Clash 和 Docker daemon 位於同一台主機,可以使用本機位址與對應的 HTTP 代理埠。許多 Clash 設定會使用 7890 作為混合代理埠,但實際埠號必須以目前設定為準,不要直接照抄預設值。先在主機上測試代理是否能正常轉發:
curl -I \
-x http://127.0.0.1:7890 \
--connect-timeout 10 \
https://registry-1.docker.io/v2/
如果 Clash 執行在另一台主機,不能在 Docker daemon 的代理設定裡填 127.0.0.1,因為那代表 Docker 主機自身。此時應填寫 Clash 所在主機的區域網路 IP,並在 Clash 設定裡開啟允許區域網路連線,同時確認防火牆只允許可信任的內網來源存取代理埠。
使用 systemd 管理 Docker 的 Linux 主機,可建立 drop-in 設定目錄與服務檔:
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo nano /etc/systemd/system/docker.service.d/proxy.conf
[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,registry.internal,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
NO_PROXY 很重要。它應包含本機回環位址、內部 Registry、Docker 主機所在的內網段和不需要代理的內部服務。不要把整個 Docker Hub 網域放進 NO_PROXY,否則 daemon 會繞過 Clash,配置看似完成但實際仍然直連。
sudo systemctl daemon-reload
sudo systemctl restart docker
sudo systemctl show --property=Environment docker
docker pull docker.io/library/alpine:latest
若輸出中的 Environment 沒有出現預期代理,表示 drop-in 檔案位置、語法或服務管理方式不正確。若代理已載入但仍然超時,再查看 Clash 連線面板:執行 docker pull 的瞬間,應該能看到對應的 Registry 連線。如果完全沒有連線記錄,問題仍在 daemon 沒有使用代理或代理位址不可達。
Clash 端的代理與規則配置要點
Docker Hub 的請求不只涉及一個網域。常見流程會先接觸 Registry API,再前往認證服務取得 token,最後從內容分發網路下載映像層。不同時間、不同區域或不同帳號狀態下,實際出現的網域可能不同,所以只加入一條 registry-1.docker.io 規則並不一定足夠。
在 Clash 或 mihomo 設定裡,可以把 Docker 相關網域集中放到一個規則段,再指向可用的代理群組。下面是概念範例,其中策略群組名稱必須與你的設定檔實際名稱一致:
rules:
- DOMAIN,registry-1.docker.io,PROXY
- DOMAIN,auth.docker.io,PROXY
- DOMAIN,hub.docker.com,PROXY
- DOMAIN-SUFFIX,docker.io,PROXY
- DOMAIN-SUFFIX,docker.com,PROXY
- MATCH,DIRECT
這段配置的重點是規則順序。Clash 規則通常由上往下比對,命中後就停止往下檢查。如果在前面已有寬泛的 GEOIP、IP-CIDR 或自訂 DIRECT 規則,可能在 Docker 網域進入精確規則前就被判定為直連。排查時應查看連線詳情中的「命中規則」和「策略」,不要只看目前選中的代理群組。
使用規則集時,也要留意規則集本身的載入狀態。規則集更新失敗、格式不相容或快取仍是舊版本,都可能讓 Docker 流量落到最後的 MATCH。可以在測試期間先加入明確的 Docker 網域規則,確認流量確實進入代理,再決定是否整理回規則集。
不要把 docker.io、docker.com 無條件全部代理後就結束排查。某些內部 Registry 或企業鏡像站應該直連,而且部分代理節點不適合傳輸大型映像層。建議先以 Docker Hub 相關網域做最小規則,確認可用後再按實際需求擴大範圍。
透明代理方案:讓 daemon 不必理解代理協定
當 Docker daemon、容器和其他系統服務都需要統一接管,或某些程式不支援 HTTP 代理時,可以考慮 Clash 的 TUN 或 Linux 上的 REDIRECT/TPROXY 透明代理。透明代理的核心概念是:應用程式照常向目標位址建立連線,由作業系統的路由或防火牆規則把流量導入 Clash,再由 Clash 依網域、IP 和策略群組處理。
在 mihomo 類核心中,TUN 配置通常需要指定啟用狀態、堆疊類型、DNS 劫持和自動路由。以下是一份示意配置,實際欄位支援情況要以所使用的核心版本和前端文件為準:
tun:
enable: true
stack: system
auto-route: true
auto-detect-interface: true
dns-hijack:
- any:53
dns:
enable: true
ipv6: false
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
nameserver:
- https://1.1.1.1/dns-query
- https://dns.google/dns-query
auto-route 讓核心協助建立路由接管,auto-detect-interface 用於選擇實際出站介面,dns-hijack 則把 DNS 查詢導入 Clash。這些選項不是單獨開啟就能保證 Docker 正常:還要避免 Clash 自己的出站連線再次被 TUN 捕獲,也要保留到區域網路閘道、DNS 上游和代理節點的直連例外,否則容易形成路由迴圈。
如果使用的是 Linux REDIRECT 或 TPROXY,則需要額外管理 nftables 或 iptables 規則,並排除 Docker bridge、Clash 行程 UID、主機內部網段和代理伺服器位址。Docker 預設會建立 docker0 及其他 bridge,容器流量的來源與介面可能和主機流量不同。只對主機輸出鏈設定規則,不代表容器轉發流量也會被接管;反過來,粗略把所有轉發流量重定向,又可能讓容器 DNS、內部服務和健康檢查全部受到影響。
透明代理不是把 HTTP_PROXY 改成另一個值。它會改變封包的實際路徑,涉及路由表、防火牆鏈、DNS 和核心排除規則。正式環境修改前先保留目前的網路規則,並準備本機主控台或帶外管理通道,避免配置錯誤後只能透過網路遠端修復。
DNS、Docker bridge 與 fake-ip 的交互影響
Docker Hub 拉取失敗時,DNS 是第二個需要獨立驗證的環節。Docker daemon 可能使用主機的 /etc/resolv.conf,而容器通常由 Docker 內建 DNS 轉發器提供名稱解析。開啟 TUN 或 fake-ip 後,如果主機 DNS、Docker DNS 和 Clash DNS 的責任邊界沒有整理清楚,就可能出現主機能解析、daemon 不能解析,或瀏覽器正常但容器無法解析的情況。
先查看 Docker 實際使用的 DNS 和網路設定:
docker info
docker network inspect bridge
cat /etc/resolv.conf
docker run --rm busybox nslookup registry-1.docker.io
docker run --rm busybox nslookup auth.docker.io
如果容器內的 nslookup 逾時,但主機上的 getent hosts 正常,問題可能在 Docker bridge 的 DNS 轉發或主機防火牆,不一定是 Docker Hub 被封鎖。若解析結果落在 fake-ip 網段,這本身不一定是錯誤,但必須確認後續 TCP 連線能被 Clash 正確還原並套用網域規則。
Docker daemon 的代理方案和透明代理方案不要同時盲目疊加。兩者都可能把請求送到 Clash,但透明接管加上 daemon 的 HTTPS_PROXY 後,可能出現代理套代理、連線繞路或 CONNECT 請求被再次重定向。建議先選一條路徑完成驗證:要嘛只配置 daemon HTTP 代理,要嘛讓 daemon 直連並由 TUN/TPROXY 接管,確認成功後再評估是否真的需要混用。
按照資料流逐層定位超時原因
當配置完成後仍然失敗,建議依照「程序 → 代理 → DNS → 規則 → 下載」的順序檢查。每一步都要有可觀察的證據,不要只反覆重啟 Docker 或切換節點。
- 確認 daemon 有載入配置:查看 systemd 環境、Docker 設定和重啟時間,確定修改的檔案確實被服務讀取。
- 確認 Clash 代理埠可達:從 Docker 主機用
curl -x測試 Registry,若這一步失敗,先修復 Clash 代理本身。 - 觀察連線面板:執行
docker pull時查看是否有 Registry、認證或 CDN 相關連線,並記錄命中規則與實際策略。 - 分開測試認證和內容服務:不要只測
registry-1.docker.io,也測auth.docker.io;如果 manifest 能取得但 blob 下載超時,可能是內容分發網域或代理節點對大檔案處理不穩定。 - 確認 MTU 與封包分片:TUN、VPN、Docker bridge 疊加時,有效 MTU 可能低於實體網卡。若 TCP 能建立但下載到一半停住,可檢查介面 MTU、PMTU 和相關防火牆,不要第一時間把問題歸咎於 DNS。
- 縮小測試映像:先拉取小型的
alpine或busybox,再測試大型映像。小映像成功、大映像失敗,通常更值得檢查 CDN、節點頻寬、連線重置與 MTU。
curl -v -x http://127.0.0.1:7890 \
https://auth.docker.io/token?service=registry.docker.io
DOCKER_CLI_EXPERIMENTAL=enabled docker manifest inspect \
docker.io/library/alpine:latest
docker pull --quiet docker.io/library/alpine:latest
sudo journalctl -u docker -f
如果 Clash 日誌顯示請求已命中代理,但 Docker 仍然報 TLS 或下載錯誤,可以暫時切換到另一個已知穩定的代理群組做對照。若更換節點後立即恢復,問題較可能是節點出口、CDN 路徑或大型回應處理能力;若所有節點都相同,則應回到 daemon 代理、DNS、MTU 和規則順序繼續排查。
完成排障後的穩定化與安全邊界
確認拉取成功後,不要只留下「能用」的臨時配置。先記錄 Clash 代理埠、Docker daemon 使用的代理方式、NO_PROXY 內容、Docker DNS 和規則命中結果,日後更新核心或訂閱時才有可比較的基準。
- 限制代理監聽範圍:只在確實需要時開啟區域網路存取,不要把帶有完整代理能力的埠直接暴露到公網。
- 保留內部 Registry 直連:企業鏡像站、私有網段和 Docker API 通常應放在
NO_PROXY或直連規則中,避免內部資料經過不必要的代理節點。 - 避免過度寬泛的規則:以 Docker Hub 相關網域為最小範圍,確認需求後再加入其他內容分發網域,並定期檢查規則集是否仍然有效。
- 為 daemon 使用專用設定:代理密碼或認證資訊不要直接散落在多人可讀的 Shell 歷史中,systemd drop-in 檔案也要設定合適的檔案權限。
- 監控失敗類型:定期查看 Docker daemon 日誌和 Clash 連線記錄,區分認證失敗、DNS 失敗、TLS 失敗與下載逾時,這比只監控「拉取成功或失敗」更容易定位問題。
整體來說,最簡單的 Docker Hub 超時修復通常是為 Docker daemon 正確設定 HTTP/HTTPS 代理;需要接管更多不支援代理的服務時,才進一步採用 TUN 或 REDIRECT/TPROXY。透明代理的價值在於統一處理網路層流量,但它同時引入 DNS、路由、Docker bridge 和防火牆之間的耦合。先用最小配置確認每一段資料流,再逐步擴大接管範圍,才是 2026 年仍然可靠的 Clash Docker 排障方法。