Daimon's Blog
主页 归档 关于 RSS 探针 常用工具
主页
归档
关于
RSS
探针
常用工具

Docker 走代理的 3 种姿势:daemon / Compose / host 网络

daimon daimon 2026-03-02 #Docker#代理#Compose#网络

Docker 走代理是个看似简单实则坑很深的话题,因为它有三个完全独立的层级:

  1. docker pull 拉镜像走代理(Docker daemon 的代理)
  2. 容器内程序自己走代理(容器进程的环境变量)
  3. Compose 跨服务怎么访问宿主机代理

每个层级的配置位置都不一样,搞混了排查能查一天。这篇按这三个层级分别讲。

一、整体关系#

graph TD H[宿主机
跑着 mihomo 7890 端口] H --> D[Docker daemon
拉镜像走代理] H --> C[容器内进程
访问外网走代理] D --> R[Docker Hub
GHCR 等 Registry] C --> X[各种 API / 外网]

D -.读.-> S[/etc/systemd/system/docker.service.d/proxy.conf] C -.读.-> E1[容器 environment 环境变量] C -.或读.-> E2[~/.docker/config.json]

层级配置位置影响
Daemon/etc/systemd/system/docker.service.d/proxy.conf拉镜像
容器构建(build)~/.docker/config.json 或 --build-argdocker build 时容器内的网络请求
容器运行(run)-e HTTP_PROXY=... 或 environment:容器进程的 HTTP 客户端

二、场景一:让 docker pull 走代理#

国内服务器拉 Docker Hub 经常超时,最干净的方案不是改镜像源,而是让 daemon 走你的代理。

2.1 创建 systemd drop-in#

Terminal window
1
sudo mkdir -p /etc/systemd/system/docker.service.d
2
sudo vim /etc/systemd/system/docker.service.d/proxy.conf

内容:

1
[Service]
2
Environment="HTTP_PROXY=http://127.0.0.1:7890"
3
Environment="HTTPS_PROXY=http://127.0.0.1:7890"
4
Environment="NO_PROXY=localhost,127.0.0.1,::1,*.aliyun.com,*.tencentyun.com"

💡 NO_PROXY 把国内常用的镜像域名加进去,避免走代理反而慢。

2.2 重启 Docker#

Terminal window
1
sudo systemctl daemon-reload
2
sudo systemctl restart docker
3
4
# 验证
5
sudo systemctl show docker | grep -i proxy

2.3 取消代理#

删掉 proxy.conf 再重启:

Terminal window
1
sudo rm /etc/systemd/system/docker.service.d/proxy.conf
2
sudo systemctl daemon-reload
3
sudo systemctl restart docker

🪤 这个配置只影响 docker pull / docker push,不影响容器内部进程。容器要走代理需要单独配置(场景二)。

三、场景二:让容器内程序走代理#

最常见的需求:容器跑了个程序要访问 GitHub / OpenAI,但你的代理在宿主机上。

3.1 简单粗暴:-e 传环境变量#

Terminal window
1
docker run -d \
2
-e HTTP_PROXY=http://172.17.0.1:7890 \
3
-e HTTPS_PROXY=http://172.17.0.1:7890 \
4
-e NO_PROXY=localhost,127.0.0.1,::1 \
5
myimage

注意:用宿主机 IP 172.17.0.1(默认 bridge 网关),不要写 127.0.0.1(那是容器自己)。

3.2 Compose 写法#

1
services:
2
myapp:
3
image: myimage
4
environment:
5
HTTP_PROXY: http://172.17.0.1:7890
6
HTTPS_PROXY: http://172.17.0.1:7890
7
NO_PROXY: localhost,127.0.0.1,::1

3.3 ~/.docker/config.json(构建时也生效)#

1
{
2
"proxies": {
3
"default": {
4
"httpProxy": "http://172.17.0.1:7890",
5
"httpsProxy": "http://172.17.0.1:7890",
6
"noProxy": "localhost,127.0.0.1,::1"
7
}
8
}
9
}

这种方式对所有 docker build 和 docker run 都自动生效,省去每次写 -e。

⚠️ 程序必须遵守 HTTP_PROXY 环境变量才会走代理。比如 curl / wget / Python 的 requests / Java 都遵守;但有些程序不读这个变量(如 Go 自己实现的 HTTP 客户端、部分 nodejs 库),那就只能在程序代码里改。

