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 端点,你有三种实用选择:
- 运行本地 HTTP 到 SOCKS5 桥接。一个小型转发器监听
127.0.0.1:3128并转发到你的 SOCKS5 代理。然后把 Docker 的 HTTP_PROXY 指向该桥接。这样可以将 SOCKS5 凭据排除在 Docker 环境之外。 - 使用 SSH 动态转发加桥接。
ssh -D 1080为你提供一个本地 SOCKS5 代理;桥接将其转换为 HTTP 供 Docker 使用。当你已经可以通过 SSH 访问远程主机时,这非常有效。 - 配置容器内的应用直接使用 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 或将凭据烘焙到镜像中。