一、典型现象:宿主机已翻墙,容器里却拉不动镜像?

常见反馈包括:docker pull nginx 长时间无进度,最后报 context deadline exceeded 或 TLS 超时;在基于 Debian 或 Ubuntu 的基础镜像里执行 apt-get update 失败;Node 项目里 npm ci 卡在 registry 请求上。与此同时,你在 macOS 上打开浏览器访问同一类站点却完全正常。这往往说明问题不在订阅或节点,而在容器出站路径没有显式指向宿主机代理

另一个误区是把代理写成 http://127.0.0.1:7890。在容器内,这会尝试连接容器自己的回环接口,除非你把 Clash 也跑在容器里并监听该端口,否则必然失败。Docker Desktop 为访问宿主机提供了专用主机名 host.docker.internal(在 Mac 与较新版本 Windows 上均受支持),这才是把 HTTP_PROXY 指向本机 Clash 时的正确锚点。

二、原理简述:虚拟机、桥接与 host.docker.internal

Docker Desktop macOS 使用轻量虚拟机承载 Linux 引擎,容器与 Mac 应用不在同一网络命名空间。Docker 会在容器侧解析 host.docker.internal 为可达宿主机的地址,使容器内的 TCP 连接能落到你在 Mac 上监听的端口。该机制与「在 Mac 终端里直接用 127.0.0.1 访问 Clash」是两条不同的路径,不可混用。

因此,打通 容器代理 的核心公式可以记为:代理地址 = http://host.docker.internal:<Clash 混合端口或 HTTP 端口>,并确保 Clash 对该入站来源开放(见下一节)。若你使用纯 Linux 上的 Docker 而非 Desktop,有时需要手动配置 extra_hosts,这与本文场景不同,此处不展开。

三、macOS 本机 Clash:混合端口、allow-lan 与 bind

在填写任何 HTTP_PROXY 之前,请先在 Clash 客户端或 config.yaml 中确认实际监听端口:mixed-port(混合)或 port(HTTP)常见为 7890。若服务仅绑定 127.0.0.1,来自 Docker 网桥的连接会被拒绝,表现为连接超时。请开启「允许局域网连接」或等价选项,使配置中出现 allow-lan: true,并令入站监听在 0.0.0.0 或可接受外部接口的形式(具体字段名以你所用内核与客户端为准)。

macOS 自带的防火墙若开启,需确保对应 TCP 端口对本地虚拟网段放行。更细的端口占用、策略组与报错分类,可对照《Clash 常见报错解决方案》。多设备共享与 allow-lan 思路也可参考《Clash 开启局域网代理》,其中 macOS 段落与本文容器走宿主机是同一类「非回环客户端入站」问题。

四、先在宿主机确认代理端口可用

在 Mac 终端执行(将端口换成你的混合端口):

curl -x http://127.0.0.1:7890 -I https://www.google.com/generate_204

若此处失败,应先修复本机 Clash 与规则,不要急于改 Docker。通过后再在容器内测试经 host.docker.internal 的连通性(见后文「验证」一节),形成「宿主机 → 容器」两级确认,排错成本最低。

五、单次运行:docker run 注入环境变量

对临时调试,可直接向容器传入标准代理变量(大小写建议成对设置,兼容不同工具):

docker run --rm -it \
  -e HTTP_PROXY=http://host.docker.internal:7890 \
  -e HTTPS_PROXY=http://host.docker.internal:7890 \
  -e http_proxy=http://host.docker.internal:7890 \
  -e https_proxy=http://host.docker.internal:7890 \
  -e NO_PROXY=localhost,127.0.0.1,::1,host.docker.internal \
  -e no_proxy=localhost,127.0.0.1,::1,host.docker.internal \
  debian:bookworm-slim bash

进入容器后可执行 apt-get updatecurl -I https://example.com 观察是否仍超时。NO_PROXY 用于让访问本地 registry、内网 Git 等流量直连,请按公司网络实际补全域名与网段,避免误走代理。

六、Docker Compose:运行期 environment 与构建期 build.args

docker-compose.yml 中,运行中的服务通过 environment 继承与 shell 相同的语义:

services:
  web:
    image: node:20-bookworm
    environment:
      HTTP_PROXY: http://host.docker.internal:7890
      HTTPS_PROXY: http://host.docker.internal:7890
      NO_PROXY: localhost,127.0.0.1,host.docker.internal
    command: npm install

