dockerd 代理

在执行 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
 

在这个 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"
 

Container 代理

在容器运行阶段,如果需要代理上网,则需要配置 ~/.docker/config.json。 以下配置,只在 Docker 17.07 及以上版本生效。

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

这个是用户级的配置,除了 proxiesdocker login 等相关信息也会在其中。 而且还可以配置信息展示的格式、插件参数等。

此外,容器的网络代理,也可以直接在其运行时通过 -e 注入 http_proxy 等环境变量。 这两种方法分别适合不同场景。 config.json 非常方便,默认在所有配置修改后启动的容器生效,适合个人开发环境。 在 CI/CD 的自动构建环境、或者实际上线运行的环境中,这种方法就不太合适,用 -e 注入这种显式配置会更好,减轻对构建、部署环境的依赖。 当然,在这些环境中,最好用良好的设计避免配置代理上网。

docker build

docker build 命令虽然是在 docker 所在的宿主机上执行的,看上去像是直接使用了宿主机的网络环境,但实际上 docker build 也是启动了一个 container 进行构建,所以在构建过程中的所有命令都是在 container 中执行的,http_proxy 和 https_proxy 的配置也应该是在 container 中进行的。

并且,由于 container 默认是桥接 bridge 网络,宿主机和 container 是平级的,被放在了一个虚拟的网段里。访问宿主机上的代理服务器,对于 container 来说实际上是访问另一台机器上的服务器,127.0.0.1 指向的是 container 本身。除非使用 --network host 否则是无法使用的

在 docker 默认的桥接 bridge 网络中,宿主机的 IP 一般是 172.17.0.1(Linux),或者 192.168.65.1(MacOS),可以将 http_proxy 中的 IP 换成 172.17.0.1/192.168.65.1,来实现通过宿主的代理服务器访问网络

1、可以使用 ENV 来配置 container 中的环境变量。

ENV http_proxy "http://172.17.0.1:7890"  
ENV https_proxy "http://172.17.0.1:7890"  

2、通过 build-arg, 在构建时,需要注入 http_proxy 等参数。

docker build . \
    --build-arg "http_proxy=http://172.17.0.1:7890" \
    --build-arg "https_proxy=http://172.17.0.1:7890" \
    --build-arg "no_proxy=localhost,127.0.0.1,.example.com" \
    -t your/image:tag
 

确认 docker 默认网卡的配置

通常情况下Linux的Docker默认网卡名为 docker0 ,默认IP及其网段信息一般情况下是 172.17.0.1/16 ,在运行 build 命令时临时容器将会在这张网卡的网段下,所以需要明确这张网卡的配置信息,以方便后续配置本地 HTTP 代理的地址,以及如果你跟我一样开启了防火墙的情况下开放端口使用。

首先可以运行以下命令查看网卡信息

ip a show docker0

你会得到类似如下的输出

xianyu@book:/home/xianyu $ ip a show docker0
14: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever`

如果你不打算修改默认配置的话就可以看下一节了,但如果你觉得默认网段有问题(不少企业网络会使用 172 的网段作为内网的地址段,会有比较大的概率冲突),那么就可以修改默认的网段。

修改默认网段

修改默认网段的方法非常简单,只需向 /etc/docker/daemon.json 中添加如下配置

{
    "bip":"10.233.0.1/24"
}

并重启 docker 即可

`systemctl restart docker`

同样的,重启后通过以下命令验证网段是否修改成功

`xianyu@book:/home/xianyu $ ip a show docker0
14: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 10.233.0.1/24 brd 10.233.0.255 scope global docker0
       valid_lft forever preferred_lft forever`

BASH

开放防火墙端口

由于目前 IPv6 的逐渐普及,计算机不开放防火墙会有极大的安全风险,所以在我的本地环境中是启用防火墙的

在上一节中我们提到了默认网卡的网段问题,此时我们可以知道默认网卡的网段为 10.233.0.0/24 本机作为网关在这个网段中的地址为 10.233.0.1

我们希望这个网段中的容器可以访问本机的HTTP代理服务,即我们需要添加一条ufw规则以允许来自 10.233.0.0/24 的请求访问 10.233.0.1:8080 ,具体的 ufw 命令如下

ufw allow from 10.233.0.0/24 to 10.233.0.1 port 8080

编辑 Dockerfile

做好上述准备我们就可以开始配置容器的 HTTP 代理了

本次实际案例为编译一个包含自定义插件的 Caddy2 容器,依照官方文档原始的 Dockerfile 长这样:

`FROM caddy:builder AS builder

RUN xcaddy build \
    --with github.com/caddy-dns/cloudflare

FROM caddy

COPY --from=builder /usr/bin/caddy /usr/bin/caddy`

DOCKERFILE

可以看到在 RUN 当中我们需要从 Github 拉取我们需要的代码,但实际上由于网络环境问题很难正常拉取到,此时我们可以在这里加入 HTTP 代理的变量使其拉取时走本机的代理

`FROM caddy:builder AS builder

ENV http_proxy=http://10.233.0.1:8080
ENV https_proxy=http://10.233.0.1:8080

RUN xcaddy build \
    --with github.com/caddy-dns/cloudflare

FROM caddy

COPY --from=builder /usr/bin/caddy /usr/bin/caddy`

DOCKERFILE

经过上述处理即可愉快的手搓容器了。

重启生效

代理配置完成后,reboot 重启当然可以生效,但不重启也行。

docker build 代理是在执行前设置的,所以修改后,下次执行立即生效。 Container 代理的修改也是立即生效的,但是只针对以后启动的 Container,对已经启动的 Container 无效。

dockerd 代理的修改比较特殊,它实际上是改 systemd 的配置,因此需要重载 systemd 并重启 dockerd 才能生效。

sudo systemctl daemon-reload
sudo systemctl restart docker