四、场景三:宿主机代理在 Compose 网络中”找不到”#

这是 Compose 用户最高频的坑:

用 host.docker.internal 在 Compose 容器里根本访问不到宿主机代理。

4.1 为什么 host.docker.internal 不行?#

1
# 这种写法在 Compose 里基本不工作
2
services:
3
app:
4
image: myimage
5
extra_hosts:
6
- "host.docker.internal:host-gateway"

原理:

  • host-gateway 是 Docker 的魔法关键字,会被解析成默认 bridge 网桥的网关 IP 172.17.0.1。
  • 但 Compose 会为每个项目创建独立网络,网关 IP 是 172.18.0.1 / 172.19.0.1 等,和 172.17.0.1 不在同一段。
  • 结果:容器把 host.docker.internal 解析成 172.17.0.1,但自己网络段是 172.18.0.0/16,根本到不了 172.17.0.1。

4.2 查看你 Docker 上所有网关 IP#

Terminal window
1
docker network inspect $(docker network ls -q) \
2
--format '{{.Name}}: Subnet={{(index .IPAM.Config 0).Subnet}} Gateway={{(index .IPAM.Config 0).Gateway}}'

输出示例:

1
bridge: Subnet=172.17.0.0/16 Gateway=172.17.0.1
2
myapp_default: Subnet=172.18.0.0/16 Gateway=172.18.0.1
3
otherproject_default: Subnet=172.19.0.0/16 Gateway=172.19.0.1

bridge 才是 Docker 默认的;其它都是 Compose 自动创建的。

4.3 正确做法:手动指定 Compose 网络段 + 网关#

1
services:
2
myapp:
3
image: myimage
4
extra_hosts:
5
- "host.docker.internal:172.30.0.1" # 指向下面定义的网关
6
environment:
7
HTTP_PROXY: http://host.docker.internal:7890
8
HTTPS_PROXY: http://host.docker.internal:7890
9
10
networks:
11
default:
12
ipam:
13
config:
14
- subnet: 172.30.0.0/16
15
gateway: 172.30.0.1

关键点:

  1. 自定义网段和网关(172.30.0.0/16,挑一个没被占用的)
  2. extra_hosts 写死这个网关 IP,不要用 host-gateway 魔法值
  3. 容器里访问 host.docker.internal → 172.30.0.1 → 宿主机

4.4 必备:宿主机防火墙放开这个网段#

⚠️ 重要:默认 ufw 会拦截除 127.0.0.1 外所有源(含 Docker 网段)。容器请求宿主机代理会被防火墙挡掉。

Terminal window
1
sudo ufw allow from 172.30.0.0/16
2
sudo ufw reload

或者代理软件层面允许 LAN(如 mihomo 的 allow-lan: true 配合 bind-address: "*")。

4.5 验证容器内能否解析 + 连通#

Terminal window
1
docker exec -it myapp sh
2
3
# 解析
4
getent ahosts host.docker.internal
5
# 应该显示 172.30.0.1
6
7
# 连通
8
nc -zv host.docker.internal 7890
9
# 应该 succeeded

五、environment 代理 vs 应用配置文件代理#

很多人发现:明明 environment 写了 HTTP_PROXY,应用的某些请求还是没走代理——这是因为两者作用层级不同:

层级影响范围
environment 的 HTTP_PROXY进程级,影响通用 HTTP 库(curl、Python requests、Java HttpClient 等)
应用自己的 config.yaml 里的 proxy业务级,应用特定 API 走的代理

举例:某个 AI 网关程序

  • environment: HTTP_PROXY=... → 控制下载管理面板、访问 GitHub API 等”框架请求”
  • config.yaml 里的 proxy → 控制转发到 OpenAI 的”业务请求”

两个都要配,不能互相覆盖。

六、host 网络模式:最简单粗暴的方案#

如果你不想折腾网络段,直接让容器和宿主机共享网络栈:

Terminal window
1
docker run -d --network host myimage
1
services:
2
myapp:
3
image: myimage
4
network_mode: host
5
environment:
6
HTTP_PROXY: http://127.0.0.1:7890 # 直接写 127.0.0.1
7
HTTPS_PROXY: http://127.0.0.1:7890

优点:

  • 127.0.0.1 就是宿主机的 127.0.0.1
  • 不用映射端口(-p 失效)
  • 性能最好(无 NAT)

