Clash Docker 透明代理進階設定:排解映像檔下載逾時

針對開發者與維運人員整理 Docker 搭配 Clash 的進階代理方案,涵蓋容器流量轉送、Docker Engine 設定、CI/CD 使用方式,以及映像檔下載異常的診斷技巧。

Docker 為什麼會在映像檔下載時逾時

Docker 搭配 Clash 時,最常見的誤區是把「主機上的代理已經連線」等同於「Docker 流量也會自動經過代理」。實際上,Docker Engine、Docker CLI、容器內的應用程式是三個不同的網路層。瀏覽器能打開外部網站,只代表瀏覽器遵循了系統代理設定;執行 docker pull 時,真正連線到映像檔 Registry 的通常是 Docker daemon,它不會讀取瀏覽器的代理設定,也不會自動套用 Clash 的系統代理。

因此,映像檔下載逾時可能發生在不同位置:Docker daemon 無法解析 Registry 網域、daemon 沒有使用代理、Clash 規則把 Registry 送往錯誤出口、代理節點無法連線目標地區,或是 Docker 使用的 HTTPS 代理設定不完整。錯誤訊息通常會出現 i/o timeoutcontext deadline exceededTLS handshake timeoutClient.Timeout exceeded while awaiting headers 等字樣。

先確認問題屬於哪一層,再決定設定方式。若只有 docker pull 失敗,而主機上的瀏覽器正常,優先檢查 Docker daemon 代理。若映像檔可以下載,但容器內的套件管理器或 API 請求失敗,則要另外設定容器環境變數。若希望不修改每個容器與 daemon 設定,才考慮使用 mihomo 的 TUN 與透明轉送。

先分清楚三種流量

docker pull 主要是 Docker daemon 的流量;docker build 可能同時包含 daemon 下載基礎映像檔與建置容器執行指令的流量;docker run 後的連線則屬於容器流量。三者要分別驗證,不能只在容器裡執行一次 curl 就判定整套代理正常。

排查順序:先確認 Registry、DNS 與出口

開始修改 YAML 前,先使用最小測試確認網路故障的範圍。以下指令不會改變 Docker 設定,適合在 Linux 主機上逐步執行。若使用 macOS 或 Windows 的 Docker Desktop,命令仍可在終端機執行,但 Docker daemon 實際位於虛擬機內,後續的位址判斷要以 Docker Desktop 的網路架構為準。

  1. 查看 Docker daemon 狀態與版本,確認目前連線的是哪個 Engine:
    docker infodocker version。如果 docker info 本身就等待很久或回傳連線錯誤,問題還沒進入 Registry,應先處理 Docker 服務。
  2. 在主機測試 Registry 的 DNS 與 HTTPS:
    getent hosts registry-1.docker.iocurl -I -v --connect-timeout 10 https://registry-1.docker.io/v2/。回傳 401 Unauthorized 反而代表 HTTPS 已經抵達 Registry,因為未帶認證時這是正常回應。
  3. 在容器內測試解析與連線:
    docker run --rm alpine:3.20 getent hosts registry-1.docker.io。如果基礎映像檔尚未存在,可先用現有的 busybox 或其他本地映像測試。
  4. 檢查 Clash 的連接記錄。執行 docker pull 時,觀察是否出現 Registry 網域、認證服務網域與 CDN 網域。只看到瀏覽器連線,完全看不到 Docker 相關請求,通常表示 daemon 根本沒有進入 Clash。
  5. 用直連與代理各測一次,不要只測單一節點。若直連逾時、代理能收到請求但遠端仍逾時,再檢查節點地區、TLS 連線與規則命中結果。
現象較可能的原因優先處理位置
主機 curl 正常, docker pull 逾時Docker daemon 沒有代理設定Docker Engine / systemd
daemon 能連線,建置步驟中的 curl 逾時建置容器沒有 HTTP(S) 代理環境Dockerfile 或 build arguments
Clash 完全沒有 Docker 連線記錄代理位址填成錯誤的 127.0.0.1 或未開放區域網路Clash 監聽埠與網路路由
出現 TLS handshake timeout代理節點不穩、MTU 不合或 HTTPS 代理鏈路異常節點、TUN stack、MTU
解析到錯誤或不存在的位址Docker DNS 未經 Clash,或上游 DNS 回應遭污染DNS、dns-hijack 與規則

方案一:為 Docker Engine 設定 HTTP 與 HTTPS 代理

只想解決 docker pulldocker push 與私有 Registry 存取問題時,為 Docker daemon 設定代理是最穩定、改動最小的方案。Clash 必須對 Docker daemon 提供可達的 HTTP 代理埠,例如 mihomo 的 mixed-port: 7890。如果 Docker daemon 和 Clash 在同一台 Linux 主機上,可以使用主機可被 daemon 存取的位址;不要在容器或遠端 daemon 的情境下盲目填 127.0.0.1:7890,因為那個 127.0.0.1 可能指向不同的網路命名空間。

