Colima代理¶
对于中国的软件开发者、运维者来说,要顺利使用 dockerhub
来获取镜像, Proxy代理服务 是必须采用的技术,所以也要为Colima解决绕过GFW阻塞的问题。
我最初没有使用代理,发现 Debian镜像(tini进程管理器) 无法拉取镜像:
docker build --rm -t debian-ssh-tini .
始终报错:
[+] Building 32.9s (2/2) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 1.04kB 0.0s
=> ERROR [internal] load metadata for docker.io/library/debian:latest 32.8s
------
> [internal] load metadata for docker.io/library/debian:latest:
------
Dockerfile:1
--------------------
1 | >>> FROM debian:latest
2 | MAINTAINER vincent huatai <vincent@huatai.me>
3 |
--------------------
error: failed to solve: DeadlineExceeded: DeadlineExceeded: DeadlineExceeded: debian:latest: failed to copy: httpReadSeeker: failed open: failed to do request: Get "https://production.cloudflare.docker.com/registry-v2/docker/registry/v2/blobs/sha256/19/19fa7f391c55906b0bbe77bd45a4e7951c67ed70f8054e5987749785450c0442/data?verify=1723911692-vjk1SJX8qBVoT%2FQRQSQuHdu%2BCsQ%3D": dial tcp 31.13.87.34:443: i/o timeout
FATA[0033] no image was built
FATA[0033] exit status 1
此外,在 colima ssh
进入 Lima: Linux Machines 虚拟机内部,就会发现即使 Ubuntu Linux 系统更新( apt update
)也是存在和 Docker Atlas 更新相关错误:
...
Ign:31 https://download.docker.com/linux/ubuntu noble InRelease
Ign:31 https://download.docker.com/linux/ubuntu noble InRelease
Ign:31 https://download.docker.com/linux/ubuntu noble InRelease
Err:31 https://download.docker.com/linux/ubuntu noble InRelease
Could not connect to download.docker.com:443 (157.240.13.8), connection timed out
Fetched 26.3 MB in 37s (703 kB/s)
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
14 packages can be upgraded. Run 'apt list --upgradable' to see them.
W: Failed to fetch https://download.docker.com/linux/ubuntu/dists/noble/InRelease Could not connect to download.docker.com:443 (157.240.13.8), connection timed out
W: Some index files failed to download. They have been ignored, or old ones used instead.
解决实践小结¶
如果你没有耐心看完本文,这里给出一个结论:
只需要在物理主机上配置好代理服务器的用户环境变量,例如我使用 SSH隧道 访问远端服务器的 Squid代理服务 / Privoxy代理服务
执行
colima start
启动colima虚拟机的时候,会自动将物理主机环境变量中有关代理配置设置注入虚拟机,不过只有 APT包管理 解决了 越过长城 (此时可以顺利执行apt update && apt upgrade
需要同时配置 Docker服务器Proxy 和 containerd服务端代理 (目前我的验证,尚未验证是否可以只配置其中之一)
在
colima
虚拟机内部配置 Docker客户端的Proxy 这样执行docker build
就能够在docker客户端获取meta信息,再进一步盗用docker/containerd服务器端下载镜像
警告
配置代理需要同时满足 docker 客户端和服务器的代理设置,单方面配置客户端和服务器端都不能实现代理跨越GFW
分析和解决思路¶
这个问题需要采用 配置Docker使用代理 方式解决:
Ubuntu Linux 系统需要 配置linux系统级代理 :至少需要配置 APT包管理 的代理
Docker Atlas / containerd运行时(runtime) 需要配置 Docker服务器Proxy ,这样可以让容器运行时能够下载镜像
容器内部需要通过 Docker客户端的Proxy 注入代理配置,这样容器内部的应用就能够顺畅访问internet
代理服务器(之前的尝试,可行但复杂,留作参考)¶
我个人的经验是使用轻量级的HTTP/HTTPS代理 Privoxy代理服务 最为简单(服务器无缓存),如果希望更为稳定和企业级,则选择 Squid代理服务 (服务器有缓存),不过对实际效果没有太大影响,都是非常好的选择。
首先通过 SSH隧道 构建一个本地到远程服务器代理服务端口(服务器上代理服务器仅监听回环地址)的SSH加密连接。我实际采用的是在
~/.ssh/config
配置如下:
Host *
ServerAliveInterval 60
ControlMaster auto
ControlPath ~/.ssh/%h-%p-%r
ControlPersist yes
StrictHostKeyChecking no
Compression yes
Host MyProxy
HostName <SERVER_IP>
User admin
LocalForward 3128 127.0.0.1:3128
LocalForward 172.17.0.1:3128 127.0.0.1:3128
IdentitiesOnly yes
IdentityFile ~/.ssh/proxy/id_rsa
执行构建SSL Tunnel:
ssh MyProxy
apt代理¶
修改Colima虚拟机内部配置
/etc/apt/apt.conf.d/01-vendor-ubuntu
添加一行 APT包管理 代理配置:
Acquire::http::Proxy "http://127.0.0.1:3128/";
...
现在再次执行 apt update && apt upgrade
就不会有任何GFW的阻塞,顺利完成虚拟机操作系统更新
Colima虚拟机内部运行的 docker/containerd 需要设置代理以便能够下载镜像运行容器:
Docker服务器代理¶
我的实践中虚拟机中运行containerd取代默认的Docker:
colima start --runtime containerd --cpu 4 --memory 8 --vm-type=vz
所以这段Docker服务器代理设置是我之前实践 Docker服务器Proxy ( 👈 请参考)
Containerd服务器代理¶
Colima虚拟机内部使用的操作系统是 Ubuntu Linux ,完整使用了 Systemd进程管理器 系统来管理进程服务,所以可以采用我之前的实践 containerd代理 相同方法:
修订
/etc/environment
添加代理配置:
HTTP_PROXY="http://127.0.0.1:3128"
HTTPS_PROXY="http://127.0.0.1:3128"
NO_PROXY="*.baidu.com,192.168.0.0/16,10.0.0.0/8"
colima ssh
重新登陆系统使上述代理环境变量生效,然后执行以下脚本为containerd服务添加代理配置:
if [ ! -d /etc/systemd/system/containerd.service.d ];then
mkdir -p /etc/systemd/system/containerd.service.d
fi
cat <<EOF >/etc/systemd/system/containerd.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=${HTTP_PROXY:-}"
Environment="HTTPS_PROXY=${HTTPS_PROXY:-}"
Environment="NO_PROXY=${NO_PROXY:-localhost},${LOCAL_NETWORK}"
EOF
systemctl daemon-reload
systemctl restart containerd
代理服务器再次尝试(建议方案)¶
发现colima会将HOST主机proxy环境变量注入虚拟机¶
在晚上折腾时偶然发现,如果我的 macOS 操作系统环境变量设置了代理,例如:
export HTTP_PROXY="http://127.0.0.1:3128"
export HTTPS_PROXY="http://127.0.0.1:3128"
export NO_PROXY="*.baidu.com,192.168.0.0/16,10.0.0.0/8"
export http_proxy="http://127.0.0.1:3128"
export https_proxy="http://127.0.0.1:3128"
export no_proxy="*.baidu.com,192.168.0.0/16,10.0.0.0/8"
则重新启动colima虚拟机之后,这个环境变量会注入到虚拟机内部,但是会做修改(IP地址从 127.0.0.1
调整为 192.168.5.2
),而且这个修改是写到虚拟机内部 /etc/environment
中:
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin"
#LIMA-START
HTTPS_PROXY=http://192.168.5.2:3128
HTTP_PROXY=http://192.168.5.2:3128
NO_PROXY=*.baidu.com,192.168.0.0/16,10.0.0.0/8
http_proxy=http://192.168.5.2:3128
https_proxy=http://192.168.5.2:3128
no_proxy=*.baidu.com,192.168.0.0/16,10.0.0.0/8
#LIMA-END
我忽然想到,既然Colima将我的HOST物理主机的 PROXY
相关环境变量在启动 Lima: Linux Machines 虚拟机时候注入到虚拟机内部作为环境变量,那么说明Colima开发者默认就是让虚拟机继承物理服务器的代理配置。同时,观察到Colima在虚拟机的 /etc/environment
标准配置中添加了代理配置,但是很巧妙地将物理主机 127.0.0.1
回环地址转变成了 192.158.5.2
,也就是对应虚拟机( 192.168.5.1
)的默认网关( 192.168.5.2
)。这说明,Colima会借助物理主机的代理服务器访问外网。
综上所述,看起来完全不用手工配置虚拟机内部服务的代理,而是之际在启动 colima
虚拟机时,操作命令所在的HOST物理主机shell环境变量PROXY相关设置会自动注入,来解决Colima虚拟机内部的代理。这是Colima的feature。
通过HOST物理主机 HTTP_PROXY
配置注入虚拟机¶
首先删除掉刚才测试的虚拟机,准备干净地启动一个全新虚拟机:
colima delete
在启动
colima
虚拟机之前,先确保发起启动的用户的环境变量如下(配置到~/.zshrc
中,或者直接在SHELL中执行):
export HTTP_PROXY="http://127.0.0.1:3128"
export HTTPS_PROXY="http://127.0.0.1:3128"
export NO_PROXY="*.baidu.com,192.168.0.0/16,10.0.0.0/8"
export http_proxy="http://127.0.0.1:3128"
export https_proxy="http://127.0.0.1:3128"
export no_proxy="*.baidu.com,192.168.0.0/16,10.0.0.0/8"
重新创建colima虚拟机:
colima start --runtime containerd --cpu 4 --memory 8 --vm-type=vz
果然,这次干净启动的
colima
虚拟机内部注入了原先在HOST物理主机配置的PROXY相关配置,colima ssh
登陆后检查/etc/environment
可以看到配置:
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin"
#LIMA-START
HTTPS_PROXY=http://192.168.5.2:3128
HTTP_PROXY=http://192.168.5.2:3128
NO_PROXY=*.baidu.com,192.168.0.0/16,10.0.0.0/8
http_proxy=http://192.168.5.2:3128
https_proxy=http://192.168.5.2:3128
no_proxy=*.baidu.com,192.168.0.0/16,10.0.0.0/8
#LIMA-END
注意,这里配置环境变量 HTTP_PROXY
/ http_proxy
,有全大写也有全小写,这是因为不同的程序的默认差异,比较搞...
HTTP_PROXY
配置注入虚拟机的 colima.yaml¶
实际上还有一个更为方便的注入方法,就是使用 $HOME/.colima/default/colima.yaml
直接添加PROXY配置:
env:
HTTP_PROXY: http://127.0.0.1:3128
HTTPS_PROXY: http://127.0.0.1:3128
NO_PROXY: localhost,127.0.0.1,*.baidu.com,192.168.0.0/16,10.0.0.0/8
新的困扰¶
其实上述两种方案(虚拟机内部配置 containerd服务端代理 和 通过注入HOST主机PROXY配置到colima虚拟机)都是完成相同的工作,看起来都很完善。但是,我实际构建 Colima镜像 还是再次遇到了报错(两个方法都是一样的报错):
当 containerd
开始同步时是使用代理的(因为我看到如果不启动SSH tunnel,则出现如下访问代理报错:
error: failed to solve: debian:latest: failed to authorize: failed to fetch anonymous token: Get "https://auth.docker.io/token?scope=repository%3Alibrary%2Fdebian%3Apull&service=registry.docker.io": proxyconnect tcp: dial tcp 127.0.0.1:3128: connect: connection refused
这里代理IP地址也可能是 ``192.168.5.2`` ,取决于采用上述两个方案之一
但是我发现接下来的https请求居然不再走代理,原因是我发现它报错信息解析的地址 production.cloudflare.docker.com => 210.209.84.142
是我本地虚拟机解析DNS的结果:
error: failed to solve: DeadlineExceeded: DeadlineExceeded: DeadlineExceeded: debian:latest: failed to copy: httpReadSeeker: failed open: failed to do request: Get "https://production.cloudflare.docker.com/registry-v2/docker/registry/v2/blobs/sha256/19/19fa7f391c55906b0bbe77bd45a4e7951c67ed70f8054e5987749785450c0442/data?verify=1724172530-5QFH8JiRFjY5RRAQqyHkaNW0Kb4%3D": dial tcp 210.209.84.142:443: i/o timeout
而不是远在墙外squid服务器解析的域名地址(不同地区解析同一个域名返回的地址不同)。看起来 containerd运行时(runtime) 的代理设置并不是和 配置Docker使用代理 一致,这让我很困扰。
那么怎么解决这个问题呢?
Colima是Docker/Containerd混合体?¶
我原本以为我在 colima start
运行时传递了参数 --runtime containerd
就会在 colima
虚拟机中只单纯运行 containerd运行时(runtime) 从而避免运行 dockerd
。然而,事实证明不管怎样,实际上服务器上是通过 dockerd
去访问 containerd
( containerd.sock )。
从服务器上 systemctl status dockerd
和 systemctl status conainerd
可以看到,两个服务同时在运行:
root@colima:~# systemctl status containerd
● containerd.service - containerd container runtime
Loaded: loaded (/usr/lib/systemd/system/containerd.service; enabled; preset: enabled)
Active: active (running) since Wed 2024-08-21 14:56:45 CST; 37s ago
Docs: https://containerd.io
Process: 1880 ExecStartPre=/sbin/modprobe overlay (code=exited, status=0/SUCCESS)
Main PID: 1882 (containerd)
Tasks: 9
Memory: 16.2M (peak: 16.8M)
CPU: 212ms
CGroup: /system.slice/containerd.service
└─1882 /usr/bin/containerd
Aug 21 14:56:45 colima containerd[1882]: time="2024-08-21T14:56:45.768595311+08:00" level=info msg="Start subscribing containerd event"
Aug 21 14:56:45 colima containerd[1882]: time="2024-08-21T14:56:45.768664312+08:00" level=info msg="Start recovering state"
Aug 21 14:56:45 colima containerd[1882]: time="2024-08-21T14:56:45.768706979+08:00" level=info msg="Start event monitor"
Aug 21 14:56:45 colima containerd[1882]: time="2024-08-21T14:56:45.768722711+08:00" level=info msg="Start snapshots syncer"
Aug 21 14:56:45 colima containerd[1882]: time="2024-08-21T14:56:45.768731059+08:00" level=info msg="Start cni network conf syncer for default"
Aug 21 14:56:45 colima containerd[1882]: time="2024-08-21T14:56:45.768736430+08:00" level=info msg="Start streaming server"
Aug 21 14:56:45 colima containerd[1882]: time="2024-08-21T14:56:45.768754558+08:00" level=info msg=serving... address=/run/containerd/containerd.sock.ttrpc
Aug 21 14:56:45 colima containerd[1882]: time="2024-08-21T14:56:45.768790457+08:00" level=info msg=serving... address=/run/containerd/containerd.sock
Aug 21 14:56:45 colima containerd[1882]: time="2024-08-21T14:56:45.768857303+08:00" level=info msg="containerd successfully booted in 0.025767s"
Aug 21 14:56:45 colima systemd[1]: Started containerd.service - containerd container runtime.
root@colima:~# systemctl status docker
● docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; preset: enabled)
Active: active (running) since Wed 2024-08-21 14:56:39 CST; 51s ago
TriggeredBy: ● docker.socket
Docs: https://docs.docker.com
Main PID: 1228 (dockerd)
Tasks: 10
Memory: 99.6M (peak: 100.2M)
CPU: 670ms
CGroup: /system.slice/docker.service
└─1228 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
Aug 21 14:56:39 colima dockerd[1228]: time="2024-08-21T14:56:39.584977473+08:00" level=info msg="Default bridge (docker0) is assigned with an IP address 172.17.0.0/16. Daemon option --bip can be used to set a preferred IP address"
Aug 21 14:56:39 colima dockerd[1228]: time="2024-08-21T14:56:39.662021438+08:00" level=info msg="Loading containers: done."
Aug 21 14:56:39 colima dockerd[1228]: time="2024-08-21T14:56:39.678308259+08:00" level=info msg="Docker daemon" commit=662f78c containerd-snapshotter=false storage-driver=overlay2 version=27.0.3
Aug 21 14:56:39 colima dockerd[1228]: time="2024-08-21T14:56:39.678675509+08:00" level=info msg="Daemon has completed initialization"
Aug 21 14:56:39 colima systemd[1]: Started docker.service - Docker Application Container Engine.
Aug 21 14:56:39 colima dockerd[1228]: time="2024-08-21T14:56:39.721738274+08:00" level=info msg="API listen on /run/docker.sock"
Aug 21 14:56:45 colima dockerd[1228]: time="2024-08-21T14:56:45.700065516+08:00" level=error msg="Failed to get event" error="rpc error: code = Unavailable desc = error reading from server: EOF" module=libcontainerd namespace=plugins.moby
Aug 21 14:56:45 colima dockerd[1228]: time="2024-08-21T14:56:45.700111600+08:00" level=info msg="Waiting for containerd to be ready to restart event processing" module=libcontainerd namespace=plugins.moby
Aug 21 14:56:45 colima dockerd[1228]: time="2024-08-21T14:56:45.700110522+08:00" level=error msg="Failed to get event" error="rpc error: code = Unavailable desc = error reading from server: EOF" module=libcontainerd namespace=moby
Aug 21 14:56:45 colima dockerd[1228]: time="2024-08-21T14:56:45.700159153+08:00" level=info msg="Waiting for containerd to be ready to restart event processing" module=libcontainerd namespace=moby
这说明需要同时设置 Docker Atlas 和 containerd运行时(runtime) 的代理配置,特别是 Docker服务器Proxy
Systemd进程管理器 的 Docker Atlas 方法见 Docker服务器Proxy ( /etc/default/docker
配置是针对 SysVinit
配置,对systemd不生效) ,其实也是设置 Systemd进程管理器 启动配置的环境变量
if [ ! -d /etc/systemd/system/docker.service.d ];then
mkdir -p /etc/systemd/system/docker.service.d
fi
cat <<EOF >/etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=${HTTP_PROXY:-}"
Environment="HTTPS_PROXY=${HTTPS_PROXY:-}"
Environment="NO_PROXY=${NO_PROXY:-localhost},${LOCAL_NETWORK}"
EOF
systemctl daemon-reload
systemctl restart docker
现在,加上前面配置 containerd服务端代理 ,实际上服务器端运行时(containerd)和管控(docker)都已经启用的PROXY代理。可以通过在colima虚拟机内部检查 systemctl show <service_name> --property Environment
查看:
# systemctl show containerd --property Environment
Environment=HTTP_PROXY=http://192.168.5.2:3128 HTTPS_PROXY=http://192.168.5.2:3128 "NO_PROXY=*.baidu.com,192.168.0.0/16,10.0.0.0/8,"
# systemctl show docker --property Environment
Environment=HTTP_PROXY=http://192.168.5.2:3128 HTTPS_PROXY=http://192.168.5.2:3128 "NO_PROXY=*.baidu.com,192.168.0.0/16,10.0.0.0/8,"
输出显示 docker
和 containerd
都已经具备了PROXY环境配置
然而很不幸,我发现 nerdctl build
输出依然是报错, httpReadSeeker
复制错误。奇怪,为何没有通过代理来访问 docker 官方仓库?:
error: failed to solve: DeadlineExceeded: DeadlineExceeded: DeadlineExceeded: debian:latest: failed to copy: httpReadSeeker: failed open: failed to do request: Get "https://production.cloudflare.docker.com/registry-v2/docker/registry/v2/blobs/sha256/19/19fa7f391c55906b0bbe77bd45a4e7951c67ed70f8054e5987749785450c0442/data?verify=1724231825-HSnthXcWUnRbhLGA8eMXCizsEq8%3D": dial tcp 199.59.150.43:443: i/o timeout
我理解实际上 nerdctl build
和 docker build
并不仅仅是服务器端需要访问internet,有一部分数据是通过客户端这边下载的,也就是META数据是通过客户端下载,来定位需要下载的镜像,再由服务器端去pull镜像。这个逻辑导致客户端和服务器端都要能够跨越GFW。
我突然感觉到是 nerdctl
客户端的问题,看起来 nerdctl build
不支持代理? 我尝试在客户端(macOS HOST主机以及colima虚拟机内部都设置了 http_proxy
和 HTTP_PROXY
环境变量,避免大小写差异),但是始终没有解决通过代理访问问题。
从 nerdctl build --help
输出来看,没有提供 proxy 相关配置 -- nerdctl这样的docker复刻工具实际上功能做了精简,并不能完全支持docker丰富的功能
最终的解决之道
¶
最终解决,说来难以置信地简单,就是: 如果要使用代理服务器来下载docker镜像,务必使用 docker
客户端来管理,支持代理; nerdctl
客户端不支持代理。
具体解决方法是: 在 colima
虚拟机内部执行 docker build
命令,这样结合前面的的服务器配置:
colima start
通过HOST物理主机HTTP_PROXY
配置注入虚拟机,此时仅解决 APT包管理 翻墙配置 Docker服务器Proxy 和 containerd服务端代理 (目前我验证两者都配置,没有验证是否可以只配置其中之一) 确保服务器端能够翻墙拉取镜像
一定要在
colima
虚拟机内部,配置 docker 客户端使用代理,即配置~/.docker/config.json
如下:
{
"proxies":
{
"default":
{
"httpProxy": "http://192.168.5.2:3128",
"httpsProxy": "http://192.168.5.2:3128",
"noProxy": "*.baidu.com,192.168.0.0/16,10.0.0.0/8"
}
}
}
最后一定要使用
docker build
才能支持客户端使用代理,nerdctl
客户端不支持代理