Debian镜像(tini进程管理器)

Debian官方仓库已经提供了 Docker tini进程管理器 ,这意味着无需单独复制 tini (类似 Fedora镜像(采用tini替代systemd) 就不得不从容器外部复制对应的 tini 到镜像中)

dockerhub: debian 提供官方镜像(需要梯子):

  • 当前 debian.org 最新版本是 bookworm (12.6),通过 tag 关键字 bookwormlatest 引用

tini运行ssh debian-ssh-tini

具备ssh服务的debian镜像Dockerfile
FROM debian:latest

ENV container=docker

RUN apt update -y
RUN apt upgrade -y

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

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

# Fedora docker image not include systemd,install systemd by initscripts
RUN apt -y install sudo passwd openssh-client openssh-server curl

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

# add account "admin" and give sudo privilege
RUN groupadd -g 505 admin
RUN useradd -g 505 -u 505 -d /home/admin 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

# run service when container started - sshd
EXPOSE 22:1122

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

构建包含tini和ssh的debian镜像
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代理 来解决无法访问问题

  • 运行容器: 注意这里继承了 Colima存储管理 中两个HOST物理主机卷映射到容器内部,以便提供SSH密钥以及工作 docs 目录

运行容器挂载2个从HOST主机映射到colima虚拟机的卷(这样可以直接访问HOST主机数据)
docker run -dt --name debian-ssh-tini --hostname debian-ssh-tini \
    -p 1122:22 \
    -v /Users/huatai/secrets:/home/admin/.ssh \
    -v /Users/huatai/docs:/home/admin/docs \
    debian-ssh-tini:latest
  • 一切顺利,现在在HOST主机上配置 ~/.ssh/config 添加访问debian容器的SSH登陆(端口1122):

~/.ssh/config 添加访问debian容器的SSH登陆
Host debian
  HostName 127.0.0.1
  User admin
  Port 1122

备注

HOST主机的 ~/secrets 包含了SSH公钥,所以如果被正确挂载到容器的 ~/.ssh 目录,就能够无需密码登陆容器

现在 ssh debian 就能够登陆到运行的容器中,并且执行 df -h 可以看到如下输出,显示HOST主机的存储卷被正确挂载到容器内部(包括登陆密钥):

登陆容器检查卷挂载
Filesystem      Size  Used Avail Use% Mounted on
overlay          58G  1.8G   56G   4% /
tmpfs            64M     0   64M   0% /dev
shm              64M     0   64M   0% /dev/shm
/dev/root        58G  1.8G   56G   4% /etc/hosts
mount1          234G  149G   86G  64% /home/admin/.ssh
mount2          234G  149G   86G  64% /home/admin/docs
tmpfs           3.9G     0  3.9G   0% /proc/acpi
tmpfs           3.9G     0  3.9G   0% /proc/scsi
tmpfs           3.9G     0  3.9G   0% /sys/firmware

开发环境 debian-dev

debian-ssh-tini 基础上,增加开发工具包安装:

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

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

ENV container=docker

# 容器内代理设置: 物理服务器上通过SSH Tunnel访问墙外HTTP/HTTPS代理,所以IP地址是Docker的NAT网络网关IP
# 如不需要请注释掉
# 不过我觉得比较灵活的方式还是配置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"

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

# 补全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

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

# add account "admin" and give sudo privilege
RUN groupadd -g 505 admin
RUN useradd -g 505 -u 505 -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
RUN curl -sSL https://get.rvm.io | bash -s stable
# /bin/sh 不支持source,需要使用 . 
# 每个RUN都是一个容器,所以为了继承环境变量来运行多个命令,将这些命令写成一行RUN
# rvm需要使用BASH,所以最终改成 bash -c 'xxx && yyy && zzz'
RUN bash -c 'source /home/admin/.rvm/scripts/rvm && rvm install 3.3.4 && gem install rails'

# 我使用Jekyll构建个人blog,如不需要请注释掉下面一行
RUN bash -c 'source /home/admin/.bashrc && gem install bundler jekyll'
# 如果报错 "gem: command not found" (我在debian的ARM镜像时遇到),则改成 "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 && pip install sphinx_rtd_theme && pip install sphinxnotes-strike'
RUN bash -c 'source /home/admin/venv3/bin/activate && pip install sphinxcontrib-video && pip install sphinxcontrib-youtube'

# node program: nvm install node
RUN bash -c "cd /home/admin && curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash" 
RUN bash -c "source /home/admin/.bashrc && nvm install 22"
# 如果报错 "nvm: command not found" (在debian的ARM镜像时遇到),则改成 "source /home/admin/.nvm/nvm.sh" 来加载nvm (参考 ~/.bashrc )
# RUN bash -c "/home/admin/.nvm/nvm.sh && nvm install 22"

