Docker pull 為什麼會卡在 Docker Hub?

在伺服器上執行 docker pull 時,最常見的錯覺是「Docker Hub 被封鎖,所以只要替 Docker 設定一個代理就好」。實際上,一次完整的映像檔下載通常會經過多個不同主機:先向 registry 取得認證資訊,再向 Docker Registry 查詢 manifest,最後從內容分發網路下載數十 MB 甚至數 GB 的 layer。只要其中一段沒有經過正確的代理出口,終端就可能長時間停在 WaitingRetryingcontext deadline exceeded

如果錯誤訊息是 i/o timeoutTLS handshake timeoutnet/http: request canceled while waiting for connection,通常代表 TCP 連線、DNS 解析或 TLS 握手階段沒有在預期時間內完成;若顯示 pull access deniedunauthorized,則更接近帳號權限、私有映像檔或登入憑證問題,不能單純歸咎於 Clash。先把錯誤類型分開,後面的分流和效能調校才不會變成盲目修改。

本篇的重點是在 Linux 伺服器上以 Mihomo/Clash 建立透明代理,讓 Docker daemon、容器內的套件管理器,以及主機本身的診斷指令可以使用清楚、可追蹤的網路路徑。Clash 只負責本機流量轉送與規則管理,不提供遠端節點;請在合法授權、符合所在地法規與服務條款的前提下使用自己的代理服務。

先記住:Docker daemon 的網路命名空間與你的 SSH shell 不一定相同。即使主機上用瀏覽器或 curl 測試成功,Docker daemon 仍可能完全沒有走代理。

先畫出資料流:主機代理、Docker daemon 與容器不是同一層

在 Linux 環境中,Docker 下載映像檔通常由背景執行的 dockerd 發起,而不是由你輸入命令的 shell 直接建立所有外部連線。這一點非常關鍵:你在終端設定的 http_proxyhttps_proxy,只有在 systemd 環境變數確實傳給 dockerd 時才會生效。若只是把變數寫入目前使用者的 ~/.bashrc,重啟 Docker 後通常不會自動繼承。

透明代理則是另一條路徑。當 Mihomo 以 TUN 或 redir-host/redirect 方式接管主機流量時,封包會在核心路由、虛擬介面或 nftables 規則層被導向 Clash。這種方式不依賴每個程式是否理解 HTTP proxy,因此適合 Docker、Git、curl 和不支援代理環境變數的服務。不過,透明代理也更容易遇到代理自身回環、DNS 被重導、Docker bridge 流量未納入規則,以及管理流量被誤送代理等問題。

  • 主機 shell:用於執行 curldigdocker 等指令,可能使用環境變數,也可能完全不使用代理。
  • Docker daemon:負責向 registry、認證端點與 layer CDN 發起下載,應單獨確認它的代理設定或透明接管狀態。
  • 容器內應用程式:容器啟動後的 aptapk、npm 或 API 請求,還要看容器 DNS、預設閘道與環境變數是否正確。
  • Docker bridge 與宿主機:封包可能從 docker0 或自訂 bridge 出發;若 nftables/iptables 只處理主機程序,容器流量就可能繞過 Mihomo。

因此,排查時不要只問「Clash 有沒有開」。更有用的問題是:哪個程序建立了連線、解析出什麼 IP、命中了哪條規則、最後從哪個策略群組出去。在 Mihomo 的連線面板或日誌中,應觀察 registry、auth、CDN 等主機是否出現;若完全看不到相關連線,代表問題發生在 Clash 之前,應先查 Docker daemon、DNS 或防火牆。

Mihomo 透明代理的基礎設定與安全邊界

以下是適合伺服器環境理解的設定骨架。不同版本的 Mihomo、不同客戶端對欄位名稱和權限處理可能略有差異,請先備份現有設定,再依實際版本文件調整。初次配置時,建議先使用固定的 mixed inbound 或 TUN 入口驗證流量,再逐步加入 DNS 劫持和容器網段,避免一次改動太多而無法定位問題。

YAMLmixed-port: 7890
allow-lan: true
bind-address: 0.0.0.0
mode: rule
log-level: info

tun:
  enable: true
  stack: mixed
  auto-route: true
  auto-detect-interface: true
  dns-hijack:
    - any:53

dns:
  enable: true
  ipv6: false
  enhanced-mode: fake-ip
  nameserver:
    - https://1.1.1.1/dns-query
    - https://8.8.8.8/dns-query

