為什麼終端與 Docker 常常不跟系統代理/TUN

桌面瀏覽器多數會尊重作業系統的 HTTP 代理設定,因此當 Clash 客戶端寫入系統代理後,你很容易得到一種錯覺:「我已經全局走 Clash 了」。然而命令列工具的生態並不一致:curl 預設不讀 macOS「系統代理」那套設定,除非你顯式加上 -x 或透過環境變數告訴它代理位址;git 在走 HTTPS 遠端時,會參考 http.proxyhttps.proxy 或標準代理環境變數,但若你從未設定,它就會嘗試直連。換句話說,終端代理往往需要你「多寫一層契約」,而不是只靠 GUI 開關。

TUN 模式可以把多數程式的 IP 流量導入 Clash 決策堆疊,對「不想逐一手動設代理」非常省事;但它牽涉路由表、權限與和其他 VPN/防火牆的互動,並非每個環境都適合長駐。許多開發者反而偏好:瀏覽器走系統代理或擴充套件,而終端與 CI 類工具顯式指向 127.0.0.1:混合埠,必要時再用 NO_PROXY 把內網與本機服務排除。這條路的好處是行為可預期、可寫進 shell 設定檔或專案文件,團隊成員複製同一組變數即可重現。本文聚焦這條「顯式代理」路線,與本站其他偏重 TUN、安裝與規則分流的篇章互補而不重複。

Docker 則再多一層隔離:容器內的 127.0.0.1 指向容器自己,不是宿主機。若你在容器裡設定 HTTP_PROXY=http://127.0.0.1:7890,卻沒有在宿主把 Clash 監聽到可從橋接網路抵達的位址,或沒用正確的宿主主機名,就會出現「宿主上 curl 正常、進容器全失敗」的經典現象。下文會把埠、協定、環境變數與宿主位址四件事對齊,讓 docker build 拉 base image、docker pull 與建置過程中的 aptnpm 都能穩定命中同一個 Clash 入口。

先選協定再選變數:mixed-port 上同時有 HTTP 代理語意與 SOCKS5。多數工具讀 HTTP_PROXYHTTPS_PROXY 時走 HTTP CONNECT;讀 ALL_PROXY 時常搭配 socks5h:// 讓 DNS 也經代理解析。兩種都合法,重點是與工具預期一致

認識 mixed-port:HTTP 與 SOCKS5 共用同一監聽埠

在 Clash/Mihomo 系設定中,mixed-port 代表單一 TCP 埠同時接受 HTTP 代理與 SOCKS5 連線(實際細節依核心版本與客戶端封裝而定,請以你所用的發行版文件為準)。對使用者而言,最實用的記法是:把圖形介面或設定檔裡顯示的混合埠數字(常見預設如 7890,僅作舉例)當成「終端與腳本的統一入口」。這比分別記 portsocks-port 更不容易抄錯,也方便在文件裡只寫一個數字。

當你為同事寫「請在本機開 Clash,然後 export 這幾行」時,建議直接把協定前綴寫清楚:http://127.0.0.1:PORT 給純 HTTP 代理客戶端;socks5h://127.0.0.1:PORT 給希望遠端解析主機名的工具鏈(字母 h 常代表讓代理解析 DNS,可避免本地汙染或假 IP 造成的怪異行為)。若某程式只認其中一種,就換對應前綴,而不是堅持「我只用一種寫法打天下」。Clash 的核心價值在規則分流與穩定轉發,代理埠只是對外契約;更多規則編排可延伸閱讀 規則分流最佳實踐

設定檔層級通常會看到 mixed-portallow-lan 等欄位並列出現。下面是一段示意片段(埠號、鍵名請依你的訂閱與核心版本調整,勿當成唯一標準答案):

config.yaml fragment (illustrative)
mixed-port: 7890
allow-lan: false
bind-address: '*'

重點在於:當你只需要本機終端連線時,127.0.0.1 就夠用;一旦涉及 Docker 預設橋接網路或其他裝置連到你電腦上的 Clash,就要重新評估 allow-lan 與綁定位址,否則會出現「宿主可連、容器永遠 connection refused」的落差。

監聽位址、allow-lan 與 Docker 橋接網路