# neovim
# 由于容器镜像构建时需要使用HTTP代理,所以这里配置git通过代理clone,如果没有GFW干扰或者运行时没有遇到类似 "error: RPC failed; curl 18 Transferred a partial file" 报错,则不需要添加git代理配置
#RUN git config --global http.proxy http://172.17.0.1:3128
# 当git使用operations over HTTP时,实际使用的是curl library
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
# 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 .
  • 运行 debian-dev :

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

# 如果需要在运行时注入环境变量,则添加类似如下参数(添加代理案例)
#    -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 \

问题排查记录

apt安装找不到软件包

我在 amd64 (x86_64)平台构建 debian-dev 成功,但是当我将同样的Dockerfile在 树莓派Raspberry Pi OS(64位)安装Docker 却遇到无法安装 tini 报错:

在树莓派 Raspberry Pi OS上构建Docker镜像遇到无法找到软件包报错
...
 => CACHED [ 2/36] RUN apt update -y                                                                                                                                                                            0.0s
 => CACHED [ 3/36] RUN apt upgrade -y                                                                                                                                                                           0.0s
 => ERROR [ 4/36] RUN apt -y install tini                                                                                                                                                                       0.6s
------
 > [ 4/36] RUN apt -y install tini:
0.363
0.363 WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
0.363
0.366 Reading package lists...
0.373 Building dependency tree...
0.374 Reading state information...
0.375 E: Unable to locate package tini
------
Dockerfile:17
--------------------
  15 |
  16 |     # Debian仓库内置tini,可以直接安装
  17 | >>> RUN apt -y install tini
  18 |
  19 |     # Copy tini entrypoint script
--------------------
ERROR: failed to solve: process "/bin/sh -c apt -y install tini" did not complete successfully: exit code: 100

我最初以为是 Docker 代理快速起步 设置代理网关错误(我确实错误设置了 127.0.0.1:3128 ,正确应该是 172.17.0.1:3128 ),但是实践发现即使修正了代理网关IP也同样报错。最后我发现,原来ARM版本的debian官方镜像似乎有仓库配置残留,在Dockerfile中添加 apt clean 步骤清理现场,然后执行升级和安装就能够正常工作。(已修正上文 debian-dev Dockerfile)

git下载出现curl报错"RPC failed; curl 18 Transferred a partial file"

我在 树莓派Raspberry Pi OS(64位)安装Docker 上执行构建(家里的网络远不如阿里云虚拟机公网),反复出现curl报错:

git clone 时始终出现 "RPC failed; curl 18 Transferred a partial file" 错误
 => ERROR [35/37] 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 && export http_proxy   83.3s
------
 > [35/37] 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 && export http_proxy && export https_proxy=\$http_proxy && 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':
0.340 Cloning into 'neovim'...
83.19 error: RPC failed; curl 18 Transferred a partial file
83.19 error: 1183 bytes of body are still expected
83.19 fetch-pack: unexpected disconnect while reading sideband packet
83.19 fatal: early EOF
83.19 fatal: fetch-pack: invalid index-pack output
------
Dockerfile:84
--------------------
  82 |
  83 |     # neovim
  84 | >>> 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 && export http_proxy && export https_proxy=\$http_proxy && 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'
  85 |     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'
  86 |     RUN rm -rf /home/admin/src
--------------------
ERROR: failed to solve: process "/bin/sh -c 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 && export http_proxy && export https_proxy=\\$http_proxy && 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'" did not complete successfully: exit code: 128

注意,这里报错的 error: 1183 bytes of body are still expected 每次可能数值不同,看起来就是传输错误

我尝试增加 git config --global http.proxy http://172.17.0.1:3128 这样可以确保 git 通过代理访问 HTTPS 来解决,但是我发现并没有解决上述报错。既然 git 在执行 operations over HTTP 实际使用的是curl库,所以我也尝试了 echo "proxy=172.17.0.1:3128" > /home/admin/.curlrc 来指定curl代理,但是此时报错:

git clone 配置 ~/.curlrc 指定代理,但是依然出现TLS报错,看起来是代理无法通过,还是代理问题
 => [37/38] RUN echo "proxy=172.17.0.1:3128" > ~/.curlrc                                                                                                                                                        0.5s
 => ERROR [38/38] RUN bash -c 'mkdir /home/admin/src && source /home/admin/.bashrc && cd /home/admin/src && git clone https://github.com/neovim/neovim.git'                                                    82.7s
------
 > [38/38] RUN bash -c 'mkdir /home/admin/src && source /home/admin/.bashrc && cd /home/admin/src && git clone https://github.com/neovim/neovim.git':
