menu E4b9a6's blog
rss_feed
E4b9a6's blog
有善始者实繁,能克终者盖寡。

Docker的网络代理

作者:E4b9a6, 创建:2024-05-14, 字数:4976, 已阅:1009, 最后更新:2024-07-15

这篇文章更新于 129 天前,文中部分信息可能失效,请自行甄别无效内容。

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

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

下面讲针对以上三个场景涉及到代理的部分进行设置

首先假设有一个不受限制的本地HTTP网络代理:http://127.0.0.1:8080

由于不同的 Docker 版本针对代理的策略不一样,如下面的方法无效,请自行检查版本差异

我的Docker版本信息如下:

Bash
$ sudo docker version 
Client: Docker Engine - Community
 Version:           27.0.3
 API version:       1.46
 Go version:        go1.21.11
 Git commit:        7d4bcd8
 Built:             Sat Jun 29 00:02:50 2024
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          27.0.3
  API version:      1.46 (minimum version 1.24)
  Go version:       go1.21.11
  Git commit:       662f78c
  Built:            Sat Jun 29 00:02:50 2024
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.7.18
  GitCommit:        ae71819c4f5e67bb4d5ae76a6b735f29cc25774e
 runc:
  Version:          1.7.18
  GitCommit:        v1.1.13-0-g58aa920
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

1. Docker pull

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

1.1. 更换Docker源

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

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

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

重启docker应用新的源

Bash
sudo systemctl restart docker

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

Bash
journalctl -xue docker.service

1.2. 网络代理

1.2.1. 临时环境变量

临时设置系统环境变量 HTTP_PROXYHTTPS_PROXY 对于Docker同样是有效的

例如设置如下:

Bash
export HTTP_PROXY=http://127.0.0.1:8080
export HTTPS_PROXY=http://127.0.0.1:8080

使用 curl 验证是否获得了网络代理:

Bash
curl cip.cc

然后拉取镜像

Bash
sudo -E docker pull hello-world

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

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

另外 sudo 由于安全问题并不一定会全部继承环境变量,使用 -E 并不是一定会继承 HTTP_PROXYHTTPS_PROXY

请确保 /etc/sudoers 有如下配置:

Text only
Defaults env_keep += "HTTP_PROXY HTTPS_PROXY"

1.2.2. daemon.json

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

编辑:/etc/docker/daemon.json

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

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

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

Bash
sudo systemctl restart docker

但请注意,这个方法也同样影响容器内的网络,容器内的 http 请求也会走网络代理

1.2.3. 永久环境变量

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

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

INI
...

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

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

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

重启docker以使环境变量生效

Bash
sudo systemctl daemon-reload
sudo systemctl restart docker

但请注意,这个方法也同样影响容器内的网络,容器内的 http 请求也会走网络代理

2. Docker Build

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

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

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

Docker
...

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,如:

Bash
$ 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 两个地址都可以被容器访问到,那么构建参考如下:

Bash
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参数设置环境变量:

Bash
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

如果容器内存在环境变量 http_proxy/etc/docker/daemon.json 中存在网络环境变量 prixies

容器将优先容器内的环境变量 http_proxy


[[replyMessage== null?"发表评论":"发表评论 @ " + replyMessage.m_author]]

account_circle
email
web_asset
textsms

评论列表([[messageResponse.total]])

还没有可以显示的留言...
gravatar
[[messageItem.m_author]] [[messageItem.m_author]]
[[messageItem.create_time]]
[[getEnviron(messageItem.m_environ)]]
[[subMessage.m_author]] [[subMessage.m_author]] @ [[subMessage.parent_message.m_author]] [[subMessage.parent_message.m_author]]
[[subMessage.create_time]]
[[getEnviron(messageItem.m_environ)]]