allow-lan: true 不應被理解成「把代理公開給整個網際網路」。若伺服器有公網介面,將 mixed port 綁定到 0.0.0.0 後,必須用雲端安全群組、防火牆或 bind address 限制來源網段。最安全的做法通常是只允許內部管理網、Docker bridge 或特定私有網段連入,並禁止從公網直接存取代理埠。若只需要接管本機 Docker daemon,可以優先綁定 127.0.0.1,再以 systemd proxy 或本機透明路由完成整合。

使用 TUN 時,auto-routeauto-detect-interface 能減少多網卡、雲端主機預設路由變化造成的問題;但它們不會自動修正所有策略迴路。請把 Clash 本身的控制器、DNS 上游、節點伺服器和 Docker 網段納入 bypass 或 direct 設計,否則可能出現「Clash 連線依賴自己的 TUN、TUN 又等待 Clash 建立連線」的循環。若啟用 fake-ip,還要確認 Docker 內部服務不依賴只能取得真實 IP 的特殊 DNS 行為。

不要直接照抄公開設定:透明代理涉及路由表、nftables、DNS 與核心權限。先在測試伺服器驗證,並保留 SSH 管理路徑的直連例外,避免規則錯誤後把自己鎖在伺服器外。

Docker Hub 精準分流:registry、認證與 CDN 要一起處理

Docker Hub 的網域不應只用一條模糊的 DOMAIN-KEYWORD,docker 規則處理。這種寫法可能把不需要代理的內部服務一併送出,也可能漏掉真正承載 layer 的主機。建議先從 Mihomo 連線日誌記錄實際出現的完整網域,再使用較窄的 DOMAIN 或 DOMAIN-SUFFIX 規則。常見起點包括 registry-1.docker.ioauth.docker.iohub.docker.com,以及下載過程中由回應導向的 CDN 主機;CDN 網域會因映像檔、區域和時間而變化,不能假設永遠只有一個固定名稱。

YAMLproxy-groups:
  - name: DOCKER
    type: select
    proxies:
      - Proxy
      - DIRECT

rules:
  - DOMAIN,registry-1.docker.io,DOCKER
  - DOMAIN,auth.docker.io,DOCKER
  - DOMAIN,hub.docker.com,DOCKER
  - DOMAIN-SUFFIX,docker.com,DOCKER
  - DOMAIN-SUFFIX,docker.io,DOCKER
  - DOMAIN,localhost,DIRECT
  - IP-CIDR,127.0.0.0/8,DIRECT,no-resolve
  - MATCH,DIRECT

這份規則只是可驗證的起點,不代表所有 CDN 都應用 DOMAIN-SUFFIX,docker.io 無條件代理。更穩妥的流程是先固定一個節點,執行一次 docker pull alpine:latest 或你實際需要的映像,接著在連線紀錄中觀察每個新出現的主機。如果 registry 和 auth 已經命中 DOCKER,但 layer 下載仍逾時,通常表示 CDN 主機沒有匹配規則,或該策略群組的節點對大檔案與長連線表現不佳。

對於公司私有 registry,建議使用明確的 DOMAIN,registry.example.com,DIRECT 或指定的內部策略群組,不要讓 Docker Hub 的泛用規則覆蓋它。若企業 DNS 解析出內網 IP,可用 IP-CIDR 搭配 no-resolve 避免再次解析;同時確認 registry 的 TLS 憑證和 Docker daemon 的信任鏈正確。把所有錯誤都送代理,有時反而會造成內網 registry 無法連線。

DNS 模式也會影響規則命中。若 Mihomo 使用 fake-ip,而 Docker daemon 使用宿主機 DNS、容器使用內建 DNS,三者可能得到不同結果。診斷時可分別執行 getent hosts registry-1.docker.iodig registry-1.docker.io 和容器內的 cat /etc/resolv.conf,確認解析請求是否進入 Mihomo。若你只看到錯誤 IP 或完全沒有 DNS 連線紀錄,先修正 resolver 路徑,再調整分流規則。

讓 Docker daemon 真正使用代理,並調校大檔案下載

若不打算讓整台伺服器使用 TUN,也可以為 Docker daemon 設定明確的 HTTP、HTTPS 代理。以 systemd drop-in 為例,建立對應目錄和設定檔後,讓 dockerd 使用本機 mixed port:

