[Docker] 如何给 Docker 配置网络代理?

有时因为网络原因,比如公司 NAT,或其它啥的,需要使用代理。Docker 的代理配置,略显复杂,因为有三种场景。但基本原理都是一致的,都是利用 Linux 的 http_proxy 等环境变量。

概述:Docker 网络代理方案

方案1:Dockerd 代理 := systemd 代理

  • 在执行docker pull时,是由守护进程dockerd来执行。

因此,代理需要配在dockerd的环境中。而这个环境,则是受systemd所管控,因此实际是systemd的配置。

sudo mkdir -p /etc/systemd/system/docker.service.d
sudo touch /etc/systemd/system/docker.service.d/proxy.conf

ls -la /etc/systemd/system/docker.service.d/

在这个proxy.conf文件(可以是任意*.conf的形式)中,添加以下内容:

[Service]
Environment="HTTP_PROXY=http://proxy.example.com:8080/"
Environment="HTTPS_PROXY=http://proxy.example.com:8080/"
Environment="NO_PROXY=localhost,127.0.0.1,.example.com"

其中,http://proxy.example.com:8080 要换成可用的免密代理。

通常使用 cntlm 在本机自建免密代理,去对接公司的代理。可参考《Linux下安装配置Cntlm 代理》。

补充参考(了解即可)

# 新增环境变量提供代理服务器信息
Environment="HTTP_PROXY=http://<user>:<password>@<domain>:<port>"
Environment="HTTPS_PROXY=http://<user>:<password>@<domain>:<port>"
# 如果使用了国内镜像源可以配置镜像服务器不使用代理
Environmeng="NO_PROXY=<registry.domain>"
ExecStart=...

注意:尖括号<>中的内容需要替换为自己的代理服务器信息

  • 随后刷新配置
systemctl daemon-reload

此时,可以通过以下指令检查配置是否加载成功:

systemctl show --property Environment docker

# 输出
Environment=HTTP_PROXY=http://<user>:<password>@<domain>:<port> HTTPS_PROXY=http://<user>:<password>@<domain>:<port> NO_PROXY=<registry.domain>
  • 重启Docker服务,使配置生效
systemctl restart docker
  • 重启成功之后,通过docker info指令查看docker服务中的代理配置
docker info | grep Proxy

# 输出
 HTTP Proxy: http://<user>:<password>@<domain>:<port>
 HTTPS Proxy: http://<user>:<password>@<domain>:<port>
 No Proxy: <registry.domain>

此时再使用docker pull指令拉取镜像时Docker服务会使用代理服务器拉取镜像。

【补充说明】此外,systemd也会从/etc/systemd/system/docker.service.d/lib/systemd/system/docker.service.d文件夹下读取配置。
所以,可以再其中一个文件夹中创建一个名为http-proxy.conf的文件用来保存代理信息。内容如下:

[Service]
Environment="HTTP_PROXY=http://<user>:<password>@<domain>:<port>"
Environment="HTTPS_PROXY=http://<user>:<password>@<domain>:<port>"
Environmeng="NO_PROXY=<registry.domain>"

方案2:Container 代理(daemon.json/config.json)

config.json 版

  • 容器运行阶段,如果需要代理上网,则需要配置 ~/.docker/config.json

以下配置,只在Docker 17.07及以上版本生效。

//demo1
{
 "proxies":
 {
   "default":
   {
     "httpProxy": "http://proxy.example.com:8080",
     "httpsProxy": "http://proxy.example.com:8080",
     "noProxy": "localhost,127.0.0.1,.example.com"
   }
 }
}

//demo2
{
  "auths": {
    "..."
  },
  "proxies": {
    # 通用配置,会对当前客户端连接的所有Docker服务生效
    "default": {
      "httpProxy": "http://proxy.example.com:3128",
      "httpsProxy": "https://proxy.example.com:3129",
      "noProxy": "*.test.example.com,.example.org,127.0.0.0/8"
    },
    # 如果只对某个Docker服务时配置代理,则需要通过 docker-host: proxy-settings的方式在下面配置 
    "tcp://docker-daemon1.example.com": {
      "noProxy": "*.internal.example.net"
    }
  }
}
  • 验证