缺点:

  • 失去网络隔离(容器能看到宿主机所有端口)
  • 多个容器端口冲突(不能再有两个监听 80 的容器)
  • 不能用 Compose 的容器名 DNS(host 模式没有 Compose 网络)

适合:单机部署、性能敏感、不需要多容器互联的场景。

七、Docker 镜像加速(拉镜像的另一条路)#

如果不想配代理,国内可以用镜像加速器:

Terminal window
1
sudo vim /etc/docker/daemon.json
1
{
2
"registry-mirrors": [
3
"https://docker.1ms.run",
4
"https://hub.fast360.xyz",
5
"https://docker.hpcloud.cloud"
6
]
7
}
Terminal window
1
sudo systemctl restart docker
2
docker info | grep -A 10 "Registry Mirrors"

⚠️ 国内可用的镜像源经常被封 / 失效,建议配 3-5 个,失败自动 fallback。这条路适合只想拉镜像、不需要容器内程序走代理的场景。

八、调试代理是否生效#

按”层级”排查:

8.1 daemon 代理生效了吗?#

Terminal window
1
sudo systemctl show docker | grep -i proxy

8.2 镜像拉得动了吗?#

Terminal window
1
docker pull hello-world

8.3 容器能访问宿主机吗?#

Terminal window
1
docker run --rm -it alpine sh
2
# 进容器后
3
apk add curl
4
curl -v http://172.17.0.1:7890

8.4 容器内程序确实读到环境变量了吗?#

Terminal window
1
docker exec myapp env | grep -i proxy

8.5 程序是否遵守环境变量?#

Terminal window
1
docker exec myapp curl -v https://www.google.com
2
# 如果走代理,会看到 Proxy-Connection 之类的头

九、踩坑速查表#

现象原因解决
docker pull 慢daemon 没配代理配 /etc/systemd/system/docker.service.d/proxy.conf
配了 daemon 代理,容器还是慢容器不读 daemon 代理容器需单独 -e HTTP_PROXY
容器内 curl 走代理失败ufw 挡了 Docker 网段ufw allow from <Docker 网段>
Compose 用 host.docker.internal 不通跨网段问题手动指定网段 + 网关
host-gateway 解析成 172.17.0.1 但不通Compose 网络段不是 172.17用上面的”指定网关”方案
环境变量配了程序还是没代理程序不读 HTTP_PROXY在程序代码里改

十、几条实用经验#

  • 能用宿主机代理就别在容器里再装 mihomo——多套一层全是麻烦。
  • 172.17.0.1 只在默认 bridge 网络下可用,Compose 自定义网络要算自己的网关。
  • 代理 + 防火墙是组合拳:代理通了但 ufw 挡了网段,照样不通。
  • docker exec ... env 是排查”环境变量是否生效”的第一招。
  • 混用 daemon 代理 + 容器代理没关系,分别管不同事。

把这三个层级理清楚,后面再遇到任何”Docker 走不通”的问题,定位都是几分钟的事。

1Panel 全教程:用一个面板把 VPS 管理得明明白白
Cloudflare 优选 IP / 优选域名:原理 + 实操 + 避坑
本博客所有文章除特别声明外,均遵循 CC BY-NC-SA 4.0 协议,转载请注明出处。
博客框架 Astro & Fuwari
冀ICP备20260167号
1
一、整体关系
2
二、场景一:让 docker pull 走代理
2.1 创建 systemd drop-in
2.2 重启 Docker
2.3 取消代理
3
三、场景二:让容器内程序走代理
3.1 简单粗暴:-e 传环境变量
3.2 Compose 写法
3.3 ~/.docker/config.json(构建时也生效)
4
四、场景三:宿主机代理在 Compose 网络中”找不到”
4.1 为什么 host.docker.internal 不行?
4.2 查看你 Docker 上所有网关 IP
4.3 正确做法:手动指定 Compose 网络段 + 网关
4.4 必备:宿主机防火墙放开这个网段
4.5 验证容器内能否解析 + 连通
5
五、environment 代理 vs 应用配置文件代理
6
六、host 网络模式:最简单粗暴的方案
7
七、Docker 镜像加速(拉镜像的另一条路)
8
八、调试代理是否生效
8.1 daemon 代理生效了吗?
8.2 镜像拉得动了吗?
8.3 容器能访问宿主机吗?
8.4 容器内程序确实读到环境变量了吗?
8.5 程序是否遵守环境变量?
9
九、踩坑速查表
10
十、几条实用经验