Colima 代理

2025年5月,我在旧笔记本 MacBook Pro 15" Late 2013 重新部署 Colima 。由于时隔多时,软件堆栈有了很大的更新,我想重新实践和整理手册,修订之前 Colima代理(文章归档) 中过时的操作步骤,通过简洁有效的方式实现 越过长城 完成容器的流畅部署。

之所以需要设置Colima代理,是因为GFW屏蔽了 dockerhub 导致无法直接从开源社区获取官方镜像。例如在build镜像:

构建镜像
docker build --rm -t debian-dev .

此时显示访问registry仓库端口超时:

构建镜像网络超时报错
[+] Building 39.5s (2/2) FINISHED                                               
 => [internal] load build definition from Dockerfile                       4.4s
 => => transferring dockerfile: 4.87kB                                     0.0s
 => ERROR [internal] load metadata for docker.io/library/debian:latest    30.0s
------
 > [internal] load metadata for docker.io/library/debian:latest:
------
Dockerfile:1
--------------------
   1 | >>> FROM debian:latest
   2 |     
   3 |     ENV container=docker
--------------------
error: failed to solve: debian:latest: failed to resolve source metadata for docker.io/library/debian:latest: failed to do request: Head "https://registry-1.docker.io/v2/library/debian/manifests/latest": dial tcp 69.63.186.30:443: i/o timeout
FATA[0040] no image was built                           
FATA[0041] exit status 1

未代理之前的初始部署

Colima快速起步 中采用基本启动运行方式:

使用 qemu 模式虚拟化的 2c4g 虚拟机运行 colima
# 如果macOS v14/15 ,支持Apple Virtualization,建议使用 vz (性能更好)
# colima start --runtime containerd --cpu 2 --memory 4 --vm-type=vz

# 不支持 --vm-type=vz 则是通过qemu运行
# 注意: 如果要支持代理,需要登陆到Colima虚拟机中安装docker服务并完成配置: https://cloud-atlas.readthedocs.io/zh-cn/latest/docker/colima/colima_proxy.html
colima start --runtime containerd --cpu 2 --memory 4

# 如果不指定runtime,会要求在macOS上 'brew install docker' ,我没有使用这个方法
# colima start --cpu 2 --memory 4
  • 这里运行指定了 containerd运行时(runtime) 作为 runtime,所以Colima虚拟机内部只有 containerd 一个服务,没有安装 Docker 。现在需要通过 colima ssh 登陆到Colima虚拟机内部完成 docker 服务安装:

登陆到Colima虚拟内部安装docker服务
apt update && apt -y upgrade && apt autoremove

# 安装docker服务来支持代理配置
apt -y install docker.io

Host主机代理准备

Colima使用的代理通道是在Host主机(macOS)上创建的,在macOS上执行以下命令构建 SSH隧道 :

代理配置注入虚拟机

Host主机环境变量方法

如果在执行 colima start 命令时,Host主机的环境变量中包含代理配置,例如:

环境变量中包含代理配置
export http_proxy="http://127.0.0.1:3128"
export https_proxy="http:127.0.0.1:3128"
export no_proxy="localhost,127.0.0.1,*.baidu.com,192.168.0.0/16,10.0.0.0/8"

Colima虚拟机配置文件方法

上述通过Host主机环境变量注入proxy配置的方法要求Host主机shell环境中必须有代理配置,如果这个环境变量在启动colima之前被修改就会不起作用(如果环境变量是临时手工输入的)。所以,更为可靠的方法是修订Colima配置。

  • 修改 ~/.colima/default/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

代理配置注入虚拟机的分析和要点

完成上述两种配置方法之一以后,启动的 default Colima虚拟机会自动注入代理配置,也就是 colima ssh 登陆到虚拟机内部检查 env | grep -i proxy 会看到和Host主机一样的配置:

colima 虚拟机内部检查 env 输出可以看到注入的代理配置
no_proxy=localhost,127.0.0.1,*.baidu.com,192.168.0.0/16,10.0.0.0/8
NO_PROXY=localhost,127.0.0.1,*.baidu.com,192.168.0.0/16,10.0.0.0/8
HTTPS_PROXY=http://192.168.5.2:3128
HTTP_PROXY=http://192.168.5.2:3128
https_proxy=http://192.168.5.2:3128
http_proxy=http://192.168.5.2:3128

上述配置是 colima 虚拟机启动时自动添加到虚拟机内部的 /etc/environment 中实现的,即虚拟机内部可以看到动态在 /etc/environment 中添加了如下配置行:

colima 虚拟机内部 /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=localhost,127.0.0.1,*.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=localhost,127.0.0.1,*.baidu.com,192.168.0.0/16,10.0.0.0/8
#LIMA-END

备注

注入的proxy配置的IP地址,在Host主机上的 127.0.0.1:3128 会自动转换成Colima虚拟机内部的 192.168.5.2:3128

这是正确的: 因为这个 192.168.5.2 代表了Host物理主机。此时只需要在Host物理主机上创建起 SSH隧道 连接墙外服务器的 Squid代理服务 代理端口,就能够 越过长城