docker run --rm alpine sh -c 'env | grep -i  _PROXY'

# 输出
HTTPS_PROXY=https://proxy.example.com:3129
no_proxy=*.test.example.com,.example.org,127.0.0.0/8
NO_PROXY=*.test.example.com,.example.org,127.0.0.0/8
https_proxy=https://proxy.example.com:3129
http_proxy=http://proxy.example.com:3128
HTTP_PROXY=http://proxy.example.com:3128

这个是用户级的配置,除了 proxies,docker login 等相关信息也会在其中。

而且还可以配置信息展示的格式、插件参数等。

daemon.json 版

  • 或者:在/etc/docker/daemon.json中增加代理配置:
{
  "registry-mirrors": ["..."],
  "proxies": {
    "http-proxy": "http://<user>:<password>@<domain>:<port>",
    "https-proxy": "http://<user>:<password>@<domain>:<port>",
    "no-proxy": "<registry.domain>"
  }
}
  • 重启Docker服务:
systemctl restart docker
  • 检查配置是否生效:
docker info| grep Proxy

# 输出
 HTTP Proxy: http://<user>:<password>@<domain>:<port>
 HTTPS Proxy: http://<user>:<password>@<domain>:<port>
 No Proxy: <registry.domain>

注意:通过daemon.json方式配置的优先级会高于通过systemd配置。

docker run --env HTTP_PROXY ...命令行配置代理版

  • 此外,容器的网络代理,也可以直接在其运行时通过 -e(--env) 注入 http_proxy 等环境变量。
docker run --env HTTP_PROXY="http://<user>:<password>@<domain>:<port>" <some-image>
  • 验证:
docker run \
    --env HTTP_PROXY="http://<user>:<password>@<domain>:<port>" \
    --rm alpine sh -c 'env | grep -i  _PROXY'
# 输出
HTTP_PROXY=http://<user>:<password>@<domain>:<port>

这几种种方法分别适合不同场景。

config.json 非常方便,默认在所有配置修改后启动的容器生效,适合个人开发环境。

在CI/CD的自动构建环境、或者实际上线运行的环境中,这种方法就不太合适,用 -e 注入这种显式配置会更好,减轻对构建、部署环境的依赖。

当然,在这些环境中,最好用良好的设计避免配置代理上网

方案3:Docker Build 代理

  • 虽然 docker build 的本质,也是启动一个容器,但是环境会略有不同,用户级配置无效。

  • 在构建时,需要注入 http_proxy 等参数。

//demo0
docker build --build-arg HTTP_PROXY="http://proxy.example.com:3128"


//demo1
docker build . \
    --build-arg "HTTP_PROXY=http://proxy.example.com:8080/" \
    --build-arg "HTTPS_PROXY=http://proxy.example.com:8080/" \
    --build-arg "NO_PROXY=localhost,127.0.0.1,.example.com" \
    -t your/image:tag


//demo2
docker build \
  --no-cache \
  --progress=plain \
  - <<EOF
FROM alpine

RUN env | grep -i _PROXY
EOF
# 输出
# ...
#5 [2/2] RUN env | grep -i _PROXY
#5 0.382 HTTPS_PROXY=https://proxy.example.com:3129
#5 0.382 no_proxy=*.test.example.com,.example.org,127.0.0.0/8
#5 0.382 NO_PROXY=*.test.example.com,.example.org,127.0.0.0/8
#5 0.382 https_proxy=https://proxy.example.com:3129
#5 0.382 http_proxy=http://proxy.example.com:3128
#5 0.382 HTTP_PROXY=http://proxy.example.com:3128
#5 DONE 0.6s
# ...

验证

docker build --build-arg HTTP_PROXY="http://another-proxy.example.com:3128" \
  --no-cache \
  --progress=plain \
  - <<EOF