預設只監聽 127.0.0.1 時,只有宿主機本機行程能連上該埠;這對單純終端使用是最安全的。當 Docker 容器透過 bridge 網路要連回宿主上的 Clash,封包來源不再是 loopback,而是內部橋接位址;此時若 Clash 仍只綁在 127.0.0.1,容器無論怎麼設 HTTP_PROXY 都連不到。實務上有兩條常見路線:(1)允許 Clash 監聽宿主在橋接網路上的可達位址(並搭配防火牆與 allow-lan 政策,避免對外網意外開放);(2)使用 Docker Desktop 提供的 host.docker.internal 類特殊主機名,把流量導向宿主,但仍需宿主上代理程式在對應介面可連。

這與「區網分享手機連不上」那類議題同源:都是綁定位址、防火牆與是否允許區網連入的組合題。若你正在排查多裝置共享,亦可對照 區網共享與防火牆 的思路;本文則專注在本機終端+本機 Docker 這條主線。無論選哪條路,請記得:開放監聽面等同擴大攻擊面,筆電在公共網路時尤應審慎。

終端環境變數:ALL_PROXYHTTP_PROXYNO_PROXY

下列範例以埠號 7890 代表你的 mixed-port,請替換成實際數字。於 bashzsh 可寫成:

Shell export examples
export HTTP_PROXY="http://127.0.0.1:7890"
export HTTPS_PROXY="http://127.0.0.1:7890"
export ALL_PROXY="socks5h://127.0.0.1:7890"
export NO_PROXY="localhost,127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"

HTTP_PROXYHTTPS_PROXY 讓多數支援 HTTP 代理語意的程式(含不少語言套件管理器)走 CONNECT 隧道;ALL_PROXY 則作為兜底,讓只認「全域代理」變數的工具也能找到同一個埠。部分環境會同時辨識小寫 http_proxyhttps_proxy,若你遇到「同一個 shell 大寫有效、小寫無效」的差異,多半是程式實作選了其中一種慣例,可兩組都寫上以減少摩擦。

NO_PROXY(或 no_proxy)決定哪些目的地不要進代理。內網 RFC1918 位址、公司內部 Git、本機 Kubernetes API、Docker registry 鏡像站等,常應出現在清單裡,否則流量會被硬拖進 Clash,反而慢甚至失敗。清單語法在不同工具間略有差異:有的支援萬用字元子網域、有的只接受逗號分隔主機名;遇到怪異行為時,建議先用最小清單驗證(只排除 127.0.0.1),再逐步加上內網段。若你不確定某條流量為何沒走代理,可暫時移除 NO_PROXY 對照,並在 Clash 日誌裡看連線是否出現。

gitcurl:常見行為與驗證方式

git 而言,除了環境變數,亦可設定:git config --global http.proxy http://127.0.0.1:7890 與對應的 https.proxy。好處是設定跟著 Git 走,不污染整個 shell;壞處是團隊成員若忘記設,行為又與 export 不一致。實務上常見折衷是:在專案 README 建議使用環境變數,並在 CI 以相同變數建置。驗證時可用 GIT_CURL_VERBOSE=1 git ls-remote 觀察是否嘗試連代理,或乾脆用 curl -I https://example.com 對照出口。

curl 若未設變數,可暫時測試:curl -x http://127.0.0.1:7890 -I https://www.google.com(僅作連通性測試,實際 URL 請依你環境合法可存取的目標替換)。一旦確認代理埠可用,再把 -x 換成 export,讓其他依賴 libcurl 的工具跟著受益。若出現 TLS 或逾時錯誤,請勿直覺怪節點:先用 連線日誌與 TLS 排查 區分是握手失敗還是單純沒走到 Clash。

Docker:docker buildpull 與 daemon 代理設定

Docker 分兩層:daemon(負責 docker pull、映像管理)與容器內行程docker build 的 RUN 步驟、容器裡的 apt)。兩者都可能需要代理,但設定位置不同。daemon 層在 Linux 上常以 systemd drop-in 或 /etc/docker/daemon.json 搭配環境變數;在 Docker Desktop(macOS)則可在 GUI 填寫 HTTP/HTTPS proxy,等同幫 daemon 設好出口。