警告

需要 注意 ,在Host主机上的待注入配置或环境变量配置 必须使用完整格式 ,也就配置项必须带上 http:// ,不可以是简化格式。我实践发现,如果使用以下简化配置(虽然在macOS的Host主机上工作正常):

在Host主机使用简化配置形式(不建议)
export http_proxy=127.0.0.1:3128
export https_proxy=127.0.0.1:3128

会影响 APT包管理 使用,我的实践发现,apt受到这个环境变量影响,但格式会转换导致不支持报错:

Unsupported proxy configured: 127.0.0.1://3128

Colima虚拟机内部容器服务的代理配置

备注

我最初在 Colima代理(文章归档) 实践中,因为不确定是 Docker 服务还是 containerd运行时(runtime) 需要代理设置,所以两者都做了配置。但是我现在重新对比验证确认:

  • 只需要诶之docker服务的proxy代理 环境变量就可以实现镜像通过代理下载

  • containerd运行时(runtime)nerdctl 不支持代理,所以之前实践中配置containerd的 http_proxyhttps_proxy 环境变量实际上并没有生效(我对比了)

在Colima虚拟机内部运行了 docker 服务(containerd不需要配置),需要为这个容器服务配置 Systemd进程管理器 服务的环境变量来传递代理配置

  • colima ssh 登陆到Colima虚拟机内部,执行以下命令创建 docker 服务的环境配置:

生成 /etc/systemd/system/docker.service.d/http-proxy.conf 为docker服务添加代理配置
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
  • 现在在Colima虚拟机内部,执行以下命令检查 docker 服务环境配 :

通过 systemctl show 检查 Environment 属性
# systemctl show docker --property Environment
Environment=HTTP_PROXY=http://192.168.5.2:3128 HTTPS_PROXY=http://192.168.5.2:3128 "NO_PROXY=localhost,127.0.0.1,*.baidu.com,192.168.0.0/16,10.0.0.0/8,"

输出显示 docker 都已经具备了PROXY环境配置

docker客户端配置

备注

实践验证,当Colima虚拟机已经注入了 http_proxyhttps_proxy 代理配置环境变量,依然要配置docker客户端的 config.json

docker镜像的下载要通过proxy,不仅需要 dockerd 配置代理, docker 客户端程序也需要配置代理。这是因为:

  • docker的meta元数据是通过 docker 客户端下载的

  • docker容器内部需要注入代理配置,否则容器内部的部分安装执行(被墙)会无法完成

备注

docker镜像内部注入代理配置也可以在 Dockerfile 中配置:

在Dockerfile内添加环境变量
# 我觉得比较灵活的方式还是配置Docker客户端 ~/.docker/config.json
ENV HTTP_PROXY "http://172.17.0.1:3128"
ENV HTTP_PROXY "http://172.17.0.1:3128"
ENV FTP_PROXY "ftp://172.17.0.1:3128"
ENV NO_PROXY "*.baidu.com,192.168.0.0/16,10.0.0.0/8"
  • colima 虚拟机内部配置 ~/.docker/config.json :

配置 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"
   }
 }
}

构建容器

采用 Debian镜像(tini进程管理器)debian-dev :

  • debian-dev 包含了安装常用工具和开发环境:

包含常用工具和开发环境的debian镜像Dockerfile
FROM debian:latest

ENV container=docker

# 对于墙内用户需要构建完善的翻墙代理才能顺利执行这个Dockerfile
# 详情参考 https://cloud-atlas.readthedocs.io/zh-cn/latest/docker/colima/colima_proxy.html

RUN apt clean
RUN apt update -y
RUN apt upgrade -y

# Debian仓库内置tini,可以直接安装
RUN apt -y install tini

# Copy tini entrypoint script
COPY entrypoint_ssh_cron_bash /entrypoint.sh
RUN chmod +x /entrypoint.sh

# SSH
RUN apt -y install sudo passwd openssh-client openssh-server curl
# Utilities
RUN apt -y install cron tmux vim-tiny locales net-tools iproute2 dnsutils plocate gnupg2 git tree unzip lsof wget graphviz

# 补全locales
RUN echo "LC_ALL=en_US.UTF-8" >> /etc/environment
RUN echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
RUN echo "LANG=en_US.UTF-8" > /etc/locale.conf
RUN locale-gen en_US.UTF-8

# c program
RUN apt -y install glibc-doc manpages-dev libc6-dev gcc build-essential
# 编译neovim需要
RUN apt -y install cmake gettext
# ruby program install rvm
# python program (debian already inatalled python3)
RUN apt -y install python3.11-venv
# go program
#RUN apt -y install golang
RUN wget https://go.dev/dl/go1.24.3.linux-amd64.tar.gz
RUN tar -C /usr/local -xzf go1.24.3.linux-amd64.tar.gz

RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers

# add account "admin" and give sudo privilege
RUN groupadd -g 501 admin
RUN useradd -g 501 -u 501 -s /bin/bash -d /home/admin -m admin
RUN adduser admin sudo
RUN echo "%sudo        ALL=(ALL)       NOPASSWD: ALL" >> /etc/sudoers

