Colima容器化开发环境

备注

在colima环境中快速构建了一个基于 Debian 的开发容器,综合整理作为快速指南

macOS 平台安装colima
brew install colima
使用 vz 模式虚拟化的 4c8g 虚拟机运行 colima
colima start --runtime containerd --cpu 4 --memory 8 --vm-type=vz

备注

如果是早期的Intel架构mac,则不支持 --vm-type 参数,原因是只有Apple Silicon架构才支持 Apple Virtualization (VZ)。所以实际上在Intel架构mac,还需要安装 qemu 来运行虚拟化 Lima: Linux Machines

colima 存储挂载配置 docssecrets
# Configure volume mounts for the virtual machine.
# Colima mounts user's home directory by default to provide a familiar
# user experience.
#
# EXAMPLE
# mounts:
#   - location: ~/secrets
#     writable: false
#   - location: ~/projects
#     writable: true
#
# Colima default behaviour: $HOME and /tmp/colima are mounted as writable.
# Default: []
mounts:
  - location: ~/secrets
    writable: false
  - location: ~/docs
    writable: true
  • 确保发起启动的用户的环境变量如下(配置到 ~/.zshrc 中,或者直接在SHELL中执行):

macOS的host环境 colima start 用户的环境变量配置代理
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 服务,此时会挂载HOST主机上指定目录,并且注入HOST主机的代理配置

重启 colima 虚拟机
colima stop
colima start

进入虚拟机( colima ssh ) 可以看到目录挂载:

colima 虚拟机内部通过 df -h 检查 docs 目录映射
Filesystem      Size  Used Avail Use% Mounted on
/dev/root        58G  1.4G   56G   3% /
tmpfs           3.9G     0  3.9G   0% /dev/shm
tmpfs           1.6G  736K  1.6G   1% /run
tmpfs           5.0M     0  5.0M   0% /run/lock
efivarfs         56K  5.7K   46K  12% /sys/firmware/efi/efivars
mount0          234G  123G  111G  53% /Users/huatai/Library/Caches/colima
mount1          234G  123G  111G  53% /Users/huatai/secrets
/dev/vda16      881M   42M  778M   6% /boot
/dev/vda15      105M  6.1M   99M   6% /boot/efi
mount2          234G  123G  111G  53% /Users/huatai/docs
tmpfs           794M  4.0K  794M   1% /run/user/501
/dev/vdb         39M   39M     0 100% /mnt/lima-cidata

进入虚拟机( colima ssh )检查 /etc/environment 可以看到代理配置:

Colima启动时会自动将HOST物理主机proxy环境变量注入到虚拟机 /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
  • (物理主机)在HOST主机上 SSH隧道 构建一个本地到远程服务器代理服务端口(服务器上代理服务器仅监听回环地址)的SSH加密连接。我实际采用的是在 ~/.ssh/config 配置如下:

~/.ssh/config 配置 SSH隧道 构建一个本地到远程服务器Proxy端口加密连接
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构建了本地的一个SSH Tunneling到远程服务器的 Proxy代理服务 服务
ssh MyProxy
  • 登陆到 colima 虚拟机内部:

通过SSH登陆到colima虚拟机内
colima ssh
  • 执行以下命令,将 colima 虚拟机内部的 dockercontainerd 都配置为通过代理访问internet,这样才能正确下载镜像:

生成 /etc/systemd/system/docker.service.d/http-proxy.conf 为containerd添加代理配置
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
生成 /etc/systemd/system/containerd.service.d/http-proxy.conf 为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 虚拟机内部用户的 docker 客户端配置代理(部分meta信息是通过docker客户端下载的),这里的 docker 本地用户是 huatai ,在这个用户身份下配置 ~/.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进程管理器) 构建,执行的是 dev 容器构建:

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

包含常用工具和开发环境的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-tini 镜像:

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

运行包含开发环境的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 \