建置階段常用 docker build --build-arg HTTP_PROXY=... --build-arg HTTPS_PROXY=...,並在 Dockerfile 開頭寫 ARG HTTP_PROXYARG HTTPS_PROXY 讓 RUN 繼承。BuildKit 預設行為與舊版 builder 略有差異,若你發現只有部分步驟生效,請查官方文件確認是否需 ENV 或額外 build secret。重點仍是:代理 URL 裡的主機名必須是容器視角可達的宿主位址,而不是盲目的 127.0.0.1,除非使用 host 網路模式等特殊配置。

個人開發者也可以在 ~/.docker/config.json 內設定 proxies 區塊(鍵名與支援度依 Docker 版本而異),讓預設 build/pull 帶代理。無論哪種方式,都建議把同一組埠與協定寫進團隊文件,與上文終端 export 保持一致,減少「我本機可以、你不行」的溝通成本。

macOS 與 Linux:host.docker.internal 與宿主 IP

在 Docker Desktop for Mac,容器內通常可使用 host.docker.internal 指向宿主,因此代理可寫成 http://host.docker.internal:7890(埠號替換為你的 mixed-port)。在純 Linux 上,若無該主機名,可改用 ip route 查到的 docker0 閘道(常見為 172.17.0.1,實際以你機器為準),或自行在 extra_hosts 注入對應。重點永遠是:在容器內 ping 或 curl 該位址,確認能 TCP 連上代理埠。

若你讓 Clash 只聽 127.0.0.1,則即使用對宿主 IP,仍可能連不上;此時需調整綁定位址或啟用可從橋接網路存取的監聽面,並理解安全意涵。相對地,若你已啟用 TUN 並希望容器也統一走透明代理,則要另評估路由與 MTU,這已超出「純環境變數」範疇;可回到 TUN 模式深度解析 衡量取捨。

循環代理與其他高頻坑

循環代理是最常見的「設定看起來全對,卻什麼都拉不下來」原因:Clash 本身更新訂閱或下載規則集時,若系統或環境變數把所有 HTTP 流量都塞回同一個本機埠,而規則又把該請求再次導向代理,便可能形成迴圈或自我阻塞。解法通常是:為訂閱域名、規則 CDN、內部 registry 等加上 NO_PROXY,或在 Clash 設定裡為更新通道指定直連策略。這與 常見問題 裡提到的訂閱與 DNS 話題相呼應,排障時請優先檢查「更新流量是否誤進代理鏈」。

其他坑包括:混用公司 VPN 與本機 Clash 導致路由競爭、只設 HTTP_PROXY 卻忘 HTTPS_PROXY、以及把內網 registry 誤寫進全局代理卻沒在 NO_PROXY 排除。每個問題都可以用「縮小實驗」驗證:同一命令先關代理、再開代理、最後只改一個變數,對照 Clash 連線紀錄是否出現對應 TCP 連入。若你正在挑選日誌可讀、方便切換模式的客戶端,可參考 如何選擇適合自己的 Clash 客戶端

安全與合規:請勿將代理開放於不可信網路;在企業環境應遵守資訊安全政策。本文僅說明技術接線方式,不鼓勵違法或未授權的網路繞行。

結語

Clash mixed-portHTTP 代理SOCKS5 收斂到單一埠,讓你在 macOS/Linux 上能用一小組數字服務終端與容器;ALL_PROXYHTTP_PROXYHTTPS_PROXYNO_PROXY 則是把「誰要走代理、誰要直連」寫清楚的契約。Docker 代理的關鍵則是命名空間:在容器裡用對宿主位址,並讓 Clash 在該位址上真的聽得到。相比只依賴系統代理或一律開 TUN,這套組合更適合開發者日常:需要時顯式開啟,遇到內網或訂閱更新再用 NO_PROXY 精準放行。

相較把希望寄託在「某個工具會自動幫我搞定一切」,願意為終端與 Docker 各寫一段可複製設定的人,長期維護成本通常更低:問題發生時,你能立刻知道是環境變數、daemon 還是規則命中。選擇介面清楚、核心行為透明、日誌可對照的 Clash 客戶端,這條路會更好走。

立即免費下載 Clash,開啟流暢上網新體驗,把 mixed-portSOCKS5環境變數一次配好,讓終端與容器流量也能穩定納入你的分流策略。