Docker 開發環境裡最常見的網路問題,往往不是容器本身故障,而是容器與主機使用了不同的網路視角:主機上的 Clash 已經可以正常開啟 GitHub、Docker Hub 或 npm registry,但容器內執行 docker pullnpm installgo mod download 時仍然逾時。要解決這類問題,不一定要在每個映像檔中安裝代理工具,也不必為每個專案寫一套不同的網路邏輯。本篇將以主機上的 Clash/Mihomo 作為統一出口,說明 Docker 容器如何透過 HTTP、SOCKS5 或透明轉發取得代理,並進一步處理 Docker daemon、DNS、Compose、分流規則與安全性問題。

先理解 Docker 與 Clash 之間的網路關係

Docker 容器預設使用 bridge 網路。容器會獲得一個獨立的私有 IP,透過 Docker 建立的虛擬閘道器連到主機,再由主機轉發到外部網路。這代表容器中的 127.0.0.1 並不是主機的 localhost,而是容器自己的 localhost。因此,即使 Clash 在主機上監聽 127.0.0.1:7890,容器存取 127.0.0.1:7890 時仍然找不到主機上的 Clash。

要讓容器使用主機代理,至少要同時滿足三個條件:

  • Clash 必須監聽容器可到達的位址,而不能只綁定在主機 localhost。
  • 容器必須知道主機閘道器的位址,例如 Linux bridge 網路中的 172.17.0.1,或 Docker 提供的 host.docker.internal
  • 容器內的工具或網路路由必須把請求交給 Clash;只開啟 Clash 但沒有設定代理環境變數,流量不會自動進入 Clash。

這裡的「透明代理」需要先釐清。嚴格來說,設定 HTTP_PROXYHTTPS_PROXY顯式代理:應用程式知道代理伺服器的存在。真正的透明代理則是透過 TUN、REDIR、TPROXY 或主機防火牆規則,攔截不支援代理設定的 TCP/UDP 流量。實務上,開發環境通常先使用顯式代理,因為相容性高、容易回滾;只有在需要代理所有程式或第三方二進位工具時,才值得導入 TUN 或 TPROXY。

設定 Clash 監聽位址與代理埠

Clash 設定檔通常同時提供 HTTP、SOCKS5 以及混合代理埠。若希望 Docker 中的工具不必區分協議,可以使用 mixed-port。但要注意,主機對外開放代理埠會增加被區域網路其他裝置濫用的風險,因此應配合防火牆、區域網路信任範圍與 Clash 的認證設定。

Clash 主機監聽設定
mixed-port: 7890
socks-port: 7891
allow-lan: true
bind-address: '*'

authentication:
  - 'docker-user:change-this-password'

mode: rule
log-level: info

allow-lan: true 允許其他網路介面連入 Clash;bind-address: '*' 則讓 Clash 綁定所有可用介面。不同核心對欄位支援程度可能略有差異,若使用 Mihomo,應以目前版本的配置檔驗證結果為準。若只是同一台 Linux 主機上的 Docker bridge 容器,也可以將監聽位址限制在 Docker bridge 的主機 IP,例如 172.17.0.1,不必對整個區域網路開放。

不要直接暴露未授權的代理埠:allow-lan 開啟後,如果路由器或防火牆允許外部連入,其他人可能把你的主機當作公開代理。至少設定強密碼,並在作業系統防火牆中限制來源網段。

讓容器透過主機閘道器使用代理

不同作業系統取得主機位址的方式不同。Docker Desktop for Windows 與 macOS 通常提供 host.docker.internal 這個特殊名稱;Linux 原生 Docker 則建議在啟動容器時加入 host-gateway 對映,讓同一份 Compose 設定可以使用一致的主機名稱。

環境代理主機位址常見注意事項
Windows / macOS Docker Desktophost.docker.internal確認 Clash 允許區域網路連入,且代理埠沒有被防火牆阻擋
Linux bridge172.17.0.1host.docker.internal需加入 extra_hosts 的 host-gateway 對映
自訂 Docker network主機 bridge 位址或明確路由位址不要假設所有網段都使用 172.17.0.0/16
host 網路模式127.0.0.1Linux 可用,但會失去部分網路隔離能力,Windows/macOS 行為不同

