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 timeout、context deadline exceeded、TLS handshake timeout、Client.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 的網路架構為準。
- 查看 Docker daemon 狀態與版本,確認目前連線的是哪個 Engine:
docker info與docker version。如果docker info本身就等待很久或回傳連線錯誤,問題還沒進入 Registry,應先處理 Docker 服務。 - 在主機測試 Registry 的 DNS 與 HTTPS:
getent hosts registry-1.docker.io、curl -I -v --connect-timeout 10 https://registry-1.docker.io/v2/。回傳401 Unauthorized反而代表 HTTPS 已經抵達 Registry,因為未帶認證時這是正常回應。 - 在容器內測試解析與連線:
docker run --rm alpine:3.20 getent hosts registry-1.docker.io。如果基礎映像檔尚未存在,可先用現有的busybox或其他本地映像測試。 - 檢查 Clash 的連接記錄。執行
docker pull時,觀察是否出現 Registry 網域、認證服務網域與 CDN 網域。只看到瀏覽器連線,完全看不到 Docker 相關請求,通常表示 daemon 根本沒有進入 Clash。 - 用直連與代理各測一次,不要只測單一節點。若直連逾時、代理能收到請求但遠端仍逾時,再檢查節點地區、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 pull、docker 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-mirrors、log-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-reload 與 sudo systemctl restart docker,再用 systemctl show --property=Environment docker 確認環境變數是否已載入。代理設定生效後,重新執行 docker pull alpine:3.20,並同步觀察 Clash 的連線記錄。Docker daemon 的代理設定不會自動傳遞給容器,這一點必須特別記住。
方案二:為容器與 CI/CD 傳遞代理變數
容器內的套件下載、原始碼拉取與 API 呼叫,需要在容器程序自己的環境中設定 HTTP_PROXY、HTTPS_PROXY 與 NO_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/16、172.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,再比較system或gvisor的結果,不要同時更改節點與 stack。 - 檢查時間與憑證。主機時間偏差過大會造成 TLS 憑證驗證失敗。執行
date或平台對應的時間同步檢查,不要為了繞過錯誤而關閉 TLS 驗證。 - 確認並發與磁碟狀態。映像檔層很多時,網路正常但磁碟空間不足、inode 用盡或 Docker 儲存目錄權限異常,也可能被誤判為下載失敗。用
df -h、df -i與docker system df交叉確認。
完成修改後,按照「主機 HTTPS → Docker daemon → 容器內請求 → CI/CD 建置」的順序重新測試。每一層都能成功,才代表代理鏈路完整。正式環境建議為 Docker Registry、內部服務與外部依賴分別建立規則,保留必要的 NO_PROXY,並記錄 Clash 核心版本、Docker Engine 版本、代理埠與 Docker 網段。日後節點、核心或作業系統更新後,才能快速比對是哪一項變更造成新的逾時。
下載 Clash 用戶端
規則分流的前提是用戶端先接管流量。前往下載中心依平台選擇用戶端,再回到教學文章完成系統代理或 TUN 接管設定。