第25章 Docker 里的代理:HTTP_PROXY、build/pull 与 daemon 配置
学习目标
- 分清 Docker 代理的三个独立层面:daemon 拉镜像 / build 构建 / run 运行——搞混是头号坑
- 正确配置各层面的代理,验证
docker pull/build/容器内出网 - 避开容器里
NO_PROXY的集群坑与localhost指向坑 - 理解 registry mirror 本质是镜像仓库的反向代理/缓存
前置知识
原理
三个独立层面:这是最大坑源
"配了 Docker 代理还是不生效"——几乎都是因为没分清这三个互相独立的层面:
① Docker daemon 走代理 → 影响 docker pull/push(dockerd 自己去拉镜像)
② docker build 走代理 → 影响构建时容器内的 apt/npm/pip 等下载
③ 容器运行时走代理 → 影响 docker run 起来的应用出网
它们配置位置完全不同、互不影响。"
docker pull不走代理"要配①,"build时apt装不上"要配②,"容器里应用连不上外网"要配③。先定位是哪个层面,再对症下药。
层面①:daemon 代理(拉镜像)
docker pull 是 dockerd 守护进程发起的,不读你 shell 的环境变量。要给 systemd 管理的 dockerd 配代理,用 systemd drop-in:
# /etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://10.0.0.1:3128"
Environment="HTTPS_PROXY=http://10.0.0.1:3128"
Environment="NO_PROXY=localhost,127.0.0.1,10.0.0.0/8,*.internal"
sudo systemctl daemon-reload && sudo systemctl restart docker
sudo systemctl show --property=Environment docker # 验证生效
层面②③:build 与 run 代理(~/.docker/config.json)
构建和运行容器内的代理,现代用 ~/.docker/config.json 的 proxies,Docker 会自动注入 HTTP_PROXY 等环境变量到 build/run:
{
"proxies": {
"default": {
"httpProxy": "http://10.0.0.1:3128",
"httpsProxy": "http://10.0.0.1:3128",
"noProxy": "localhost,127.0.0.1,*.internal,10.0.0.0/8"
}
}
}
或显式传:
# build 时(老式 --build-arg,需 Dockerfile 里 ARG 声明)
docker build --build-arg HTTP_PROXY=http://10.0.0.1:3128 -t myimg .
# run 时
docker run -e HTTP_PROXY=http://10.0.0.1:3128 -e NO_PROXY=localhost myimg
localhost 陷阱:容器里的 localhost 不是宿主
容器有独立网络命名空间(容器手册)。容器里的 127.0.0.1 是容器自己,不是宿主机。所以代理跑在宿主机时:
- ❌
HTTP_PROXY=http://127.0.0.1:3128——容器里这指向容器自己,连不上 - ✅ 用宿主 IP,或
host.docker.internal(Docker Desktop 自带;Linux 上需--add-host=host.docker.internal:host-gateway)
NO_PROXY 的容器/集群坑(呼应第08章)
第08章 强调过:设了代理一定要把不该走代理的内网目标加进 NO_PROXY。容器场景尤其要排除:
localhost,127.0.0.1 # 本机
172.17.0.0/16 # Docker 默认网桥网段(容器间)
host.docker.internal # 宿主
私有 registry 域名/IP # 拉私有镜像不该走外网代理
.svc,.cluster.local # K8s 集群内(到了 K8s 更要命,见第26-28章)
169.254.169.254 # 云元数据(绝不能走外部代理)
漏了集群内网段,会导致"容器访问内部服务/registry 全部超时"——一个超高频故障。
registry mirror:镜像仓库的反向代理/缓存
国内拉 Docker Hub 慢,常配 registry mirror——这本质是给镜像仓库做了个反向代理 + 缓存(第10章、第28章):
// /etc/docker/daemon.json
{ "registry-mirrors": ["https://mirror.example.com"] }
docker pull nginx 会先问 mirror,mirror 缓存命中直接给、未命中回源 Docker Hub 并缓存——和 Squid 缓存(第17章)一个道理。这是 第28章 Egress 出网治理 的常用手段。
️ 实现 / 命令
实验一:配 daemon 代理并验证拉镜像
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="HTTPS_PROXY=http://10.0.0.1:3128"
Environment="NO_PROXY=localhost,127.0.0.1,10.0.0.0/8,registry.internal"
EOF
sudo systemctl daemon-reload && sudo systemctl restart docker
# 抓代理日志能看到 docker pull 的 CONNECT 请求(第04章)
docker pull alpine # 经代理拉取
实验二:验证三层面互相独立
# 只配了 daemon 代理时:
docker pull alpine # ✅ 走代理(层面①)
docker run --rm alpine wget -qO- http://example.com # ❌ 容器内不走代理(层面③没配)
# 要让容器内出网,得另配 config.json proxies 或 -e HTTP_PROXY(层面③)
这个对比一眼说明:三层面独立,配了一个不等于配了全部。
实验三:容器内代理指向宿主(localhost 陷阱)
# 代理在宿主 3128,容器内要用 host-gateway 而非 127.0.0.1
docker run --rm --add-host=host.docker.internal:host-gateway \
-e HTTP_PROXY=http://host.docker.internal:3128 \
alpine wget -qO- http://example.com
实验四:registry mirror 加速
sudo tee /etc/docker/daemon.json >/dev/null <<'EOF'
{ "registry-mirrors": ["https://mirror.example.com"] }
EOF
sudo systemctl restart docker
docker pull nginx # 经 mirror 拉取(缓存命中则秒回)
docker info | grep -A2 "Registry Mirrors" # 验证生效
排错
| 现象 | 根因 | 解决 |
|---|---|---|
docker pull 不走代理 | 配错层面(配了 shell env 而非 daemon) | 配 systemd drop-in(层面①) |
build 时 apt/npm 装不上 | build 没代理 | config.json proxies 或 --build-arg(层面②) |
| 容器内应用连不上外网 | run 没代理 | -e HTTP_PROXY 或 config.json(层面③) |
容器内代理 127.0.0.1 连不上 | localhost 指向容器自己 | 用 host.docker.internal/宿主 IP |
| 拉私有 registry 走了外网代理超时 | NO_PROXY 没含 registry | 把 registry 域名/IP 加进 NO_PROXY |
| 访问容器间服务超时 | NO_PROXY 没含 172.17/16 | 补容器网段 |
https_proxy=https:// 报错 | scheme 写错(第08章) | 改 http://代理 |
本章小结
- Docker 代理分三个独立层面:daemon(pull/push)、build(构建内)、run(运行内)——搞混是头号坑。
- daemon 配 systemd drop-in;build/run 配
~/.docker/config.jsonproxies 或-e/--build-arg。 - 容器里
localhost是容器自己,代理指宿主用host.docker.internal;NO_PROXY必须含容器/集群网段。 - registry mirror 是镜像仓库的反向代理+缓存,属 Egress 治理手段。
下一章 第26章 Sidecar 与流量劫持,进入 K8s 核心:Istio 如何用 init-container + iptables 把 Pod 流量零侵入地劫持给 Envoy——这是 第11章透明代理 在 K8s 的终极落地。