Docker 代理配置全攻略:Daemon、CLI、容器三层一次搞懂
你是不是也遇到过这些问题?
docker pull 一直超时,配了 HTTP_PROXY 环境变量还是不行。或者容器里用 apt-get install 下不动,明明建容器时传了代理参数。
最让人困惑的是:同样是”配代理”,docker pull 和容器内部联网其实是两回事。配了其中一个,另一个并不会自动好。
Docker 的代理配置分成四个层次,从上到下作用范围递减。看清楚每一层管什么,才能定位”配了不生效”是哪个环节出了问题。
层次一:Daemon 持久化(系统级)
作用范围:docker pull、docker 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 run 和 docker 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_PROXY、HTTPS_PROXY、NO_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.json,docker 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_PROXY 和 HTTPS_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 exec 和 docker 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 模式全局接管