以下 Compose 範例將代理環境變數集中放在服務層。容器內的 curl、npm、pip、Git 等支援標準環境變數的工具,通常會自動使用這些設定。

Docker Compose 代理設定
services:
  dev:
    image: node:22-bookworm
    working_dir: /workspace
    volumes:
      - .:/workspace
    environment:
      HTTP_PROXY: http://docker-user:[email protected]:7890
      HTTPS_PROXY: http://docker-user:[email protected]:7890
      ALL_PROXY: socks5://docker-user:[email protected]:7890
      NO_PROXY: localhost,127.0.0.1,::1,.local,host.docker.internal
    extra_hosts:
      - "host.docker.internal:host-gateway"

實際使用時,建議不要把密碼直接提交到 Git 儲存庫,可以改用 Compose 的 .env、Docker secrets 或本機未追蹤的覆寫檔。NO_PROXY 很重要:它可以避免本地服務、資料庫、Docker 內部網域與公司內網請求繞到外部代理,降低延遲並避免內部服務因代理規則而失敗。

為不同工具補上代理設定

並非所有程式都遵循相同的環境變數。npm、curl 和多數 Unix 工具通常遵循大小寫混用的代理變數,但某些程式只讀取大寫或只支援自己的配置檔。Git 可以單獨設定代理,npm 也可以透過配置指令指定代理端點。

容器內的工具代理設定
# 測試主機代理是否可達
curl -I https://github.com

# Git 使用 HTTP 代理
git config --global http.proxy http://host.docker.internal:7890
git config --global https.proxy http://host.docker.internal:7890

# npm 使用代理
npm config set proxy http://host.docker.internal:7890
npm config set https-proxy http://host.docker.internal:7890

如果只有 npm 需要代理,不建議在整個基礎映像檔中永久寫入代理設定,因為映像檔可能被帶到沒有 Clash 的 CI 或生產環境。比較好的做法是在本機 Compose 覆寫檔中注入環境變數,或在開發容器啟動腳本中檢查代理主機是否可用後再設定。

Docker pull 與容器內請求是兩條不同路徑

很多人已經在 Compose 裡設定 HTTP_PROXY,卻發現 docker pull node:22 仍然逾時。原因是 docker pull 由 Docker daemon 執行,而不是由即將啟動的容器執行。容器的環境變數只影響容器啟動後的程式,無法替 Docker daemon 下載映像檔。

在 Linux 使用 systemd 管理 Docker 時,可以為 daemon 建立代理覆寫設定。以下內容中的代理位址應替換成 Docker daemon 能夠連到的主機位址;如果 daemon 本身就在主機上,通常不能把容器名稱當成代理主機名稱。