0.525 Cloning into 'neovim'...
82.65 error: RPC failed; curl 56 GnuTLS recv error (-9): Error decoding the received TLS packet.
82.65 error: 2389 bytes of body are still expected
82.65 fetch-pack: unexpected disconnect while reading sideband packet
82.65 fatal: early EOF
82.65 fatal: fetch-pack: invalid index-pack output
------
Dockerfile:89
--------------------
  87 |     # RUN git config --global http.proxy http://172.17.0.1:3128
  88 |     RUN echo "proxy=172.17.0.1:3128" > ~/.curlrc
  89 | >>> RUN bash -c 'mkdir /home/admin/src && source /home/admin/.bashrc && cd /home/admin/src && git clone https://github.com/neovim/neovim.git'
  90 |     #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'
  91 |     #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'
--------------------
ERROR: failed to solve: process "/bin/sh -c bash -c 'mkdir /home/admin/src && source /home/admin/.bashrc && cd /home/admin/src && git clone https://github.com/neovim/neovim.git'" did not complete successfully: exit code: 128

参考 Github - unexpected disconnect while reading sideband packet 提示是网络不稳定导致的,可以尝试:

设置git环境变量跟踪错误
export GIT_TRACE_PACKET=1
export GIT_TRACE=1
export GIT_CURL_VERBOSE=1

不过输出信息其实和之前报错是一样的

git ssh最终解决方法

参考 Error Cloning Repository: RPC Failed; curl 18 transfer closed with outstanding read data remaining #18972 : 使用 ssh 替代 https 来进行 git clone ,可以解决网络传输问题。(应该可行,因为GFW没有屏蔽github的SSH)就是需要在Dockerfile中复制一个本地专用于git的SSH密钥,导致Dockerfile通用性较差。

不过,也不是很顺利,原因是每次访问github的HOST主机密钥变化,需要默认接受,否则会报错:

如果git没有默认接受github的主机ssh密钥,则会报错失败
 => ERROR [39/39] RUN bash -c 'mkdir /home/admin/src && source /home/admin/.bashrc && cd /home/admin/src && git clone git@github.com:neovim/neovim.git'                                                         2.9s
------
 > [39/39] RUN bash -c 'mkdir /home/admin/src && source /home/admin/.bashrc && cd /home/admin/src && git clone git@github.com:neovim/neovim.git':
0.915 Cloning into 'neovim'...
2.442 Host key verification failed.
2.442 fatal: Could not read from remote repository.
2.442
2.442 Please make sure you have the correct access rights
2.442 and the repository exists.
------
Dockerfile:94
--------------------
  92 |     # RUN echo "proxy=172.17.0.1:3128" > ~/.curlrc
  93 |
  94 | >>> RUN bash -c 'mkdir /home/admin/src && source /home/admin/.bashrc && cd /home/admin/src && git clone git@github.com:neovim/neovim.git'
  95 |
  96 |     #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'
--------------------
ERROR: failed to solve: process "/bin/sh -c bash -c 'mkdir /home/admin/src && source /home/admin/.bashrc && cd /home/admin/src && git clone git@github.com:neovim/neovim.git'" did not complete successfully: exit code: 128

参考 Git error: "Host Key Verification Failed" when connecting to remote repository 在执行git之前,首先添加github的主机密钥,避免脚本执行报错:

通过 ssh-keyscan 命令添加github主机认证密钥
ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts

另外一个要点是,用户git的私钥不能有密码保护(我通常个人使用都会给RAS密钥对的私钥加上密码),否则也会报错: git@github.com: Permission denied (publickey). ,所以需要单独为git准备一个 没有密码保护 的RSA密钥对:

在当前目录下生成没有密码保护的密钥对(这样脚本执行时无需输入保护密码)
ssh-keygen -t rsa

ARM版本 neovim 安装clangd LSP

由于 LLVM clangd 没有在官方提供clangd的ARM64 for Linux,所以需要通过发行版安装 clangd 来实现 NeoVim clangd ARM版本 :

通过安装发行版clangd解决debian ARM的NeoVim LSP
sudo apt install clangd-16
ln -s /usr/bin/clangd-16 ~/.local/share/nvim/mason/bin/clangd
mkdir ~/.local/share/nvim/mason/packages/clangd

开发环境 debian-dev (ARM64版本)

备注

实践在 树莓派Raspberry Pi 5 的环境中进行,为 树莓派软件定义存储集群 做准备

结合上述要点,参考 git SSH操作脚本 方法改进Dockerfile(不过由于SSH方法比较复杂,常规还是采用 git operations over HTTP,只有为了解决网络阻塞GFW的时候才使用SSH方法),以下是完整Dockerfile:

包含常用工具和开发环境的debian镜像Dockerfile(为解决GFW干扰采用SSH方法)
FROM debian:latest