镜像构建阶段(docker compose build)默认不会自动带上你在 shell 里的 export,需要在 build.args 中显式传入,Dockerfile 里用 ARG HTTP_PROXY 接收,并在需要时 ENV 固化给后续 RUN

services:
  app:
    build:
      context: .
      args:
        HTTP_PROXY: http://host.docker.internal:7890
        HTTPS_PROXY: http://host.docker.internal:7890
        NO_PROXY: localhost,127.0.0.1,host.docker.internal

若使用 BuildKit,部分环境会自动透传代理相关变量,但行为随版本变化;在文档与 CI 中,仍推荐把 HTTP_PROXY 写清楚,减少「本地能构建、同事机器失败」的差异。

七、Dockerfile 里 apt、npm 与多阶段构建

在 Dockerfile 顶部声明 ARG HTTP_PROXY HTTPS_PROXY NO_PROXY,在 RUN apt-get update 之前用 ENV 导出,可在构建时让 Debian/Ubuntu 基础镜像走代理。多阶段构建时,每一阶段若仍有外网 RUN,需要重复声明或在阶段间谨慎传递,否则后一阶段会退回直连。

若仅运行期需要代理、构建期使用内网镜像源,可以刻意不传 build.args,避免把构建流量导到本机 Clash,减轻日志噪音。根据流水线是否访问公网注册表,选择「仅运行期 容器代理」或「构建与运行双阶段都走 host.docker.internal」。

八、Docker Desktop 设置里的「代理」与容器环境变量

Docker Desktop 偏好设置中的代理选项,主要影响守护进程拉取镜像等行为的出站路径,与某个容器进程是否读取 HTTP_PROXY 并不是同一件事。实际排错时,请分清:① docker pull 是否在宿主机侧已走代理;② 容器内应用是否读取了你注入的环境变量或自身配置文件。

有些团队只在 Desktop 里填代理,却发现进入容器后 curl 仍直连失败,就是因为业务进程只看容器环境。反之,仅配置 Compose 而不处理守护进程,也可能导致拉基础镜像阶段超时。根据你卡住的命令,分别对症配置,避免只改一处就以为全局生效。

九、何时用 SOCKS,与 ALL_PROXY 的补充

本文以 HTTP 代理为例,因为 Clash 的混合端口对大量工具链兼容性最好。若某 CLI 只认 SOCKS5,可将 ALL_PROXY 设为 socks5://host.docker.internal:7891(端口以你本地 SOCKS 监听为准),并保留 HTTP_PROXY 给 apt、npm 等仍以 HTTP 代理为主的场景。混用时注意同一端口是否同时提供 HTTP 与 SOCKS,避免写错协议导致握手失败。

十、验证顺序与排错清单

建议按序执行:① Mac 本机 curl -x 127.0.0.1:端口 成功;② 在容器里复用同一代理地址,例如 docker run --rm curlimages/curl curl -x http://host.docker.internal:7890 -I --max-time 15 https://www.google.com/generate_204(将端口换成你的混合端口),确认经 host.docker.internal 的整条链路可用;③ 在目标镜像内带完整代理环境变量执行 curl -I https://registry-1.docker.io 或实际失败命令。若②失败,检查主机名解析与 Clash 是否监听 0.0.0.0;若②成③败,检查变量名大小写、NO_PROXY 是否误伤、以及应用是否忽略环境变量而使用自有配置。

Clash 连接日志是否出现来自 Docker 网段的入站,是判断流量是否打到核心的直观依据。若只有浏览器会话、没有任何容器来源,说明容器代理仍未指向宿主机。完成以上步骤后,大多数 Docker Desktop macOSClash 本机代理 联用问题都能收敛到「地址写错、端口未对外开放、构建与运行阶段漏配其一」三类。

十一、小结

macOS 上让 Docker Desktop 里的 workload 走 本机 Clash,关键是接受「容器里的 127.0.0.1 不是 Mac」这一事实,把出站统一写到 host.docker.internal,并为 HTTP_PROXYHTTPS_PROXYNO_PROXY 建立可复制模板;Compose 与 Dockerfile 则要区分运行期构建期,避免只配其一。与 Windows WSL2 下手动解析宿主机 IP 相比,Mac 上 Docker 官方提供的主机名让脚本更稳定。若你希望用图形界面管理端口、规则与局域网选项,减少手写 YAML 出错,可从本站获取 macOS 客户端安装包:→ 立即免费下载 Clash,开启流畅上网新体验