Skip to content
文章 / 1 分钟阅读

Docker 代理与 SOCKS5 配置:守护进程、构建、容器和桥接

Docker 并没有一个统一的代理开关。守护进程、构建过程和每个容器会在不同位置读取代理设置——而且守护进程完全不支持 SOCKS5。下面介绍如何配置每一层并验证其是否生效。

Docker 代理与 SOCKS5 配置:守护进程、构建、容器和桥接

如果你因为 Docker 拉取一直超时而搜索“socks5 configurator”,你并不孤单。Docker 至少有三种代理层,而且它们不共享设置。对 docker pull 有效的代理,可能无法帮助构建过程中的 apt-get,而许多代理服务常见的 SOCKS5,并不被 Docker 守护进程的代理变量接受。本指南将逐步介绍每一层,并提供可复制粘贴的配置和验证步骤,以便你只路由需要路由的内容,并避免泄露 DNS 或凭据。

Docker 的三个代理层(以及每一层适用于哪里)

层 影响什么 在哪里设置 协议支持
守护进程 docker pull、docker push、仓库认证 systemd drop-in(Linux)或 Docker Desktop 设置 HTTP/HTTPS 代理 URL
构建 docker build 期间的 RUN 命令(apt、pip、npm) --build-arg + Dockerfile 中的 ARG HTTP/HTTPS 代理 URL
容器运行时 容器内应用产生的流量 -e 标志或 Compose environment 取决于应用(HTTP、HTTPS、SOCKS5,如果应用支持)

人们忘记的第四个地方:宿主机自己的包管理器和工具。如果你无法访问 Docker 仓库,通常也无法在宿主机上访问 apt 或 pip。先设置宿主机级代理,然后再配置 Docker。关于宿主机上按应用路由,请参阅我们的 SOCKS5 配置器指南,了解浏览器、SSH、curl、git 和 Python。

守护进程级代理:用于拉取镜像和与仓库通信

Docker 守护进程从其 systemd 服务读取标准代理环境变量。创建一个 drop-in 文件:

sudo mkdir -p /etc/systemd/system/docker.service.d
sudo tee /etc/systemd/system/docker.service.d/http-proxy.conf > /dev/null <<'EOF'
[Service]
Environment=HTTP_PROXY=http://user:[email protected]:3128
Environment=HTTPS_PROXY=http://user:[email protected]:3128
Environment=NO_PROXY=localhost,127.0.0.1,::1,.internal.example,registry.internal.example
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

注意:

  • 该值是代理 URL。对于支持 CONNECT 的标准 HTTP 代理,使用 http://。如果你的代理端点本身是 TLS 包装的,使用 https://。
  • NO_PROXY 应包含你不想代理的任何内部仓库或主机。.internal.example 中的前导点在许多实现中匹配子域名,但行为因实现而异。
  • systemd 文件中的凭据对 root 可见。对于共享机器,考虑使用不需要在环境中提供凭据的本地转发器。

验证:

docker info | grep -i -A2 'Proxy'

你应该会看到 HTTP Proxy、HTTPS Proxy 和 No Proxy 行。如果它们为空,说明守护进程没有读取该 drop-in。

然后测试拉取:

docker pull alpine:latest

如果仍然失败,检查守护进程日志:

journalctl -u docker.service --no-pager -n 50

Docker Desktop(macOS/Windows)在 Settings、Resources、Proxies 下有 GUI 代理设置。它会为守护进程写入相同的变量。

构建时代理:当拉取正常但 RUN apt-get 不正常时

守护进程代理不会自动应用于构建容器。你必须传递代理参数并在 Dockerfile 中声明它们。

FROM debian:bookworm-slim

ARG HTTP_PROXY
ARG HTTPS_PROXY
ARG NO_PROXY

RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates && rm -rf /var/lib/apt/lists/*

构建:

docker build --build-arg HTTP_PROXY=http://user:[email protected]:3128 --build-arg HTTPS_PROXY=http://user:[email protected]:3128 --build-arg NO_PROXY=localhost,127.0.0.1 -t myapp .

重要:--build-arg 值可能在镜像历史和构建日志中可见。永远不要把凭据烘焙到最终镜像中。使用多阶段构建,并在最终阶段前取消这些参数,或使用 BuildKit secret mount 处理凭据。

每容器代理:在运行时设置环境变量

对于读取代理环境变量的应用,在启动容器时设置它们:

docker run --rm -e HTTP_PROXY=http://proxy.example:3128 -e HTTPS_PROXY=http://proxy.example:3128 -e NO_PROXY=localhost,127.0.0.1 myapp

在 Docker Compose 中:

services:
  app:
    image: myapp
    environment:
      HTTP_PROXY: http://proxy.example:3128
      HTTPS_PROXY: http://proxy.example:3128
      NO_PROXY: localhost,127.0.0.1,.internal.example

并非所有应用都会遵循这些变量。有些只读取小写的 http_proxy,有些完全忽略 NO_PROXY,还有些有自己的配置文件。务必检查应用的文档。

SOCKS5:为什么 Docker 的代理变量不接受它

Docker 守护进程和 --build-arg 代理变量期望 HTTP 或 HTTPS 代理 URL。socks5:// 不是守护进程支持的协议。如果你的代理服务只给你一个 SOCKS5 端点,你有三种实用选择:

  1. 运行本地 HTTP 到 SOCKS5 桥接。一个小型转发器监听 127.0.0.1:3128 并转发到你的 SOCKS5 代理。然后把 Docker 的 HTTP_PROXY 指向该桥接。这样可以将 SOCKS5 凭据排除在 Docker 环境之外。
  2. 使用 SSH 动态转发加桥接。ssh -D 1080 为你提供一个本地 SOCKS5 代理;桥接将其转换为 HTTP 供 Docker 使用。当你已经可以通过 SSH 访问远程主机时,这非常有效。
  3. 配置容器内的应用直接使用 SOCKS5。许多工具(curl、requests、git)原生支持 socks5h://。这完全绕过 Docker 的代理设置,只影响该应用。

示例:不使用桥接,从容器内使用 curl 测试 SOCKS5。在 Linux 上,添加 --add-host=host.docker.internal:host-gateway,以便容器可以访问运行在宿主机上的代理。

docker run --rm --add-host=host.docker.internal:host-gateway curlimages/curl:latest --socks5-hostname host.docker.internal:1080 https://ifconfig.me

--socks5-hostname 将主机名发送给代理进行 DNS 解析,从而避免 DNS 泄露。如果使用 --socks5,curl 会在本地解析 DNS,只隧道 TCP 连接——这是错误地理位置和泄露的常见来源。

对于应用代码,socks5h 方案通常更清晰:

curl --proxy socks5h://user:[email protected]:1080 https://ifconfig.me

验证代理确实被使用(并捕获泄露)

配置后,在每一层验证:

  • 守护进程:docker info | grep -i proxy 显示变量。然后 docker pull 应该命中代理。你也可以检查代理自己的日志,查看对 registry-1.docker.io:443 的 CONNECT 请求。
  • 构建:添加一个打印所观察 IP 的 RUN 步骤:RUN curl -s https://ifconfig.me || true。如果该 IP 是你的真实 IP,则构建没有走代理。在发布前删除此行。
  • 容器:docker exec -it <container> sh -c 'curl -s https://ifconfig.me' 并与你的真实 IP 比较。
  • DNS:HTTP 代理通常会在代理端为 HTTPS 解析 DNS(通过 CONNECT),但如果客户端本地解析,普通 HTTP 请求可能会泄露 DNS。使用 socks5h 的 SOCKS5 始终远程解析。如果 DNS 很重要,请使用 DNS 泄露检查器测试或检查代理日志。

Docker 和代理的常见陷阱

  • NO_PROXY 不是自动的。如果你的内部仓库或宿主机上的服务被不必要地代理,请将其添加到 NO_PROXY。包括 localhost、127.0.0.1 和任何内部域。
  • 大写与小写。有些工具读取 HTTP_PROXY,其他读取 http_proxy。如果不确定,两者都设置。在 Dockerfile 中,ARG HTTP_PROXY 足以让构建过程读取大写变体;如果特定工具需要小写,添加 ARG http_proxy。
  • 构建历史中的凭据。--build-arg 值可能出现在 docker history 中。对令牌和密码使用 BuildKit secret mount(--mount=type=secret)。
  • Docker Desktop 与 Linux 守护进程。在 Linux 上你编辑 systemd;在 Docker Desktop 上你使用 GUI 或 settings.json。不要假设 Linux 教程适用于 Docker Desktop。
  • IPv6 和双栈。如果你的代理只支持 IPv4,强制容器或守护进程优先使用 IPv4。有些工具需要显式标志。

受限或过滤网络的实用工作流

如果你所在的网络对仓库访问很慢、被限速或被过滤——这在一些企业和国家网络中很常见——先从守护进程代理开始,让镜像拉取成功。然后为包管理器添加构建时参数。最后,只为需要通过代理出站的容器设置运行时环境变量。如果你唯一的代理端点是 SOCKS5,运行本地桥接并将 Docker 的 HTTP 代理变量指向它,或者对于直接支持 SOCKS5 的应用,将 SOCKS5 保留在容器内。

这种分层方法避免在所有容器流量中,只有镜像拉取和构建步骤需要时才通过代理发送。它还能让凭据出现在更少的地方。

要点

Docker 代理配置不是一个设置;它是一个守护进程设置、一个构建时参数和一个每容器环境变量。SOCKS5 仅在应用程序支持的地方受支持——Docker 守护进程不支持。通过有意识地配置每一层,并用 docker info、测试拉取和容器内 IP 检查进行验证,你可以保持构建正常工作,而不会泄露 DNS 或将凭据烘焙到镜像中。