FROM alpine
RUN env | grep -i _PROXY
EOF
# 输出
# ...
#5 [2/2] RUN env | grep -i _PROXY
#5 0.393 HTTPS_PROXY=https://proxy.example.com:3129
#5 0.393 no_proxy=*.test.example.com,.example.org,127.0.0.0/8
#5 0.393 NO_PROXY=*.test.example.com,.example.org,127.0.0.0/8
#5 0.393 https_proxy=https://proxy.example.com:3129
#5 0.393 http_proxy=http://another-proxy.example.com:3128
#5 0.393 HTTP_PROXY=http://another-proxy.example.com:3128
#5 DONE 0.6s
# ...

注意:无论是 docker run 还是 docker build,默认是网络隔绝的。

如果代理使用的是 localhost:3128 这类,则会无效。

这类仅限本地的代理,必须加上 --network host 才能正常使用。

而一般则需要配置代理的外部IP,而且代理本身要开启 Gateway 模式。

  • 重启生效
  • 代理配置完成后,reboot 重启当然可以生效,但不重启也行。
  • docker build 代理是在执行前设置的,所以修改后,下次执行立即生效
  • Container 代理的修改也是立即生效的,但是只针对以后启动的 Container,对已启动的 Container 无效。
  • dockerd 代理的修改比较特殊,它实际上是改 systemd 的配置,因此需要重载 systemd 并重启 dockerd 才能生效。
sudo systemctl daemon-reload
sudo systemctl restart docker

参考文献:如何配置docker通过代理服务器拉取镜像?

  • 如果 docker 所在的环境是通过代理服务器和互联网连通的,那么需要一番配置才能让 docker 正常从外网正常拉取镜像。

  • 然而,仅仅通过配置环境变量的方法是不够的。本文结合已有文档,介绍如何配置代理服务器能使docker正常拉取镜像。

本文使用的docker 版本是

docker --version
Docker version 24.0.2, build cb74dfc

问题现象

如果不配置代理服务器就直接拉镜像,docker 会直接尝试连接镜像仓库,并且连接超时报错。如下所示:

$ docker pull busybox
Using default tag: latest
Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled 
while waiting for connection (Client.Timeout exceeded while awaiting headers)

问题分析

容易误导的官方文档

有这么一篇关于 docker 配置代理服务器的 官方文档 ,如果病急乱投医,直接按照这篇文章配置,是不能成功拉取镜像的。

我们来理解一下这篇文档,文档关键的原文摘录如下:

If your container needs to use an HTTP, HTTPS, or FTP proxy server, you can configure it in different ways: Configure the Docker client On the Docker client, create or edit the file ~/.docker/config.json in the home directory of the user that starts containers.

...

When you create or start new containers, the environment variables are set automatically within the container.

这篇文档说:如果你的 容器 需要使用代理服务器,那么可以以如下方式配置: 在运行容器的用户 home 目录下,配置 ~/.docker/config.json 文件。重新启动容器后,这些环境变量将自动设置进容器,从而容器内的进程可以使用代理服务。

所以这篇文章是讲如何配置运行 容器 的环境与如何拉取镜像无关

如果按照这篇文档的指导,如同南辕北辙。

要解决问题,我们首先来看一般情况下命令行如何使用代理。

环境变量

  • 常规的命令行程序如果要使用代理,需要设置两个环境变量:HTTP_PROXYHTTPS_PROXY 。但是仅仅这样设置环境变量,也不能让 docker 成功拉取镜像。

我们仔细观察 上面的报错信息,有一句说明了报错的来源:

Error response from daemon:

  • 因为镜像的拉取和管理都是 docker daemon 的职责,所以我们要让 docker daemon 知道代理服务器的存在。

  • docker daemon 是由 systemd 管理的,所以我们要从 systemd 配置入手。

正确的官方文档

关于 systemd 配置代理服务器的 官方文档在这里,原文说:

The Docker daemon uses the HTTP_PROXY, HTTPS_PROXY, and NO_PROXY environmental variables in its start-up environment to configure HTTP or HTTPS proxy behavior. You cannot configure these environment variables using the daemon.json file.

This example overrides the default docker.service file.

If you are behind an HTTP or HTTPS proxy server, for example in corporate settings, you need to add this configuration in the Docker systemd service file.