Docker daemon 代理設定
# /etc/systemd/system/docker.service.d/http-proxy.conf
[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,::1"

# 套用設定
sudo systemctl daemon-reload
sudo systemctl restart docker
sudo systemctl show --property=Environment docker
docker pull node:22

Docker Desktop 的 daemon 由 Docker Desktop 管理,通常應在 Docker Desktop 的設定介面或其代理設定頁面配置,而不是直接修改 Linux 的 systemd 檔案。若公司使用私有 Registry,還要確認 Registry 網域是否應加入 NO_PROXY;錯誤地讓內部 Registry 經過外部代理,可能造成憑證、認證或速度問題。

真正透明轉發與 TUN 模式的使用時機

顯式代理無法覆蓋所有場景,例如某些閉源 CLI、需要 UDP 的測試工具、使用固定 IP 的程式,或完全忽略環境變數的建置工具。這時才需要考慮 Mihomo 的 TUN 模式,或在 Linux 主機上使用 REDIR/TPROXY 配合 iptables 將流量導向 Clash。

TUN 模式會在主機建立虛擬網路介面,由核心把符合條件的流量交給 Clash 處理。它的優點是應用程式無須知道代理存在,域名規則與 DNS 也可以由 Clash 統一接管;缺點是需要更高的系統權限,並且要特別處理 Docker 自身網段、Clash 監聽埠、DNS 回環與排除清單。若把 Docker bridge 的流量再次導回 TUN,還可能形成回環,導致 CPU 使用率升高或所有連線逾時。

Mihomo TUN 基本範例
tun:
  enable: true
  stack: system
  auto-route: true
  auto-detect-interface: true
  dns-hijack:
    - any:53
  route-exclude-address:
    - 192.168.0.0/16
    - 172.16.0.0/12
    - 10.0.0.0/8

這只是方向性的配置,不代表所有 Linux 發行版都能直接套用。啟用 TUN 前,應先確認核心支援、程序具備 NET_ADMIN 或相應權限,並保留主機的本地網段排除規則。Docker 容器若需要直接使用 TUN 路徑,還應逐步測試 DNS、TCP、UDP 以及容器到內網服務的連線,不要一次把所有開發環境切換過去。

分流規則與開發流量的實務設計

透明代理真正有價值的地方,不是「所有流量都代理」,而是讓不同目的地使用合理的路徑。GitHub、npm、PyPI、Docker Hub 等開發服務通常需要代理;公司 Git、區域網路 Registry、資料庫與本機服務則應直連。規則順序非常重要,Clash 通常採用由上到下的第一條命中規則,過早使用 MATCH 會讓後續規則全部失效。

開發環境分流規則示例
rules:
  # Docker 與本機服務優先直連
  - DOMAIN-SUFFIX,local,DIRECT
  - DOMAIN,host.docker.internal,DIRECT
  - IP-CIDR,127.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,github.com,PROXY
  - DOMAIN-SUFFIX,githubusercontent.com,PROXY
  - DOMAIN-SUFFIX,npmjs.org,PROXY
  - DOMAIN-SUFFIX,pypi.org,PROXY
  - DOMAIN-SUFFIX,docker.io,PROXY
  - MATCH,DIRECT

對於大型團隊,不建議把公司網段、內部域名與公開服務規則全部寫在單一檔案中。可以使用 Rule Provider 分開管理,例如一份專門放內網直連網段,另一份放開發工具域名。這樣更容易在主機、CI Runner 與遠端開發機之間共用,也能在服務域名變更時單獨更新規則。

建議的排查順序:先在容器內確認主機代理埠可連,再確認代理環境變數是否被工具讀取,接著檢查 DNS 解析與 Clash 連線記錄,最後才調整規則。不要一開始就修改大量 iptables,否則很難判斷問題究竟來自路由、DNS、認證還是代理規則。

常見故障排查與安全收尾

若容器顯示 Connection refused,通常代表主機位址或代理埠錯誤,或 Clash 仍只監聽 localhost;若顯示 Connection timed out,則可能是防火牆、Docker 網路路由或代理節點本身無法連線。若 GitHub 可以使用但 npm 失敗,應檢查 npm 是否讀取代理變數、registry 是否被自訂,以及 HTTPS 代理是否使用正確的協議。

  1. 在容器內執行 getent hosts host.docker.internal,確認主機名稱可以解析。
  2. 使用 nc -vz host.docker.internal 7890curl 測試代理埠是否可達。
  3. 檢查 env | grep -i proxy,確認變數沒有拼寫錯誤,並查看 NO_PROXY 是否誤把目標域名排除。
  4. 在 Clash Dashboard 的 Connections 或日誌中確認請求是否出現,以及實際命中的規則與策略組。
  5. 分別測試 DNS、HTTPS、Git、套件管理器與 Docker daemon,不要把不同層級的失敗混為同一個問題。

安全方面,開發代理最好只服務於可信任的本機或區域網路。不要在映像檔中寫入長期有效的代理密碼,也不要將帶有認證資訊的 HTTP_PROXY 環境變數輸出到公開 CI 日誌。完成測試後,可以把 Clash 的監聽範圍縮回 Docker 所需網段,或關閉 allow-lan,並移除不再需要的 daemon 代理設定。

總結來說,Docker 使用 Clash 的關鍵不是單純「把代理埠填進去」,而是分清楚主機、Docker daemon、容器應用程式與透明轉發核心這四個層次。先用主機閘道器加上顯式代理建立可驗證的基礎,再依照需求加入 TUN、DNS 接管與分流規則,通常能以最低複雜度獲得穩定的開發體驗。設定完成後,Docker pull、GitHub 存取與 npm 安裝就能共享同一套 Clash 出口,不必為每個容器重複維護網路配置。

立即開始

用 Clash 掌控您的流量

支援 Windows、macOS、Linux、Android 與 iOS,靈活規則,開箱即用。

免費下載 查看設定指南 →