Docker的网络代理

在国内的服务器上使用 Docker 会遇到网络失败的问题,例如经典的3个场景都会出现错误:

拉取镜像 镜像构建 容器运行

下文涉及到代理的部分,将假设有一个本地HTTP的网络代理:http://127.0.0.1:1080

1. Docker pull

Docker在拉取镜像时,还是借助宿主机的网络,所以设置源和设置环境变量中的网络代理变量均是有效的

1.1. 更换Docker源

在2024年6月后,国内不少Docker源都失效了,如果能找到可用的国内源,更换源这个方法还是很好用的

更换源,编辑:/etc/docker/daemon.json

JSONcontent_copy
{
  "registry-mirrors": [
    "https://registry.docker-cn.com",
    "https://docker.mirrors.ustc.edu.cn",
    "https://hub-mirror.c.163.com",
    "https://mirror.baidubce.com"
  ]
}

重启docker应用新的源

Bashcontent_copy
sudo systemctl restart docker

如果拉取不成功,可以借助 journalctl 查看错误

Bashcontent_copy
journalctl -xue docker.service

1.2. 网络代理

1.2.1. 临时环境变量

临时设置系统环境变量:

Bashcontent_copy
export HTTP_PROXY=http://127.0.0.1:1080
export HTTPS_PROXY=http://127.0.0.1:1080

然后拉取镜像

Bashcontent_copy
sudo -E docker pull hello-world

-E 用于让sudo用户继承当前普通用户的非安全环境变量(HTTP_PROXY和HTTPS_PROXY)

这个代理设置仅对当前会话生效,在退出后将不再生效

1.2.2. daemon.json

如果希望拉取时的代理设置永久生效,可以通过修改 daemon.json 来实现

编辑:/etc/docker/daemon.json

JSONcontent_copy
{
  "proxies": {
    "http-proxy": "http://127.0.0.1:1080",
    "https-proxy": "http://127.0.0.1:1080",
    "no-proxy": "*.test.example.com,.example.org"
  },
  "ipv6": false
}

走网络代理的时要注意禁用系统 ipv6 ,否则容易出现在 ipv4 环境下访问 ipv6 导致的网络错误

重启docker应用网络代理设置:

Bashcontent_copy
sudo systemctl restart docker

1.2.3. 永久环境变量

除了修改 daemon.json 实现网络代理永久生效外,也可以通过修改docker运行时的环境变量来实现

编辑:/etc/systemd/system/multi-user.target.wants/docker.service

INIcontent_copy
...

[Service]
Environment="HTTP_PROXY=http://127.0.0.1:1080"
Environment="HTTPS_PROXY=http://127.0.0.1:1080"
...

在service节点中添加环境变量,这样每一次拉取都会走网络代理

docker.service的文件位置在不同系统中不同,可以通过 find 来进行查找该文件

重启docker以使环境变量生效

Bashcontent_copy
sudo systemctl daemon-reload
sudo systemctl restart docker

2. Docker Build

无论是修改 daemon.json 还是修改 docker.service ,其本质是影响了Docker的Daemon进程环境变量

而与拉取不同,在构建时,每个Dockerfile的RUN指令都在一个全新的容器中执行,这些容器不会继承Docker Daemon进程的环境变量

如果在构建步骤中,有一些指令如 curl/wget 需要使用代理,则需要修改编辑Dockerfile文件,添加 http_proxyhttps_proxy 变量:

Dockercontent_copy
...

ARG http_proxy
ARG https_proxy
ENV http_proxy=${http_proxy}
ENV https_proxy=${https_proxy}

...

这里采用在构建时传入 http_proxyhttps_proxy 变量会更加灵活

如刚才所说,构建阶段是完全网络隔离的,所以 127.0.0.1 是无法指向宿主机的,需要填入真实的宿主机IP

先使用 ip -a 获取到宿主机的真实IP,如:

Bashcontent_copy
$ ip -a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: enp4s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether c8:7f:54:a9:98:d1 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.2/24 brd 192.168.15.255 scope global enp4s0
       valid_lft forever preferred_lft forever
...
106 docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:34:65:16:f5 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

192.168.1.2172.17.0.1 两个地址都可以被容器访问到,那么构建参考如下:

Bashcontent_copy
sudo docker build \
             --build-arg http_proxy=http://192.168.1.2:1080 \
             --build-arg https_proxy=http://192.168.1.2:1080 \
             -t your-image .

3. Docker Container

Docker在运行时,容器要使用网络代理,则需要设置容器的全局网络代理变量,类似于在Linux Shell中设置http_proxyhttp_proxy

容器在运行时,通过-e参数设置环境变量:

Bashcontent_copy
sudo docker run \
          -e http_proxy=http://192.168.1.2:1080 \
          -e https_proxy=http://192.168.1.2:1080 \
          your-image

与前文一致,容器内的环境是独立的,所以要将代理指向宿主机而不是 127.0.0.1

文章来源:

Author:chancel
link:http://www.chancel.me/markdown/docker-network-proxy