Shellsudo mkdir -p /etc/systemd/system/docker.service.d
sudo tee /etc/systemd/system/docker.service.d/proxy.conf <<'EOF'
[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,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
EOF

sudo systemctl daemon-reload
sudo systemctl restart docker
sudo systemctl show --property=Environment docker

NO_PROXY 的內容要依你的私有 registry、內部 API 和雲端 metadata 位址調整,不能不加思考地套用。錯誤的 NO_PROXY 會讓 Docker Hub 走直連,也可能讓本應保持內網直連的服務繞到代理。設定完成後,請先執行 docker info 確認 daemon 正常,再查看 Docker 服務日誌與 Mihomo 連線紀錄,不要只用瀏覽器判斷。

大映像檔的下載速度不只取決於代理開關,還受到節點頻寬、TCP 重傳、磁碟寫入和 Docker 並行下載影響。伺服器若記憶體有限,可以避免同時進行多個大型 docker pull;若磁碟接近滿載,layer 解壓和校驗也會讓畫面看起來像網路逾時。可用 df -hiostatss -sjournalctl -u docker 同時觀察資源狀態。

  • 固定節點做對照:不要用會頻繁切換出口的 url-test 測量長時間 pull,先選一個穩定節點完成 A/B 測試。
  • 保留重試空間:短暫 CDN 斷線時可重新執行 pull,Docker 已完成的 layer 通常會保留,不必每次從零下載。
  • 分開測試小檔案與大檔案:registry API 回應很快,不代表 layer CDN 的吞吐量和連線穩定性足夠。
  • 檢查 MTU:雲端網卡、VPN、TUN 和 Docker bridge 疊加時,過大的封包可能造成重傳;只有在日誌和封包測試支持時才調整 MTU。

從現象反推原因:一套可重複的排障流程

完成設定後,建議按照由近到遠的順序驗證。每一步都只改一個變數,並記錄命中規則、使用出口和錯誤時間,這比反覆切換多份設定檔更容易找出真正原因。

  1. 確認 Mihomo 本身正常:檢查核心狀態、mixed port 或 TUN 是否啟動,並確認管理介面能看到即時連線。
  2. 確認 DNS 路徑:分別在主機、Docker daemon 所在環境與容器內解析 registry-1.docker.io,比較結果和解析延遲。
  3. 確認認證端點:curl -I https://auth.docker.io/token 做基本測試,並在 Clash 連線面板查看是否命中 DOCKER
  4. 重新執行實際下載:使用平常會失敗的映像檔,而不是只測試一個很小的首頁請求,觀察 registry 與 layer CDN 是否都出現。
  5. 對照代理與直連:固定同一映像、同一時間和同一節點,分別比較代理出口與直連結果,避免把遠端服務短暫故障誤判成規則錯誤。
  6. 檢查 daemon 日誌與資源:查看 journalctl -u docker、磁碟空間、CPU、記憶體和網卡錯誤,確認不是本機資源耗盡。
現象 優先檢查項目 較可能的處理方向
認證請求逾時 auth.docker.io 是否命中代理 補上精準網域規則,檢查 DNS 與節點 TLS
manifest 成功但 layer 卡住 連線紀錄中的 CDN 主機 依實際主機補規則,改用穩定且有足夠頻寬的出口
主機 curl 成功,docker pull 失敗 dockerd 的 systemd 環境 設定 daemon proxy,或確認 Docker bridge 已被透明接管
容器內 apt 失敗,pull 正常 容器 DNS、環境變數與 bridge 路由 為容器提供正確 DNS 或納入 TUN/nftables 流量範圍
下載中途反覆斷線 節點 TCP 穩定性、MTU、磁碟 I/O 固定節點,降低並行下載,檢查重傳與本機資源

最後,請把「能不能下載」和「是否適合長期運作」分開評估。測試成功一次,只能證明某個時間點的路由可用;若這是 CI/CD、私有 registry mirror 或生產環境,還應記錄平均下載時間、失敗率、DNS 延遲和節點切換行為。與其把所有流量粗暴地送往代理,不如只為 Docker Hub 相關主機建立獨立策略群組,並為內部服務保留清楚的直連例外。相較於部分同類代理工具需要手動拼接多套 iptables 規則、介面命名不一致,或文件只涵蓋瀏覽器代理而忽略 Docker daemon,Clash 官網 的優勢在於以 Mihomo 為核心、能集中查看連線命中情況,並把 DNS、TUN、規則和 daemon 代理拆成可驗證的步驟;如果你希望在伺服器上少走一些設定彎路,不妨前往下載 Clash 官網,先用測試環境把本文的透明代理流程跑通,再逐步部署到正式環境。