ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Docker代理配置全解析:从daemon.json到BuildKit的逐层排查指南

Docker代理配置全解析:从daemon.json到BuildKit的逐层排查指南 1. 先搞清楚问题Docker的几层网络到底谁在走代理接触过Docker的人大概都有这种经历在网上搜“docker 代理配置”能搜出来一堆答案有让你改/etc/docker/daemon.json的有让你改/etc/systemd/system/docker.service.d/http-proxy.conf的还有说在Docker Desktop设置界面里填的。这些答案都对但问题是你不知道自己该用哪个。原因很简单Docker不是一个单进程工具它是“客户端-守护进程-容器运行时”这套组合。代理要配在哪个环节取决于你当前的瓶颈在哪一层。我习惯把这层关系拆成三个层面来看客户端层也就是你敲docker pull、docker push、docker build这些命令时真正执行命令的dockerCLI 进程。在网络请求层面CLI本身基本不直接发HTTP请求到镜像仓库大部分请求是转交给dockerd去做的。守护进程层dockerd这是最核心的一层。docker pull的镜像拉取、docker push的镜像推送、docker build的构建过程拉基础镜像全部由dockerd直接发起HTTP/HTTPS连接。如果dockerd不解代理镜像下载就会直连默认源。容器运行时层容器启动之后里面的进程要访问外网走的是iptables NAT规则。这一层不是“配置代理”的问题而是容器内进程自己认不认HTTP_PROXY环境变量的问题。明白了这三层你就能理解为什么网上那些配置教程看起来“互相矛盾”——它们讲的其实是不同场景。很多人一上来就改daemon.json发现容器内走代理还是不行就以为是配置没生效其实压根是层数没对应上。另外还有一个概念必须分清代理和镜像加速。镜像加速器比如各种云厂商提供的加速地址是用HTTP缓存的方式帮你拉镜像不改变网络路径代理是改变整个请求的出口路径。两者可以共存但逻辑不同后面我会专门讲。2. 守护进程级代理配置daemon.json与systemd两套方案绝大多数人配置docker代理真实需求都是“让docker pull能拉下来镜像”。这个需求对应的就是守护进程层代理。这里有两条经典路径选哪条取决于你的Docker是怎么装的。2.1 现代版本推荐daemon.json里的proxies配置Docker从版本23.0开始在daemon.json中新增了proxies这个配置段。如果你用的是较新版本这是最干净的方式。{ proxies: { http-proxy: http://192.168.1.100:7890, https-proxy: http://192.168.1.100:7890, no-proxy: localhost,127.0.0.1,*.local } }配置完后重启docker服务sudo systemctl restart docker注意这个配置是给dockerd用它只影响dockerd自己发起的请求也就是镜像拉取、推送、构建时拉基础镜像这几件事。它不会自动传给容器内的进程也不会传给docker build过程中执行的RUN指令所在的环境变量。2.2 旧版本或特定发行版systemd drop-in配置如果你的Docker是20.10及更早版本或者你的系统用的是SysVinit/OpenRC这类非systemd的init系统daemon.json的proxies字段是无效的。这时要走systemd的service配置覆盖方案。创建目录和文件sudo mkdir -p /etc/systemd/system/docker.service.d新建/etc/systemd/system/docker.service.d/http-proxy.conf[Service] EnvironmentHTTP_PROXYhttp://192.168.1.100:7890 EnvironmentHTTPS_PROXYhttp://192.168.1.100:7890 EnvironmentNO_PROXYlocalhost,127.0.0.1,*.local然后重载并重启sudo systemctl daemon-reload sudo systemctl restart docker验证是否生效systemctl show --propertyEnvironment docker这条命令会输出当前docker服务实际生效的Environment变量如果看到你设置的代理地址说明配置已经加载。这一步非常重要因为daemon-reload没做或者文件路径写错都会导致配置静默失效。2.3 折腾多次之后我的选择建议就我个人的经验能用daemon.json就用daemon.json。原因有两个它集中管理和Docker Desktop的配置逻辑一致团队协作时也容易对照。systemd drop-in 方案在有些经过定制化的发行版上docker.service的路径可能被修改过比如某些NAS系统、嵌入式系统上你按标准路径建了文件也不生效排查起来很费劲。另外提醒一点不管哪种方案NO_PROXY里一定要把内网地址段加进去。否则你 push 到自建的企业镜像仓库时请求也会绕道代理。在大型内网环境里这会造成莫名其妙的超时。我见过不止一次因为漏配NO_PROXY导致push内网仓库奇慢无比的情况。no-proxy: localhost,127.0.0.1,*.local,192.168.0.0/16,10.0.0.0/8NO_PROXY支持CIDR格式内网地址段建议直接写全。3. docker build过程中的代理不要忽略BuildKit的坑很多人在这一步栽过跟头守护进程代理配置好了docker pull正常了但docker build时RUN apt-get update仍然超时。这就引出了代理配置的另一个独立维度——构建过程内的代理。3.1 BuildKit与传统builder的代理处理差异Docker从较新的版本开始默认启用BuildKit。BuildKit在代理处理上和老builder有些区别。在传统builder中dockerd的环境变量也就是你在systemd drop-in里设置的HTTP_PROXY会被传给构建容器这导致很多人的构建过程“意外地”能走代理。切换到BuildKit之后这种隐式继承被砍掉了构建容器不再自动继承dockerd的环境变量。这是好事——因为传统builder的隐式继承很不安全会把宿主机的代理IP、端口甚至认证信息泄漏给每一个构建容器。BuildKit修正了这个问题但也意味着你必须在构建时显式传代理。两种典型做法docker build \ --build-arg HTTP_PROXYhttp://192.168.1.100:7890 \ --build-arg HTTPS_PROXYhttp://192.168.1.100:7890 \ --build-arg NO_PROXYlocalhost,127.0.0.1 \ -t my-image .或者在Dockerfile里声明ARGFROM ubuntu:22.04 ARG HTTP_PROXY ARG HTTPS_PROXY ARG NO_PROXY RUN apt-get update apt-get install -y curl推荐用--build-arg在命令行传因为这样构建命令是显式的别人看history或者CI日志都能知道传了什么。把代理写死在Dockerfile里是最不推荐的做法——镜像里的历史层会残留代理地址这在交付镜像时是个隐患。3.2 容器里的代理环境变量与应用的偏好镜像构建好之后容器运行时要访问外网同样需要代理。这一层和前面说的“守护进程代理”完全无关纯粹是容器内进程读不读环境变量的问题。最常见的做法是运行时传入环境变量docker run -d \ -e HTTP_PROXYhttp://192.168.1.100:7890 \ -e HTTPS_PROXYhttp://192.168.1.100:7890 \ -e NO_PROXYlocalhost,127.0.0.1 \ my-app但要注意环境变量传给容器了不代表容器里的应用一定会用。很多应用不走系统代理只认自己的配置文件。典型的例子是Java应用。JVM启动参数里的-Dhttp.proxyHost、-Dhttp.proxyPort是独立于环境变量的另一套配置。你只设了HTTP_PROXY环境变量Java进程完全不理会它还是直连。这种“环境变量设了但应用不生效”的坑排起来非常费时间。所以我的习惯是在容器内测试代理是否生效时不直接用curl而是先确认目标应用读的是什么代理机制。curl认环境变量但你跑的业务程序不一定认。4. 镜像加速器和代理之间的关系两者不是一回事聊Docker网络绕不开镜像加速器。很多人把加速器和代理混为一谈认为“配了加速器就不用配代理”或者“配了代理就不用加速器”其实都不准确。4.1 加速器究竟是什么镜像加速器本质是一个HTTPS反向代理缓存服务。你把它配置为镜像仓库的mirror之后拉镜像的请求首先到达加速器加速器如果没缓存就回源到Docker Hub拉取然后缓存下来下次直接从缓存返回。整个过程不改变你的网络出口路径它只改变你访问的服务器地址。所以如果你的网络访问Docker Hub本身就超时加速器大概率也不快——因为加速器回源时也要连Docker Hub。常见的加速器配置写法{ registry-mirrors: [ https://docker.mirrors.example.com ] }4.2 加速器代理联动的两种场景场景一只配了加速器没配代理。如果加速器地址在你的网络环境里可以直接访问那拉镜像速度会不错。但如果你在一个访问海外站点普遍卡顿的内网环境里加速器本身可能也连不通。这种情况要么换一个内网可达的加速器要么给dockerd配置代理。场景二加速器和代理同时配置。它们并不冲突。请求会先尝试走加速器如果加速器也慢代理会兜底。但这里有一个细节一旦给dockerd配了代理所有通过dockerd发起的HTTPS请求都会走代理包括访问加速器。如果你的加速器是公司内网自建的代理出口在外面那请求就会绕一个很大的圈子。这种时候NO_PROXY的作用就体现出来了。你必须在NO_PROXY里加上内网加速器的地址段让内网流量直连外网流量走代理{ proxies: { http-proxy: http://192.168.1.100:7890, https-proxy: http://192.168.1.100:7890, no-proxy: localhost,127.0.0.1,10.0.0.0/8,192.168.0.0/16,*.internal.example.com }, registry-mirrors: [ https://docker.mirrors.internal.example.com ] }这样配置之后内网加速器走直连Docker Hub走代理各走各的道。4.3 为什么加了代理之后拉镜像反而更慢很多人配置完代理后发现镜像拉取速度不升反降第一反应是“代理没生效”。其实更常见的原因是代理服务器本身的上行带宽不够。镜像可能就几百MB但Docker在拉取时会并发多个连接如果代理服务器的带宽被多个客户端瓜分速度自然上不去。另一个容易被忽略的因素是代理对HTTP/2的支持。Docker Hub支持HTTP/2如果代理服务器只支持HTTP/1.1连接会被降级。表现为docker pull的进度条切换变慢尤其是多manifest列表的镜像比如带多架构的镜像需要下载多个子manifest性能差异会更明显。如果你有能力选型代理服务优先支持HTTP/2、连接复用做得好的实现。如果代理是团队公共的那至少确认一下带宽上限。5. Docker Desktop上的代理配置图形界面也不省心如果你用的是Docker Desktop代理配置有个图形界面但这并不意味着可以高枕无忧。Docker Desktop的代理逻辑和纯命令行环境还有不少差异。5.1 Docker Desktop的代理设置逻辑在Docker Desktop的Settings → Resources → Proxies里可以设置Web Server (HTTP) 和 Web Server (HTTPS) 代理。这个代理是发给Docker虚拟机的相当于给dockerd配了代理作用范围是镜像拉取相关请求。Docker Desktop还支持“Manual proxy configuration”和“Use system proxy”两个选项。选“Use system proxy”时Docker Desktop会读取操作系统的系统代理设置。这里有个坑很多人明明在系统设置里关了代理但Docker Desktop还在用这个选项导致Docker Desktop时不时报网络错误。排查时不妨先去系统设置确认一下。5.2 路径差异带来的困惑Docker Desktop在macOS和Windows上实现方式不同。macOS上的Docker Desktop本质是跑在一个轻量虚拟机里Windows上也跑了WSL2后端。虚拟机和宿主机之间的网络是通过vpnkit或gvisor-tap-vsock这类组件转发的所以代理地址不能写127.0.0.1——这个地址在虚拟机内部指向虚拟机自己你要访问的是宿主机的代理服务。正确的写法是macOS上通常可以用宿主机IP比如http://192.168.1.100:7890或者在某些网络模式下用http://host.docker.internal:7890。Windows上建议直接用http://host.docker.internal:7890这个是Docker Desktop提供的特殊域名自动解析到宿主机IP。如果你在Docker Desktop里配置代理后怎么都不生效先检查是不是把代理地址写成了127.0.0.1。5.3 Docker Desktop和命令行的共存另外要注意Docker Desktop如果正处于运行状态你手动改~/.docker/config.json或者daemon.json里的代理配置Docker Desktop可能会在你下次点击界面时覆盖掉它。Docker Desktop是有自己的配置优先级的它启动时会把自己保存在setting里的配置同步给dockerd。所以我很早前就定了一条规矩在用了Docker Desktop的机器上代理配置只从Docker Desktop界面改不手动改底层文件。反过来在纯命令行环境里就完全用daemon.json方案。两种环境混着配很容易出现“明明看了配置文件没问题但实际不生效”的状态。6. 实测避坑配置完代理后如何快速验证配置完代理不能只看docker pull是否变快因为拉取速度受网络波动影响太大不一定能准确反映代理状态。我习惯用一套更精确的验证步骤。6.1 验证dockerd是否真的走了代理先想办法让dockerd对某个外部地址发一个HTTPS请求然后看代理服务器日志。最直接的办法是拉一个比较小的、而且确定没被缓存的镜像docker pull busybox:latest然后立刻去代理服务器上查访问日志。如果能看到hub.docker.com或相关镜像仓库的访问记录说明代理已生效。如果没有代理服务器日志的查看权限还有一个取巧的办法故意把代理端口改错。比如把daemon.json里的代理端口改成7891假设你的代理实际监听7890然后重启Docker再执行docker pull busybox:latest。如果代理配置生效dockerd会尝试连接7891端口然后报connection refused如果报错信息显示的还是连接Docker Hub超时说明代理配置压根没加载。这个方法很粗暴但用来判断“代理配置是否被dockerd读取”非常有效。验证完记得把端口改回来。6.2 验证容器内代理是否生效容器内验证代理是否生效要在容器里执行一个外部HTTP请求然后观察是否走了代理。比如docker run --rm -e HTTP_PROXYhttp://192.168.1.100:7890 alpine sh -c wget -q -O- https://api.ipify.orgapi.ipify.org会返回你的出口IP。如果返回的IP是代理服务器的出口IP说明代理生效如果返回的是本地公网IP说明代理没走到。不过这里又要强调那个老问题wget或curl验证通过只能证明环境变量传递没问题不代表容器里的业务进程也认。业务进程是否读代理要看你自己的应用配置。6.3 一个容易误导人的细节docker info的输出执行docker info在输出里能看到HTTP Proxy、HTTPS Proxy、No Proxy三行。很多人以为看到这三行有值就代表代理配置成功。这对了一半——它只代表dockerd本地读取到了代理环境变量但不代表代理服务器是通的、也不代表所有请求真走了代理。真正需要关注的是你能否通过代理访问目标仓库。一个快速测试思路是检查证书链路。如果你用的代理服务器做了HTTPS劫持MITMdockerd在拉镜像时可能会报证书校验失败。这种问题排查起来特别隐蔽因为curl命令用系统CA证书能通过但Docker用的是它自己内置的CA列表。遇到拉取时报x509: certificate signed by unknown authority基本就是代理在做SSL拦截而Docker不信任这个拦截证书。6.4 常见报错速查表我把自己遇到过的、以及被问得最多的几类问题整理成一个清单方便排查时对照现象可能原因解决方向docker pull超时dockerd未配置代理或代理地址不可达检查daemon.json/systemd配置从宿主机先curl代理地址测试配置后重启Docker失败daemon.json格式错误docker info或journalctl查看具体报错用docker --config验证JSON拉镜像报x509: certificate signed by unknown authority代理做了SSL拦截Docker不信任其证书让代理关闭对Docker Hub域名的MITM或配置Docker信任对应CA证书容器内curl走代理成功但业务程序不走代理应用不认环境变量走自己的配置查阅应用文档配置应用级代理内网pull镜像提速但push很慢NO_PROXY漏配内网地址段在NO_PROXY中补充内网IP段和自建仓库域名BuildKit下构建时apt-get超时构建容器没有继承宿主机代理使用docker build --build-arg显式传代理Docker Desktop里代理不生效代理地址写成127.0.0.1改为宿主机实际IP或host.docker.internal代理生效但速度极慢代理服务器带宽不足或协议降级确认代理服务器上下行带宽优先支持HTTP/2的代理实现7. 一套比较省心的配置习惯最后聊聊我个人在实际操作中逐渐形成的配置习惯算不上标准答案但帮我在多台机器、多个环境切换时省了不少事。我的基线做法是这样的主机层面统一用daemon.json的proxies字段配置dockerd代理NO_PROXY里固定包含localhost,127.0.0.1、内网主要网段以及公司内部域名避免内网流量绕路。构建传参docker build时永远通过--build-arg显式传HTTP_PROXY和HTTPS_PROXY而不是依赖Dockerfile里写死的ARG默认值确保任何一台机器上构建行为一致。容器运行只在需要访问外网的容器上通过-e传代理环境变量。不需要的容器绝不传减小环境变量泄漏面。Docker Desktop换个环境就用界面配不碰底层文件并且关闭“Use system proxy”选项完全手动指定代理地址。排查问题时我习惯按这个顺序来先确认dockerd读到了代理配置systemctl show或docker info再确认代理服务器本身可达curl测试代理端口再确认代理日志里有没有来自dockerd的请求最后才是抓包看流量走向。按这个链路走基本能在几分钟内定位到问题层。配置docker代理不是一个需要背的命令集合它的核心是弄明白流量在哪一层发出、代理需要在哪一层生效。把这个搞明白不管Docker怎么升级、环境怎么变你都能快速搞定。
返回列表