Linux 上可在 /etc/docker/daemon.json 加入代理設定。若檔案原本已有 registry-mirrorslog-driver 等欄位,必須合併 JSON,不要直接覆蓋整個檔案:

{
  "proxies": {
    "http-proxy": "http://192.0.2.10:7890",
    "https-proxy": "http://192.0.2.10:7890",
    "no-proxy": "localhost,127.0.0.1,::1,192.168.0.0/16,10.0.0.0/8"
  }
}

上面的 192.0.2.10 是文件示例位址,請替換成 Clash 所在主機對 Docker daemon 可達的實際位址。no-proxy 用來保留本機、區域網路與內部 Registry 的直連,否則內部服務可能被送到外部節點,造成認證失敗或無法回到內網。修改後先驗證 JSON 格式,再重新啟動 Docker:

sudo dockerd --validate --config-file=/etc/docker/daemon.json
sudo systemctl restart docker
docker info

不同 Docker Engine 版本對 daemon JSON 代理欄位的支援情況可能不同。若重啟後 docker info 沒有反映設定,可以改用 systemd drop-in,這也是排查 Linux daemon 代理時常用的方式:

sudo systemctl edit docker

[Service]
Environment="HTTP_PROXY=http://192.0.2.10:7890"
Environment="HTTPS_PROXY=http://192.0.2.10:7890"
Environment="NO_PROXY=localhost,127.0.0.1,::1,192.168.0.0/16,10.0.0.0/8"

儲存後執行 sudo systemctl daemon-reloadsudo systemctl restart docker,再用 systemctl show --property=Environment docker 確認環境變數是否已載入。代理設定生效後,重新執行 docker pull alpine:3.20,並同步觀察 Clash 的連線記錄。Docker daemon 的代理設定不會自動傳遞給容器,這一點必須特別記住。

方案二:為容器與 CI/CD 傳遞代理變數

容器內的套件下載、原始碼拉取與 API 呼叫,需要在容器程序自己的環境中設定 HTTP_PROXYHTTPS_PROXYNO_PROXY。這與 Docker daemon 的代理是兩套設定。以 Linux 主機為例,容器要存取主機上的 Clash,可使用 Docker bridge 的閘道位址,或在支援的環境使用 host.docker.internal。實際位址可先用 ip -4 addr show docker0 查看,常見 bridge 位址是 172.17.0.1,但不要把這個數值當成所有主機的固定值。

docker run --rm \
  -e HTTP_PROXY=http://172.17.0.1:7890 \
  -e HTTPS_PROXY=http://172.17.0.1:7890 \
  -e NO_PROXY=localhost,127.0.0.1,.local,172.17.0.0/16 \
  alpine:3.20 \
  sh -c 'env | grep -i proxy; wget -S -O- https://example.com'

建置映像檔時,不要把代理網址直接寫入最終映像檔層,避免憑證或內部網路資訊留在 image history。較常見的做法是使用 BuildKit 的建置參數:

docker build \
  --build-arg HTTP_PROXY=http://172.17.0.1:7890 \
  --build-arg HTTPS_PROXY=http://172.17.0.1:7890 \
  --build-arg NO_PROXY=localhost,127.0.0.1,.local \
  -t demo-app:dev .

Dockerfile 內可宣告對應的 ARG,並只在需要下載依賴的步驟使用。部分套件工具還有自己的代理設定,例如 npm、pip、Go module 或 Maven,它們可能讀取標準環境變數,也可能需要額外參數。若在建置時遇到「第一個 RUN 指令成功,第二個下載指令失敗」,先確認失敗工具是否尊重大寫與小寫代理變數,再檢查 NO_PROXY 是否錯把外部 Registry 網域列入。

CI/CD Runner 也要分成兩段處理:Runner 下載 job 所需的 image,由 Runner 所在主機的 Docker daemon 負責;job 內執行的 curl、套件安裝與測試,則由 job 容器的環境變數負責。建議在 CI 的 secret 或受保護變數中管理代理網址,不要把帶有認證資訊的完整 URL 寫進公開工作流程檔。對內部 Git、資料庫、Artifact Registry 等服務,明確填入 NO_PROXY,降低迴路代理與憑證錯誤。

方案三:使用 mihomo TUN 進行透明轉送

當應用程式很多、無法逐一設定環境變數,或需要接管不支援 HTTP 代理的 TCP/UDP 流量時,可以在 Docker 主機上使用 mihomo TUN。TUN 不是只開一個代理埠,而是建立虛擬網卡並配合路由規則接管流量。因此它需要更高權限、正確的路由表與 DNS 劫持設定,也更容易和 Docker 自己建立的 iptables 規則互相影響。

