開發與 AI · Clash 技術部落格

Docker 與 WSL2 怎麼走 Clash 代理?映像檔拉取超時與網路設定

Windows、WSL2、Docker 引擎和容器各有自己的網路環境,所以瀏覽器能用並不能證明 docker pull 會用代理。按請求發起位置逐層設定即可。

  • Docker
  • WSL2
  • 開發環境
本文目錄

docker pull、WSL 命令和容器請求來自不同位置

Docker 與 WSL2 要走 Windows 上的 Clash,不能只在一個終端裡寫代理。docker pull、WSL 中的 apt、映像檔建置中的 RUN 和啟動後的容器,分別由不同元件發出請求;哪一步發生錯誤,就設定哪一層。

Windows 上的 Clash 監聽 127.0.0.1,只保證 Windows 本機能存取。WSL2 預設執行在 NAT 虛擬網路裡,容器還有自己的網路命名空間;它們看到的 127.0.0.1 通常是自己,不是主機機。

Docker 拉映像檔、docker build 中的下載、執行中容器存取外部網路,也由不同元件發起。只在 WSL shell 裡 export HTTP_PROXY,未必能讓 Docker Desktop 的映像檔拉取走代理。

錯誤發生在哪一層

失敗動作真正發起請求的元件
curl / apt / git 在 WSL 裡超時WSL 發行版中的處理程式
docker pull 超時Docker Desktop 或 Docker Engine 守護處理程式
RUN apt-get 在 build 時超時BuildKit / 建置容器
啟動後的應用請求失敗執行中的容器
Windows、WSL2 與容器之間的代理路徑
  1. Windows 上的 Clash監聽區域網路可達的 mixed-port
  2. WSL2使用主機機位址存取該連接埠
  3. Docker 建置顯式接收 HTTP_PROXY 和 HTTPS_PROXY
  4. 容器內工具再處理 Git、npm 或套件管理員

127.0.0.1 在每一層都指向自己。代理位址必須填寫上一層實際可達的主機機位址。

主機代理連接埠要能被虛擬網路存取

先在 Clash 查看實際的 HTTP 或 mixed-port,常見示例是 7890,但你的用戶端可能不同。WSL 使用主機 IP 存取時,這個連線會被當作區域網路連線,用戶端需要允許 LAN 存取,並監聽 WSL 能到達的位址。

允許 LAN 會擴大連接埠可達範圍。只在可信的家庭或單位私有網路使用,並讓 Windows 防火牆限制來源;不要把無認證代理暴露到公共 Wi-Fi 或公用網路。完成測試後若不再需要,可以關閉 LAN 存取。

WSL 預設 NAT 用主機 IP,映像檔網路可直接試 127.0.0.1

預設 NAT 模式下,可以在 WSL 讀取預設路由的下一跳,它通常就是 Windows 主機在 WSL 虛擬網路中的位址。Windows 11 22H2 及後續版本啟用 mirrored networking 後,WSL 可以直接透過 127.0.0.1 存取 Windows 服務。

WSL 預設 NAT 測試,7890 是待替換的 Clash 連接埠
HOST_IP=$(ip route show | grep -i default | awk '{ print $3 }')
CLASH_PORT=7890
echo "Windows host: $HOST_IP"
curl -I -x "http://${HOST_IP}:${CLASH_PORT}" https://example.com

這條 curl 得到 HTTP 回應後,才有必要設定 WSL 環境變數。若代理連接埠直接回傳 Connection refused,問題在 Clash 監聽、允許 LAN 或防火牆;若連接埠可達但外部請求 timeout,再檢查節點和規則。

連接埠可達後,再給 WSL 命令列設定環境變數

目前 WSL shell 臨時生效,7890 是連接埠佔位值
HOST_IP=$(ip route show | grep -i default | awk '{ print $3 }')
CLASH_PORT=7890
export HTTP_PROXY="http://${HOST_IP}:${CLASH_PORT}"
export HTTPS_PROXY="http://${HOST_IP}:${CLASH_PORT}"
export NO_PROXY="localhost,127.0.0.1,::1"
curl -I https://example.com