# set TIMEZONE to Shanghai
RUN unlink /etc/localtime
RUN ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

RUN mkdir /run/sshd
RUN ssh-keygen -A

USER admin

# ruby program: rvm install ruby in $HOME
RUN gpg2 --keyserver hkp://keyserver.ubuntu.com --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
# /bin/sh 不支持source,需要使用 . 
# 每个RUN都是一个容器,所以为了继承环境变量来运行多个命令,将这些命令写成一行RUN
# rvm需要使用BASH,所以最终改成 bash -c 'xxx && yyy && zzz'
RUN curl -sSL https://get.rvm.io | bash -s stable
RUN bash -c 'source /home/admin/.rvm/scripts/rvm && rvm install 3.4.3 && gem install rails'
# fix GEM_HOME
RUN bash -c 'echo "export GEM_HOME=/home/admin/.rvm/gems/ruby-3.4.3/bin" >> /home/admin/.bashrc'

# 我使用Jekyll构建个人blog,如不需要请注释掉下面一行
# 如果报错 "gem: command not found" (不能通过 "source /home/admin/.bashrc"),改成 "source /home/admin/.rvm/scripts/rvm"
# RUN bash -c 'source /home/admin/.rvm/scripts/rvm && gem install bundler jekyll'

# python program: virtualenv
RUN bash -c 'cd /home/admin && python3 -m venv venv3'
# 我使用Sphinx doc来撰写Cloud-Atlas文档,如不需要请注释掉下面两行
RUN bash -c 'source /home/admin/venv3/bin/activate && pip install sphinx sphinx_rtd_theme sphinxnotes-strike sphinxcontrib-video sphinxcontrib-youtube myst-parser jieba'

# node program: nvm install node (目前采用v22 LTS系列)
RUN bash -c "cd /home/admin && curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash" 
RUN bash -c "source /home/admin/.nvm/nvm.sh && nvm install 22"

# react + next.js + nextra
RUN bash -c "source /home/admin/.nvm/nvm.sh && npm i next react react-dom nextra nextra-theme-docs"

# go program: setup $GOPATH
RUN bach -c 'echo "export PATH=$PATH:/usr/local/go/bin" >> /home/admin/.bashrc' 

# rust program
RUN bash -c "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y"

# neovim
# 当git使用operations over HTTP时,实际使用的是curl library,所以注入容器的 http_proxy https_proxy就能够生效
RUN bash -c 'mkdir /home/admin/src && source /home/admin/.bashrc && cd /home/admin/src && git clone https://github.com/neovim/neovim.git && cd /home/admin/src/neovim && make CMAKE_EXTRA_FLAGS="-DCMAKE_INSTALL_PREFIX=$HOME/neovim" && make install && echo "export PATH=\"\$HOME/neovim/bin:\$PATH\"" >> /home/admin/.bashrc && echo alias vi=\"\$HOME/neovim/bin/nvim\" >> /home/admin/.bashrc'
RUN bash -c 'cd /home/admin/src && git clone https://github.com/huataihuang/cloud-studio.git && cd /home/admin/src/cloud-studio/config && sh install.sh'
RUN rm -rf /home/admin/src

# entrypoint.sh 需要使用root身份执行
USER root

# run service when container started - sshd
EXPOSE 22:1122
# next.js nextra
EXPOSE 3000:13000
# Sphinx
EXPOSE 8080:18080
# Jekyll
EXPOSE 4000:14000
# HTTP
EXPOSE 80:1180
# HTTPS
EXPOSE 443:1443

# Run your program under Tini
# CMD ["/your/program", "-and", "-its", "arguments"]
CMD ["/entrypoint.sh"]
  • 构建 debian-dev 镜像:

构建包含开发环境的debian镜
docker build --rm -t debian-dev .
  • 运行 dev :

运行包含开发环境的debian容器
docker run -dt --name dev --hostname dev \
    -p 1122:22 \
    -p 13000:3000 \
    -p 18080:8080 \
    -p 14000:4000 \
    -p 1180:80 \
    -p 1443:443 \
    -v /Users/admin:/Users/admin \
    -v /Users/admin/secret:/home/admin/.ssh \
    debian-dev:latest

# 物理主机的 /Users/admin/secret 存放需要映射进容器的密钥以及ssh配置

# 如果需要在运行时注入环境变量,则添加类似如下参数(添加代理案例)
#    -e HTTP_PROXY=http://172.17.0.1:3128 \
#    -e HTTPS_PROXY=http://172.17.0.1:3128 \
#    -e NO_PROXY=localhost,127.0.0.1,*.baidu.com,192.168.0.0/16,10.0.0.0/8 \

此时在Colima中运行的容器 dev 可以在macOS的Host主机上直接访问(端口1122):

ssh admin@127.0.0.1 -p 1122
  • dev 容器中检查就可以看到几乎和Host主机完全一致的HOME目录访问,所有文件都具备,非常方便融合Linux+macOS工作