ENV container=docker

# 容器内代理设置: 物理服务器上通过SSH Tunnel访问墙外HTTP/HTTPS代理,所以IP地址是Docker的NAT网络网关IP
# 如不需要请注释掉
# 不过我觉得比较灵活的方式还是配置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"

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

# 补全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

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

# add account "admin" and give sudo privilege
RUN groupadd -g 505 admin
RUN useradd -g 505 -u 505 -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
RUN curl -sSL https://get.rvm.io | bash -s stable
# /bin/sh 不支持source,需要使用 .
# 每个RUN都是一个容器,所以为了继承环境变量来运行多个命令,将这些命令写成一行RUN
# rvm需要使用BASH,所以最终改成 bash -c 'xxx && yyy && zzz'
RUN bash -c 'source /home/admin/.rvm/scripts/rvm && rvm install 3.3.4 && gem install rails'

# 我使用Jekyll构建个人blog,如不需要请注释掉下面一行
# RUN bash -c 'source /home/admin/.bashrc && gem install bundler jekyll'
# 如果报错 "gem: command not found" (我在debian的ARM镜像时遇到),则改成 "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 && pip install sphinx_rtd_theme && pip install sphinxnotes-strike'
RUN bash -c 'source /home/admin/venv3/bin/activate && pip install sphinxcontrib-video && pip install sphinxcontrib-youtube'

# node program: nvm install node
RUN bash -c "cd /home/admin && curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash"
# RUN bash -c "source /home/admin/.bashrc && nvm install 22"
# 如果报错 "nvm: command not found" (在debian的ARM镜像时遇到),则改成 "source /home/admin/.nvm/nvm.sh" 来加载nvm (参考 ~/.bashrc )
RUN bash -c "source /home/admin/.nvm/nvm.sh && nvm install 22"

# neovim

# 这个步骤是为了解决GFW屏蔽,使用git clone的 over HTTP报错,不得已采用SSH模式,所以会复制密钥,如果直接通过git operations over HTTP工作正常,则不需要这个步骤
# 注意,需要在Dockerfile相同目录下执行 ssh-keygen -t rsa 生成密钥对(私钥不可密码保护),并将公钥上传到github以便能够git clone

USER admin

#### 接受github的主机认证密钥
#### 可以通过SSH忽略主机密钥 -o StrictHostKeyChecking=no 绕过
## -------------------------------------------------------------
## RUN mkdir /home/admin/.ssh
## RUN ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts
## -------------------------------------------------------------

#### 采用git SSH方式
## -------------------------------------------------------------
RUN mkdir /home/admin/src
## COPY 命令复制的文件需要修改属主,否则ssh无法读取
COPY id_rsa /home/admin/src/id_rsa
USER root
RUN chown admin:admin /home/admin/src/id_rsa
USER admin
#### 指定git密钥,这里使用 --global 是因为当前没有在git项目目录下,不用这个参数会报错 "fatal: not in a git directory"
RUN git config --global core.sshCommand 'ssh -i /home/admin/src/id_rsa -o StrictHostKeyChecking=no'
#### git clone使用SSH方式是为了避免GFW干扰,使用git的operation over HTTP在代理模式下存在curl RPC报错
RUN bash -c 'source /home/admin/.bashrc && cd /home/admin/src && git clone git@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 git@github.com:huataihuang/cloud-studio.git && cd /home/admin/src/cloud-studio/config && sh install.sh'
RUN rm -rf /home/admin/src
RUN rm /home/admin/.gitconfig
#### ARM版本debian需要安装clangd以便完成Mason的clangd LSP支持
USER root
RUN apt -y install clangd-16
USER admin
RUN mkdir -p /home/admin/.local/share/nvim/mason/bin
RUN ln -s /usr/bin/clangd-16 /home/admin/.local/share/nvim/mason/bin/clangd
RUN mkdir /home/admin/.local/share/nvim/mason/packages/clangd

#### 使用git operations over HTTP较为简洁,但是会受到网络干扰影响出现 "error: RPC failed; curl 18 Transferred a partial file"
## -------------------------------------------------------------
# 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
# 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"]
  • 构建 acloud-dev 镜像:

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

运行包含开发环境的ARM环境debian镜像
docker run -dt --name acloud-dev --hostname acloud-dev \
    -p 1122:22 \
    -p 18080:8080 \
    -p 14000:4000 \
    -p 1180:80 \
    -p 1443:443 \
    -v /home/admin/secrets:/home/admin/.ssh \
    -v /home/admin/docs:/home/admin/docs \
    acloud-dev

# 如果需要在运行时注入环境变量,则添加类似如下参数(添加代理案例)
#    -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 \