臨時變數關閉終端就會消失,適合疑難排解。用 apt、git 或 curl 重做同一個請求,確認成功後,再按 shell 類型寫進自己的啟動檔案。

Windows 支援的 WSL 版本也可以在 .wslconfig 使用 autoProxy=true,讓 WSL 匯入 Windows 的 HTTP 代理資訊。啟用後要重啟 WSL,並檢查實際變數值。

NO_PROXY 應包含只在 WSL 內部提供的服務,避免存取 localhost 時繞到 Windows 代理。公司內部網域和私有網段是否加入,要按實際網路決定。

docker pull 失敗,要到 Docker Desktop 的代理設定

Docker Desktop 拉取映像檔和 WSL shell 命令由不同處理程式發起。開啟 Docker Desktop 的 Settings,在 Resources / Proxies(不同版本標籤可能略有變化)裡填寫主機可用的 HTTP、HTTPS 代理,並應用設定。

Docker 官方文件明確說明,Desktop 不讀取 daemon.json 中的守護處理程式代理設定。不要因為 WSL 終端已經能存取,就跳過 Docker Desktop 自己的代理設定。

若使用的是獨立 Linux Docker Engine,而不是 Docker Desktop,才按 Engine 文件在 daemon.json 或 systemd 環境中設定守護處理程式代理,並重啟 Docker 服務。先確認自己執行的形態,避免改了一個根本不被讀取的檔案。

WSL curl 成功,docker pull 仍 timeout

檢查 Docker Desktop 的 Proxies 設定和重啟後的狀態。

Docker Desktop 改 daemon.json 沒效果

移到 Desktop 圖形設定;該產品會忽略 daemon proxy 設定。

原生 Linux Engine 拉取失敗

查看 daemon 的代理環境與服務日誌,而不是 Windows 系統代理。

映像檔能拉下來,建置步驟和執行容器仍可能沒代理

Docker 的用戶端設定可以為新容器和建置自動注入代理環境,也可以在單次命令裡顯式傳入。建置時用 --build-arg,執行階段用 --env;不要在 Dockerfile 裡用 ENV 固化帶認證資訊的代理位址,映像檔歷史和設定可能把它留下。

Docker Desktop 單次測試,7890 是待替換的 Clash 連接埠
CLASH_PROXY=http://host.docker.internal:7890

docker build \
  --build-arg HTTP_PROXY="$CLASH_PROXY" \
  --build-arg HTTPS_PROXY="$CLASH_PROXY" \
  -t demo-app .

docker run --rm \
  --env HTTP_PROXY="$CLASH_PROXY" \
  --env HTTPS_PROXY="$CLASH_PROXY" \
  --env NO_PROXY="localhost,127.0.0.1" \
  demo-app

Docker Desktop 通常提供 host.docker.internal 指向主機機。原生 Linux Engine 是否可用取決於環境,需要用明確的主機閘道或額外 host-gateway 設定。命令執行前,用容器內的 curl 或應用日誌驗證這個主機名和連接埠確實可達。

NO_PROXY 寫漏了,容器之間的請求也會繞遠

資料庫名、Compose 服務名、localhost 和內部網段通常不該交給外部代理。把實際內部網域加入 NO_PROXY,能避免應用存取 db:5432、redis:6379 時先去 Windows 兜一圈。不同工具對通配符和 CIDR 的支援不完全相同,設定後要在目標處理程式裡列印變數並做實際請求。

代理位址若含使用者名稱或密碼,不要寫進公開的 compose.yaml、Dockerfile 或映像檔。使用本機環境檔案或專案的 secret 管理方式,並確保日誌不會把完整 URL 打出來。

按實際使用完成四次驗證

  • WSL 中 curl 透過 Windows Clash 得到回應
  • docker pull 可以拉取一個小映像檔
  • docker build 的網路步驟不再超時
  • 執行容器既能存取外部網路,也能直連內部服務

四項結果可以分別對應到 WSL、Docker 引擎、建置過程和執行容器。哪一項仍失敗,就回到那一層的設定,不必重新改已經透過的其他三層。

參考資料