Docker 代理配置全攻略:Daemon、CLI、容器三层一次搞懂

2026-07-19

Docker 代理配置全攻略:Daemon、CLI、容器三层一次搞懂

你是不是也遇到过这些问题?

docker pull 一直超时,配了 HTTP_PROXY 环境变量还是不行。或者容器里用 apt-get install 下不动,明明建容器时传了代理参数。

最让人困惑的是:同样是”配代理”,docker pull 和容器内部联网其实是两回事。配了其中一个,另一个并不会自动好。

Docker 的代理配置分成四个层次,从上到下作用范围递减。看清楚每一层管什么,才能定位”配了不生效”是哪个环节出了问题。

层次一:Daemon 持久化(系统级)

作用范围docker pulldocker build 拉取基础镜像,以及 dockerd 发起的网络请求。

什么时候需要:如果 docker pull nginx 都一直卡在 Waiting,就得配这一层。

有两种配法。

方式 A:daemon.json(推荐)

创建或编辑 /etc/docker/daemon.json

{
  "proxies": {
    "http-proxy": "http://proxy.example.com:7890",
    "https-proxy": "http://proxy.example.com:7890",
    "no-proxy": "localhost,127.0.0.1,.internal.registry.com"
  }
}

重启 dockerd 生效:

sudo systemctl restart docker

这是 Docker 官方推荐的 daemon 级代理方案。写入后,dockerd 的所有出站连接都会走代理。

方式 B:systemd 环境变量

如果不想用 daemon.json,或者需要更细粒度的控制:

# /etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://proxy.example.com:7890"
Environment="HTTPS_PROXY=http://proxy.example.com:7890"
Environment="NO_PROXY=localhost,127.0.0.1,.corp"

生效需要先重载 systemd 再重启 docker:

sudo systemctl daemon-reload
sudo systemctl restart docker

Rootless 模式下路径不同:~/.config/systemd/user/docker.service.d/

Docker Desktop 用户注意

daemon.json 中的 proxies 在 Docker Desktop 上不生效。需要在 Docker Desktop 的设置界面中配置代理,和 Linux 版的路径不一样。

层次二:CLI 客户端配置(用户级)

作用范围:用户启动的 docker rundocker build

什么时候需要:不想动系统级配置,但希望自己启动的所有容器默认走代理。

配置写在 ~/.docker/config.json 中:

{
  "proxies": {
    "default": {
      "httpProxy": "http://proxy.example.com:7890",
      "httpsProxy": "http://proxy.example.com:7890",
      "noProxy": "*.test.example.com,.example.org,127.0.0.0/8"
    }
  }
}

不需要重启,保存即生效。

原理是:Docker CLI 在创建容器时自动注入 HTTP_PROXYHTTPS_PROXYNO_PROXY 三个环境变量。docker build 也会自动设置对应的 build args。

重要:这只对新容器生效。 已经运行的容器就算重启也不会获得这些变量。

层次三:容器内环境变量(容器级)

作用范围:特定容器内部的出站流量。

什么时候需要:有的容器需要代理,有的不需要。或者代理容器本身不需要配代理。

docker run

docker run -d --name myapp \
  -e HTTP_PROXY=http://host:7890 \
  -e HTTPS_PROXY=http://host:7890 \
  myimage

docker-compose

services:
  myapp:
    image: myimage
    environment:
      - HTTP_PROXY=http://host:7890
      - HTTPS_PROXY=http://host:7890

临时单次命令

有时只是临时拉一个镜像,不需要持久配置:

HTTP_PROXY=http://proxy:7890 HTTPS_PROXY=http://proxy:7890 \
  docker pull myimage:tag

层次四:Build 构建代理(构建级)

作用范围docker build 过程中 RUN 指令的出站流量。

什么时候需要:构建过程中需要下载依赖(apt-get、pip、npm 等)。

docker build \
  --build-arg HTTP_PROXY=http://host:7890 \
  --build-arg HTTPS_PROXY=http://host:7890 \
  -t myapp .

如果你已经配了 ~/.docker/config.jsondocker build 会自动设置这些 build args,不需要每次手动传。

不要用 Dockerfile 中的 ENV 指令设代理:

# ❌ 代理地址会固化到镜像中
ENV HTTP_PROXY=http://host:7890