以下是適合 Linux 主機作為起點的 mihomo 設定片段。這份範例只展示透明代理相關欄位,節點、策略組與完整規則仍要依現有設定檔補齊:

mixed-port: 7890
allow-lan: true
bind-address: "*"
mode: rule

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
  fake-ip-range: 198.18.0.1/16
  nameserver:
    - https://192.0.2.53/dns-query
  proxy-server-nameserver:
    - https://192.0.2.53/dns-query

rules:
  - IP-CIDR,127.0.0.0/8,DIRECT,no-resolve
  - IP-CIDR,10.0.0.0/8,DIRECT,no-resolve
  - IP-CIDR,172.16.0.0/12,DIRECT,no-resolve
  - IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
  - DOMAIN-SUFFIX,internal.example,DIRECT
  - MATCH,PROXY

範例中的 192.0.2.53 是文件保留示例位址,實際使用時必須換成可用的 DNS 上游。allow-lan: true 只代表 Clash 願意接收區域網路來源,不等於 Docker 流量已經自動被路由過來。還需要確認 Docker bridge 的流量能到達 TUN,以及防火牆沒有阻擋 7890 埠。若只想讓本機 daemon 使用 HTTP 代理,不必為了這個需求直接開啟整機 TUN。

Docker 使用者自訂 bridge 網路時,要特別留意容器網段與 fake-IP 網段重疊。Docker 常見網段如 172.17.0.0/16172.18.0.0/16 不應和 TUN、內網或其他 VPN 網段衝突。發生衝突時,容器可能無法連到主機閘道,或內部服務被錯誤送進代理。可用 docker network inspect bridge 查看 Subnet,再調整 Docker 的 address pool 或既有虛擬網路規劃。

透明代理不是萬用開關

Docker Engine 的 Registry 連線通常是 HTTPS,最容易用 daemon 代理穩定解決。TUN 方案涉及路由、DNS、iptables 與 MTU,適合已有 Linux 網路維運經驗的環境。先用明確的 HTTP 代理驗證可用性,再逐步切換透明轉送,排錯範圍會小很多。

映像檔下載逾時的進階修正與驗證

如果代理已經生效但仍然逾時,先不要連續更換大量設定。先固定同一個映像檔、同一個節點與同一台主機,每次只改一項參數。Registry 下載通常會先存取認證服務,再取得 manifest,最後從 CDN 拉取多層 blob;只允許 Registry 主網域通過並不一定足夠,Clash 記錄裡可能還會出現認證或內容分發網域。

  • 檢查規則命中。不要只把 registry-1.docker.io 寫成直連或代理。先觀察實際 DNS 名稱與連線目標,再用 DOMAIN-SUFFIX 規則覆蓋相關服務。規則中的 MATCH 必須放在最後,否則後面的 Registry 規則不會生效。
  • 檢查 HTTPS 代理格式。Clash 的 mixed port 可以接受 HTTP 代理與 SOCKS5,但 Docker daemon 的 https-proxy 常見寫法仍是 http://代理位址:7890,因為這表示以 HTTP CONNECT 建立 HTTPS 通道,不是把方案名稱改成 https://
  • 檢查 MTU 與 TUN stack。若連線能建立,但大檔 blob 下載到一半卡住,可能是 TUN stack、VPN 上游或路徑 MTU 問題。可先使用 stack: mixed,再比較 systemgvisor 的結果,不要同時更改節點與 stack。
  • 檢查時間與憑證。主機時間偏差過大會造成 TLS 憑證驗證失敗。執行 date 或平台對應的時間同步檢查,不要為了繞過錯誤而關閉 TLS 驗證。
  • 確認並發與磁碟狀態。映像檔層很多時,網路正常但磁碟空間不足、inode 用盡或 Docker 儲存目錄權限異常,也可能被誤判為下載失敗。用 df -hdf -idocker system df 交叉確認。

完成修改後,按照「主機 HTTPS → Docker daemon → 容器內請求 → CI/CD 建置」的順序重新測試。每一層都能成功,才代表代理鏈路完整。正式環境建議為 Docker Registry、內部服務與外部依賴分別建立規則,保留必要的 NO_PROXY,並記錄 Clash 核心版本、Docker Engine 版本、代理埠與 Docker 網段。日後節點、核心或作業系統更新後,才能快速比對是哪一項變更造成新的逾時。

延伸操作

先從 Docker daemon 的代理設定開始,再按需求加入容器環境變數或 mihomo TUN。下載用戶端可查看可用版本,完整設定流程則可依照快速入門逐步檢查。

下載 Clash 用戶端

規則分流的前提是用戶端先接管流量。前往下載中心依平台選擇用戶端,再回到教學文章完成系統代理或 TUN 接管設定。

下載Clash