这段话的意思是,docker daemon 使用 HTTP_PROXY, HTTPS_PROXY, 和 NO_PROXY 三个环境变量配置代理服务器,但是你需要在 systemd 的文件里配置环境变量,而不能配置在 daemon.json 里。

具体操作

下面是来自 Docker 官方文档 的操作步骤和详细解释:

1、创建 dockerd 相关的 systemd 目录,这个目录下的配置将覆盖 dockerd 的默认配置

$ sudo mkdir -p /etc/systemd/system/docker.service.d

新建配置文件 /etc/systemd/system/docker.service.d/http-proxy.conf,这个文件中将包含环境变量

[Service]
Environment="HTTP_PROXY=http://proxy.example.com:80"
Environment="HTTPS_PROXY=https://proxy.example.com:443"

如果你自己建了私有的镜像仓库,需要 dockerd 绕过代理服务器直连,那么配置 NO_PROXY 变量:

[Service]
Environment="HTTP_PROXY=http://proxy.example.com:80"
Environment="HTTPS_PROXY=https://proxy.example.com:443"
Environment="NO_PROXY=your-registry.com,10.10.10.10,*.example.com"

多个 NO_PROXY 变量的值用逗号分隔,而且可以使用通配符(*),极端情况下,如果 NO_PROXY=*,那么所有请求都将不通过代理服务器。

重新加载配置文件,重启 dockerd

$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

检查确认环境变量已经正确配置:

$ sudo systemctl show --property=Environment docker

从 docker info 的结果中查看配置项。

这样配置后,应该可以正常拉取 docker 镜像。

结论

  • docker 镜像由 docker daemon 管理

    所以不能用修改 shell 环境变量的方法使用代理服务,而是从 systemd 角度设置环境变量。

    在一些特定环境下,需要在代理环境下使用Docker的某些功能,本文介绍一些场景下 如何配置网络代理

总结与注意事项

不要在Dockerfile中使用ENV指令配置构建过程中使用到的代理配置

  • 使用环境变量配置构建过程中用到的代理配置会把代理服务器打包进镜像中,如果代理服务器是私有化部署的服务器,通过此镜像创建的容器可能访问不到代理服务器,产生难以理解的错误。

  • 同时,由于代理配置中可能包含敏感信息,把代理服务器信息嵌入到镜像中也有可能造成一些安全隐患

Z FAQ

Q:Centos7 Linux中设置网络代理

  • step1 vi /etc/profile
# export http_proxy='http://ip:port'    //代理服务器ip地址和端口号
# export https_proxy='http://ip:port'   //代理服务器ip地址和端口号
# export http_proxy https_proxy

# 形如:
http_proxy=http://127.0.0.1:20171
https_proxy=http://127.0.0.1:20171
export http_proxy https_proxy
  • source /etc/profile

  • 验证:网络代理是否成功

//错误示范
ping google.com

//正确示范
curl -v -i https://google.com

Q:Win10系统配置http、socks5代理

  • 推荐文献

Q: Error:failed to solve failed to do request:Head “https://registry-1.docker.io/v2/library/xxx (清理docker缓存)

  • 问题摘要

在更换Docker镜像仓库为国内源后,出现ERROR:failedtosolve的问题。该问题可能由于缓存导致,即使删除镜像也无法解决。解决方案是使用dockersystemprune-a--force命令深度清理所有未使用的镜像和缓存,以确保从新的镜像源顺利拉取。

  • 问题描述

ERROR: failed to solve failed to do request: Head “https://registry-1.docker.io/v2/library/nginx/manifests/1.19.7”: EOF

  • 问题分析
  • 更换了镜像仓库为国内镜像源;
  • 修改docker engine的配置;

这里贴一下改后的配置,两处改动:

  • 增加了国内镜像源地址registry-mirrors,buildkit 改为false。
  • 配置文件路径为~.docker/daemon.json 或 /etc/
{
 "registry-mirrors": [ 
    "http://hub-mirror.c.163.com",
    "https://docker.mirrors.ustc.edu.cn/"
  ],
  "builder": {
    "gc": {
      "defaultKeepStorage": "20GB",
      "enabled": true
    }
  },
  "experimental": false,
  "features": {
    "buildkit": false
  }
}
  • 解决方法