# ✅ 用 build arg 替代
ARG HTTP_PROXY
RUN some-command

Docker 官方明确建议:不要用 ENV 设代理,用 build arguments 替代。镜像可能被分发到不同环境运行,固化的内网代理地址既泄漏网络拓扑,又会导致环境切换后连接失败。这个原则同样适用于其他环境相关的配置——数据库地址、API 端点等都不应该用 ENV 固化。

对比总览

维度 daemon.json ~/.docker/config.json 容器环境变量 build args
影响范围 dockerd 全部 新容器 + 构建 特定容器 构建过程
配置后是否重启
影响运行中容器 不适用
粒度 全局 用户级别 每个容器 每次构建

Tun 模式:另一种思路

如果不想折腾层层代理配置,还有一种更彻底的方案——Tun 模式。

代理客户端(Clash、Mihomo 等)在系统层创建一个虚拟网卡,全局接管流量。排除 docker0 网桥后,所有容器流量透明地走代理,不用设置任何环境变量:

# 代理客户端 Tun 模式配置示例
tun:
  enable: true
  exclude-interface:
    - docker0

优点:配一次永久生效,不挑容器类型,新增容器自动走代理。

缺点:需要代理客户端支持 Tun 模式,部分云服务器可能需要额外配置。

五个真实踩坑记录

下面这些坑不是理论推演,是实际使用中反复遇到的问题。

坑 1:容器内 127.0.0.1 不指向宿主机

现象:容器内设 HTTP_PROXY=http://127.0.0.1:7890,应用连不上代理。

原因:每个容器有独立的网络命名空间。127.0.0.1 指向容器自己,不是宿主机。

解决

  • 默认 bridge 网络 → 宿主机地址是 172.17.0.1(可以通过 ip route | grep default 确认)
  • --network host → 容器直接用宿主机网络栈,127.0.0.1 指向宿主机
  • 自定义网络 → 用容器名或服务名通信

坑 2:docker pull 和容器内部是两层独立的代理需求

现象:容器里配了 HTTP_PROXY 能正常上网,但 docker pull 还是超时。

原因docker pull 由 dockerd 执行,不继承容器内的环境变量。这是两个完全独立的进程。

解决:同时配 daemon 级代理(让 dockerd 能拉镜像)和容器级代理(让容器内能联网)。

坑 3:NO_PROXY 漏配置

现象:内网 Docker registry 拉镜像也走了代理,超时报错。

原因:只配了 HTTP_PROXYHTTPS_PROXY,没配 NO_PROXY

解决:把内网地址段和域名加到 NO_PROXY

{
  "proxies": {
    "http-proxy": "http://proxy:7890",
    "https-proxy": "http://proxy:7890",
    "no-proxy": "localhost,127.0.0.1,.internal-registry.com,192.168.0.0/16"
  }
}

坑 4:ENV 把代理地址写死到镜像里

现象:镜像换了个环境运行,容器内应用还在连之前的内网代理地址。

原因:Dockerfile 里用了 ENV HTTP_PROXY=http://old-proxy:7890,代理地址被打包进了镜像层。

解决:始终用 --build-arg 在构建时注入,不要用 ENV 固化到镜像。这个原则同样适用于数据库地址、API 端点等与环境绑定的配置。

坑 5:配错代理导致容器 Crashloop

现象:代理地址配错后容器不断重启。docker execdocker cp 都用不了,陷入死循环。

解决:直接修改容器的数据卷配置文件:

# 找到数据卷的宿主机路径
docker volume inspect myapp-data
# 输出中有 Mountpoint 字段
# /var/lib/docker/volumes/myapp-data/_data/
# 直接编辑配置文件修复代理地址

这是 Crashloop 状态下最直接的恢复路径。建议配代理前先在宿主机上测试代理地址是否可达,再应用到容器配置。

一句话决策指南

  • 只想让 docker pull 镜像不超时 → 配 daemon.json
  • 希望所有新容器自动走代理,但不想改系统配置 → 配 ~/.docker/config.json
  • 只让某个特定容器走代理docker run -e HTTP_PROXY=...
  • 构建时需要下依赖--build-arg(不要用 ENV
  • 不想配任何环境变量 → 用 Tun 模式全局接管