Docker 代理配置指的是:对单个容器、docker-compose 服务或 Docker daemon 本身进行配置,使其在拉取镜像、构建或从应用内部访问外部资源等场景中,将出站网络流量经由中间代理服务器转发。
为什么要在 Docker 中使用代理?
在 Docker 环境中集成代理可以满足以下几类常见需求:
- 上网访问管控: 在企业网络或受限网络中,构建服务器或容器可能被禁止直接访问互联网。代理充当受控网关。
- 安全扫描与过滤: 代理可以拦截并扫描容器的出站流量,检测恶意内容或执行内容策略。
- 缓存: 代理可以缓存被频繁访问的外部资源(例如包管理器仓库、基础镜像),降低带宽消耗并加快构建和下载速度。
- 匿名与解除地域限制: 虽然在企业环境中较少见,但代理可以隐藏源 IP 或绕过地域限制。
代理配置的类型
Docker 中使用代理主要有两种场景:
- Docker daemon 代理: 配置 Docker daemon 本身,使其在拉取基础镜像、访问远程 registry 或构建过程中(例如
RUN apt-get update这类命令)使用代理。 - 容器/服务代理: 配置单个容器或
docker-compose中的服务,使其在运行时为自身的出站应用流量使用代理。
这两种配置相互独立,完整的方案通常两者都需要。
Docker daemon 代理配置
该方法可确保 Docker daemon 的操作(包括拉取镜像和部分构建步骤)经由指定代理。
基于 systemd 的 Linux 主机
大多数现代 Linux 发行版使用 systemd。为 Docker 服务创建 drop-in 目录:
sudo mkdir -p /etc/systemd/system/docker.service.d
在该目录中创建配置文件(例如 http-proxy.conf):
sudo nano /etc/systemd/system/docker.service.d/http-proxy.conf
添加以下内容,并将 YOUR_PROXY_HOST 和 YOUR_PROXY_PORT 替换为您的代理信息:
[Service]
Environment="HTTP_PROXY=http://YOUR_PROXY_HOST:YOUR_PROXY_PORT"
Environment="HTTPS_PROXY=http://YOUR_PROXY_HOST:YOUR_PROXY_PORT"
Environment="NO_PROXY=localhost,127.0.0.1,::1,YOUR_DOCKER_NETWORK_CIDR,YOUR_INTERNAL_DOMAINS"
注意:
* 由于 TLS 协商由代理服务器完成,即使是 HTTPS 流量,HTTP_PROXY 和 HTTPS_PROXY 通常也应使用 http:// 协议前缀。
* NO_PROXY 至关重要。它可以让发往指定主机或网络的流量不经过代理。请加入 localhost、127.0.0.1、::1 以及所有 Docker 内部网络(例如默认 bridge 网络的 172.17.0.0/16,或自定义网络的 CIDR),以确保容器之间以及容器与主机之间能够正常通信。此外,还应加入所有需要绕过代理的内部域名。
重新加载 systemd 并重启 Docker daemon 使配置生效:
sudo systemctl daemon-reload
sudo systemctl restart docker
拉取一个镜像进行验证:
docker pull alpine
如果代理配置正确,该拉取操作将经由代理完成。
Docker CLI(客户端配置)
该配置影响 Docker 客户端与 registry 的交互。它较少用于 daemon 级别的代理,但适用于特定的 CLI 操作。
创建或编辑 ~/.docker/config.json:
{
"proxies": {
"default": {
"httpProxy": "http://YOUR_PROXY_HOST:YOUR_PROXY_PORT",
"httpsProxy": "http://YOUR_PROXY_HOST:YOUR_PROXY_PORT",
"noProxy": "localhost,127.0.0.1,::1,YOUR_DOCKER_NETWORK_CIDR,YOUR_INTERNAL_DOMAINS"
}
}
}
该配置对运行 Docker CLI 的用户生效。
容器/服务代理配置
该方法配置特定容器或 docker-compose 服务,使其运行时的出站流量走代理。它与 daemon 代理不同,容器内运行的应用要通过代理访问外部资源时通常必须这样配置。
使用 Dockerfile
您可以直接在 Dockerfile 中设置代理环境变量。这会把代理设置固化进镜像。
FROM ubuntu:latest
# 为构建期操作设置代理(例如 apt-get update)
ENV HTTP_PROXY="http://YOUR_PROXY_HOST:YOUR_PROXY_PORT" \
HTTPS_PROXY="http://YOUR_PROXY_HOST:YOUR_PROXY_PORT" \
NO_PROXY="localhost,127.0.0.1,::1,YOUR_DOCKER_NETWORK_CIDR,YOUR_INTERNAL_DOMAINS"
RUN apt-get update && apt-get install -y curl
# 除非被覆盖,这些 ENV 变量在运行时同样存在
CMD ["curl", "http://example.com"]
注意事项:
* 在 Dockerfile 中写入代理凭据存在安全风险。建议改用构建参数(ARG)或运行时环境变量(-e 或 docker-compose.yml 中的 environment)。
* 如果仅在构建时需要代理,可在多阶段构建的构建阶段末尾清除这些变量,或使用 RUN --mount=type=secret,id=proxy,target=/run/secrets/proxy-creds 以更安全的方式处理。
使用 docker run
对于单个容器,通过 -e 参数传入代理设置:
docker run -e HTTP_PROXY="http://YOUR_PROXY_HOST:YOUR_PROXY_PORT" \
-e HTTPS_PROXY="http://YOUR_PROXY_HOST:YOUR_PROXY_PORT" \
-e NO_PROXY="localhost,127.0.0.1,::1,172.17.0.0/16,my-app-service" \
my-image:latest curl http://example.com
使用 docker-compose.yml
对于多服务应用,docker-compose 是标准做法。在每个需要代理访问的服务的 environment 部分定义代理环境变量:
version: '3.8'
services:
webapp:
image: my-webapp:latest
environment:
- HTTP_PROXY=http://YOUR_PROXY_HOST:YOUR_PROXY_PORT
- HTTPS_PROXY=http://YOUR_PROXY_HOST:YOUR_PROXY_PORT
- NO_PROXY=localhost,127.0.0.1,::1,your-db-service,your-cache-service,172.18.0.0/16 # 加入内部服务名和网络 CIDR
# ... 其他服务配置
database:
image: postgres:13
# 如果该服务只在内部通信或没有出站流量,通常不需要代理设置
# ... 其他服务配置
关于 docker-compose 中 NO_PROXY 的说明:
* 加入 localhost、127.0.0.1、::1。
* 加入同一 docker-compose 网络中其他服务的名称(例如 your-db-service)。
* 加入 docker-compose 创建的 Docker 网络的 CIDR 段(例如网络处于该范围时为 172.18.0.0/16)。可以用 docker network inspect <network_name> 查看网络的 CIDR。
外部 proxy.env 文件
为了更好地管理敏感或经常变动的代理设置,可以把它们写在外部 .env 文件中,并在 docker-compose.yml 中引用:
proxy.env:
HTTP_PROXY=http://YOUR_PROXY_HOST:YOUR_PROXY_PORT
HTTPS_PROXY=http://YOUR_PROXY_HOST:YOUR_PROXY_PORT
NO_PROXY=localhost,127.0.0.1,::1,your-db-service,your-cache-service,172.18.0.0/16
docker-compose.yml:
version: '3.8'
services:
webapp:
image: my-webapp:latest
env_file:
- ./proxy.env
# ... 其他服务配置
这种方式更清晰,无需直接修改 docker-compose.yml 即可轻松更新。
SOCKS 代理配置
虽然 HTTP/HTTPS 代理最为常见,但有些环境使用 SOCKS 代理。Docker 并不像支持 HTTP/HTTPS 那样原生、普遍地支持 SOCKS 代理环境变量(SOCKS_PROXY、ALL_PROXY)。
- 应用层: 大多数应用需要显式的 SOCKS 支持。如果容器内的应用支持 SOCKS,可以传入
ALL_PROXY或类似变量。 - Proxychains-NG: 对于不原生支持 SOCKS 的应用,可以在容器内安装
proxychains-ng之类的工具,强制流量走 SOCKS 代理。这会带来额外的复杂度和开销。
在典型的 Docker 部署中,由于支持面更广,通常优先使用 HTTP/HTTPS 代理。
各种代理配置方式的对比
| 方式 | 作用范围 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|---|
Docker daemon(systemd) |
主机范围内的 Docker daemon 操作 | 适用于主机上所有镜像拉取、构建步骤和 registry 交互。 | 对 daemon 统一生效,确保所有构建/拉取都走代理。 | 需要了解 systemd;需要重启 daemon;不影响容器内运行的应用。 |
Docker CLI(config.json) |
用户级别的 Docker CLI 操作 | 适用于某个用户与 registry 交互的 CLI 命令。 | 仅对该用户生效,不影响其他用户或 daemon。 | 只影响 CLI,不影响 daemon 或容器运行时。 |
Dockerfile(ENV) |
镜像构建期与容器运行时 | 当同一镜像的所有实例代理设置一致时。 | 设置内置于镜像,开发时简单。 | 设置被固化进镜像(灵活性差);包含凭据时有安全风险。 |
docker run(-e) |
单个容器运行时 | 适用于临时测试、特定容器运行,或覆盖 Dockerfile 中的设置。 | 非常灵活,针对具体运行实例。 | 面对多个容器或 docker-compose 场景时较为繁琐。 |
docker-compose.yml(environment/env_file) |
按服务的容器运行时 | 适用于各服务代理需求不同的多服务应用。 | 按服务清晰定义设置;支持外部 .env 文件以管理密钥并提升灵活性。 |
需要为每个服务分别定义;不影响 daemon 级操作(例如 docker-compose build)。 |
常见代理问题排查
- 确认环境变量: 在运行中的容器内,使用
docker exec -it <container_id> env检查HTTP_PROXY、HTTPS_PROXY和NO_PROXY是否设置正确。 - 从容器测试连通性:
bash docker exec -it <container_id> curl -v --proxy <YOUR_PROXY_HOST:YOUR_PROXY_PORT> http://example.com
这会强制curl使用代理并绕过容器环境变量,便于直接测试。 - 查看代理服务器日志: 检查代理服务器的访问日志,确认来自 Docker 主机或容器的流量是否到达并被处理。
- 网络连通性: 确保 Docker 主机能通过指定端口访问代理服务器。可从 Docker 主机使用
curl或telnet测试。 - 防火墙规则: 确认没有防火墙(主机、网络或代理服务器上的)阻断 Docker 主机/容器与代理服务器之间的流量。
- DNS 解析: 确保容器内 DNS 解析正常,尤其是代理主机本身和目标域名的解析。
NO_PROXY配置:NO_PROXY设置错误或不完整常常导致内部通信失败。请确保列出所有内部网络、Docker bridge 网络和服务名称。