使用 docker system 的系列命令来清理镜像缓存。
一般情况下,运维清理镜像是通过命令 docker rm i 删除镜像的。但是这条命令不会删除docker build命令产生的缓存文件

先查看一下docker占用的存储空间情况,执行docker system df 。如图,Build Cache本地缓存,大小为15.11MB:

执行命令docker builder prune,一键清除Build Cache缓存:

docker builder prune

【翻译】 prune : vt. 修剪,修整;删除,削减

执行命令后,会提示此操作将移除所有悬空镜像缓存,输入y确认:

再次查看docker占用情况,Build Cache已清空

  • 深度清理(慎用)

如果还是不行🙅,可以尝试使用docker system prune深度清理,此操作会删除所有未使用的容器网络镜像(包括悬空的和未引用的)以及(可选),务必谨慎操作!!!

  • 对应可使用的参数:
-a, --all:删除未被任何容器引用的所有镜像,而不仅仅是悬空镜像
-force, -f:跳过确认步骤,直接执行删除;如果不用在执行步骤时需要手动确认,建议不用
-volumes, -v:删除所有未被至少一个容器引用的卷
-filter:根据提供的条件过滤要删除的内容
  • 参考文献

Q: docker pull context deadline exceeded

  • 参考文献
  • 问题描述

在使用Docker时,有时会遇到错误提示“context deadline exceeded”。这个错误通常发生在执行docker pull命令时,指的是与Docker Hub等镜像仓库通信的超时。这可能会影响到镜像的下载与部署,理解这一错误的根源及解决办法对开发者至关重要。

  • 问题分析
  • 什么是“Context Deadline Exceeded”?

“Context Deadline Exceeded”错误通常与上下文(Context)相关。上下文在Go语言中用于管理请求的生命周期,包括超时和取消。在Docker的上下文中,当请求和响应的时间超过了设定的期限,就会抛出此错误。这可能由以下原因引起:

  • 网络问题:不稳定的网络连接会导致与Docker Hub的通信中断。
  • Docker Hub的负载:在流量高峰期,Docker Hub的响应可能会变慢。
  • Docker配置问题:不当的Docker配置可能导致超时。

  • 解决方法?
  1. 检查网络连接

首先,确保你的网络连接正常。可以通过以下方式检查:

ping registry-1.docker.io

如果存在丢包高延迟现象,需要尝试重启网络设备或联系网络管理员。(乃至需要 ke / xue 上 / 网)

  1. 增加超时时间:可以通过修改Docker的配置文件来增加超时时间。在Docker的配置文件中(通常是/etc/docker/daemon.json),你可以添加如下参数:
{
  "registry-mirrors": [],
  "max-concurrent-downloads": 3,
  "default-address-pools": [
    {
      "name": "default-pool",
      "data": ["172.16.0.0/16"]
    }
  ],
  "features": {
    "buildkit": true
  },
  "debug": true,
  "experimental": false
}

在该配置中可以增加超时时间,确保下载过程中没有中断。

  1. 使用镜像加速器

许多云服务提供商和第三方服务提供Docker镜像加速器,可以显著减少下载时间,并降低出现超时问题的概率。你可以通过配置Docker使用这些加速器。
例如,在配置文件中添加:

{
  "registry-mirrors": ["
}

如何通过CLI命令和配置文件来设置镜像仓库加速器?

直接在命令行中设置

docker pull --registry-mirror= <image-name>

或者,你可以在Docker的配置文件中设置加速器

{
  "registry-mirrors": ["
}
  1. 定期更新Docker版本

保持Docker及其组件的最新版,可以有效避免许多已知问题。执行以下命令更新Docker:

//Ubuntu 为例
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

Y 推荐文献

X 参考文献

posted @ 2025-03-02 22:22  千千寰宇  阅读(2059)  评论(0)    收藏  举报