MENU

如何优雅的给 Docker 配置网络代理

October 17, 2024 • 转载

转载自 如何优雅的给 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_PROXY 和 HTTPS_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_PROXYHTTPS_PROXY, 和 NO_PROXY 三个环境变量配置代理服务器,但是你需要在 systemd 的文件里配置环境变量,而不能配置在 daemon.json 里。

具体操作

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

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 角度设置环境变量。

参考资料https://www.lfhacks.com/tech/pull-docker-images-behind-proxy/#correcthttps://stackoverflow.com/questions/69047394/cant-pull-docker-image-behind-a-proxyhttps://mikemylonakis.com/unix/docker-proxy/https://docs.docker.com/config/daemon/systemd/

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

1. 为Docker Daemon配置代理

1.1 使用systemd配置代理

为了使docker pull指令使用代理,需要在/lib/systemd/system/docker.service中的[Service]片段下添加HTTP_PROXYHTTPS_PROXY环境变量:

[Service]

Environment="HTTP_PROXY=http://<user>:<password>@<domain>:<port>"
Environment="HTTPS_PROXY=http://<user>:<password>@<domain>:<port>"

Environmeng="NO_PROXY=<registry.domain>"
ExecStart=...
...
注意:尖括号<>中的内容需要替换为自己的代理服务器信息

随后刷新配置:

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

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服务使配置生效

重启成功之后通过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>"

1.2 通过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服务:

检查配置是否生效:

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配置。

2. 在容器中使用代理

2.1 通过命令行配置代理

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>

2.2 通过~/.docker/config.json配置

~/.docker/config.json中增加以下配置:

{
  "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

3. 在构建镜像的过程中使用代理

通过~/.docker/config.json的方式配置代理在构建过程中依然有效。

验证:

docker build \
  --no-cache \
  --progress=plain \
  - <<EOF
FROM alpine
RUN env | grep -i _PROXY
EOF 

3.1 通过命令行配置代理

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

验证

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 

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

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

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

资料来源

Configure the daemon with systemd

Configure Docker to use a proxy

配置Docker代理 - 知乎

使用 systemd 配置守护进程 | Docker 文档

本页介绍如何在使用 systemd 时自定义守护进程设置。

Docker 守护进程的大多数配置选项都是使用daemon.json 配置文件设置的。有关更多信息,请参阅Docker 守护进程配置概述。

在没有包管理器的情况下安装二进制文件时,您可能希望将 Docker 与 systemd 集成。为此,请将GitHub 存储库service中的两个单元文件 (和 socket) 安装 到./etc/systemd/system

配置 Docker 守护进程以使用代理服务器

Docker 守护进程在其启动环境中使用以下环境变量来配置 HTTP 或 HTTPS 代理行为:

  • HTTP_PROXY
  • http_proxy
  • HTTPS_PROXY
  • https_proxy
  • NO_PROXY
  • no_proxy

在 Docker Engine 23.0 及更高版本中,您还可以在daemon.json文件中为守护进程配置代理行为:

{
  "proxies": {
    "http-proxy": "http://proxy.example.com:3128",
    "https-proxy": "https://proxy.example.com:3129",
    "no-proxy": "*.test.example.com,.example.org,127.0.0.0/8"
  }
}

这些配置会覆盖默认的docker.servicesystemd 文件。

如果您位于 HTTP 或 HTTPS 代理服务器后面(例如在公司设置中),则必须在 systemd 服务文件中指定守护程序代理配置,而不是在文件中daemon.json或使用环境变量。

无根模式注意事项

在无根模式下运行 Docker 时,systemd 配置文件的位置有所不同 。当以无根模式运行时,Docker 作为用户模式 systemd 服务启动,并使用存储在每个用户主目录中的文件 ~/.config/systemd/user/docker.service.d/。此外,必须在不带标志和带标志的systemctl情况下执行。如果您在无根模式下运行 Docker,请选择下面的_“无根模式”选项卡。_sudo`--user`


  1. 为该服务创建一个 systemd 放置目录docker

    $ sudo mkdir -p /etc/systemd/system/docker.service.d 
  2. /etc/systemd/system/docker.service.d/http-proxy.conf 创建一个名为添加环境变量的文件HTTP_PROXY

    [Service]
    Environment="HTTP_PROXY=http://proxy.example.com:3128"

    如果您位于 HTTPS 代理服务器后面,请设置HTTPS_PROXY环境变量:

    [Service]
    Environment="HTTPS_PROXY=https://proxy.example.com:3129"

    可设置多个环境变量;设置非 HTTPS 和 HTTPs 代理;

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

    笔记

    代理值中的特殊字符(例如#?!()[]{})必须使用 进行双重转义%%。例如:

    [Service]
    Environment="HTTP_PROXY=http://domain%%5Cuser:complex%%23pass@proxy.example.com:3128/"
  3. 如果您有需要在不使用代理的情况下联系的内部 Docker 注册表,则可以通过NO_PROXY环境变量指定它们。

    NO_PROXY变量指定一个字符串,其中包含应从代理中排除的主机的逗号分隔值。您可以指定以下选项来排除主机:

    • IP 地址前缀 ( 1.2.3.4)
    • 域名,或特殊的 DNS 标签 ( *)
    • 域名与该名称和所有子域相匹配。以“.”开头的域名仅匹配子域。例如,给定域 foo.example.comexample.com

      • example.com匹配example.comfoo.example.com, 和
      • .example.com仅匹配foo.example.com
    • 单个星号 ( *) 表示不应进行代理
    • 1.2.3.4:80IP 地址前缀 ( ) 和域名 ( foo.example.com:80)接受文字端口号

    例子:

    [Service]
    Environment="HTTP_PROXY=http://proxy.example.com:3128"
    Environment="HTTPS_PROXY=https://proxy.example.com:3129"
    Environment="NO_PROXY=localhost,127.0.0.1,docker-registry.example.com,.corp"
  4. 刷新更改并重新启动 Docker

    $ sudo systemctl daemon-reload
    $ sudo systemctl restart docker 
  5. 验证配置是否已加载并与您所做的更改相匹配,例如:

    $ sudo systemctl show --property=Environment docker
    
    Environment=HTTP_PROXY=http://proxy.example.com:3128 HTTPS_PROXY=https://proxy.example.com:3129 NO_PROXY=localhost,127.0.0.1,docker-registry.example.com,.corp