Ubuntu镜像(采用tini替代systemd)

备注

由于使用 从Dockerfile构建Docker镜像 重复构建镜像时,需要重复在容器内部执行各种软件包安装下载,非常消耗时间。为了能够节约部署时间(特别是局域网内部),我采用在 Kubernetes部署Squid 在我的 kind(本地docker模拟k8s集群) ( macOS工作室 ) ,这样不断重复相同操作系统的安装升级可以节约时间和带宽。

我构建基于 Ubuntu Linux 的Android开发环境,基于 Ubuntu镜像(采用tini替代systemd) 修改,来实现一个轻量级的Ubuntu通用开发环境。这样我的实践就覆盖了 RedHat Linux 系(rpm)和 Debian系(apt)两个主流发行版的Docker镜像。

准备工作

由于Docker镜像制作需要反复测试,容器内部OS更新和软件安装会不断重复,所以为了加快进度和节约带宽,采用 Docker客户端的Proxy 配置来加速: (注意:这里首先需要在docker集群中部署 Docker环境运行Squid 代理容器,然后通过 Docker客户端的Proxy 配置为所有docker容器注入使用代理服务器访问外网来加速下载)

  • 修改 ~/.docker/config.json :

采用默认docker网络 bridge 上部署的 Docker环境运行Squid 代理
{
  "credsStore": "desktop",
  "proxies":
  {
    "default":
    {
      "httpProxy": "http://172.17.0.3:3128",
      "httpsProxy": "http://172.17.0.3:3128",
      "noProxy": "*.baidu.com,192.168.0.0/16,10.0.0.0/8"
    }
  }
}

基础运行 ubuntu-base-tini

  • 初始构建一个基础Ubuntu:

基础Ubuntu镜像Dockerfile

  • 构建 ubuntu-base-tini 镜像:

构建基础Ubuntu镜像Dockerfile

  • 运行 ubuntu-base-tini 镜像:

运行ubuntu-base-tini容器

  • 连接到 ubuntu-base-tini 容器内:

通过docker exec运行容器内部bash

tini运行ssh ubuntu-ssh-tini

备注

How to enable SSH connections into a Kubernetes pod 提供了一个更好的部署SSH key的 使用ConfigMap配置Pod 方法,适合对不同用户在部署pods时候注入SSH key和构建用户HOME目录,后续借鉴实践。

我改进了之前在 Fedora镜像(采用tini替代systemd) 的方案: 原先是直接在制作镜像时直接将公钥注入到镜像中,这个方法虽然简单但是比较笨拙(固化),所以我现在改为通过卷映射方式,将本地物理主机上的目录 home/ 映射到容器内部:

  • 可以灵活替换和修改密钥(也方便作为通用方案)

  • 兼顾了用户目录数据持久化: 卷映射可以避免容器销毁后数据丢失,重建容器也可以保持数据。此外,如果多个容器共享一个映射目录,可以互相传递数据

按照 Docker tini进程管理器 经验总结,实现一个初始完备的远程可登录 Ubuntu Linux 系统:

具备ssh服务的ubuntu镜像Dockerfile
FROM ubuntu:latest
MAINTAINER vincent huatai <vincent@huatai.me>

ENV container docker

RUN apt update -y
RUN apt upgrade -y

# Add Tini
ENV TINI_VERSION v0.19.0
# 标准方法是采用ADD方式向镜像添加tini,但是GFW阻碍,改为下载后本地复制
#ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
COPY tini /tini
RUN chmod +x /tini
ENTRYPOINT ["/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 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:1123

# Run your program under Tini
# CMD ["/your/program", "-and", "-its", "arguments"]
CMD ["/entrypoint.sh"]

备注

  • mkdir /run/sshd 是因为Ubuntu官方镜像是不提供ssh服务,缺少这个目录会导致 sshd 启动失败,所以补充创建

  • ssh-keygen -Asshd 运行创建主机密钥对

  • 没有如 Fedora镜像(采用tini替代systemd) 直接注入 admin 用户密钥,而是在 docker run 是将物理主机用户目录bind到虚拟机内部提供卷,这样可以更灵活也保证了数据安全

  • 使用的 entrypoint.sh 脚本是 kind部署 fedora-dev-tini (tini替代systmed) 实践中改进过的脚本:

/entrypoint.sh 脚本的 main() 确保持续运行(循环)
#!/usr/bin/env bash

sshd() {
    chown -R admin:admin /home/admin
    /usr/sbin/sshd
}

crond() {
    /usr/sbin/cron
}

main() {
    sshd
    crond
    # 在k8s不能直接bash执行结束,否则判断为pod Crash,需要改写成持续执行循环脚本
    #/bin/bash
    /bin/bash -c "while true; do (echo 'Hello from tini'; date; sleep 120); done"
}

main

备注

  • 在运行 sshd 的函数中加了 chown -R admin:admin /home/admin 是因为 docker run 将物理主机 home 目录映射到容器内部后,默认的属主是 root ,这里为了能够使用普通用户账号 admin 所以在容器初始化时候修订一次属主

  • Ubuntu Linux 安装cron的包名字就是 cron ,安装后运行程序就是 /usr/sbin/cron ,这个和 Fedora 不同

  • 如果在 docker 中运行最后执行的命令可以是 /bin/bash ,但是为了能够在 ;ref:kubernetes 中通用最后必须是一个永续执行的程序,这里是循环,如果是应用容器则改为应用程序

  • 构建 ubuntu-ssh-tini 镜像:

构建包含tini和ssh的ubuntu镜像
docker build --rm -t ubuntu-ssh-tini .
  • 运行 ubuntu-ssh-tini :

运行包含tini和ssh的ubuntu容器
. ../../../etc/environment

docker run -dt --name ubuntu-ssh-tini --hostname ubuntu-ssh-tini \
    -p 1123:22 \
    -v ${BASE_DIR}/home:/home \
    ubuntu-ssh-tini:latest

备注

这里运行docker容器的命令:

  • . ../../../etc/environment 是为了获取 ${BASE_DIR} 变量确定工作目录

  • -v ${BASE_DIR}/home:/home 将物理主机的home目录映射到容器内部以便数据持久化

备注

除了docker容器以外, systemd-nspawn 也可以构建轻量级容器(类似chroot)

开发环境 ubuntu-dev-tini

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

  • ubuntu-dev-tini 包含了安装常用工具和开发环境,并编译和安装必要的vim环境:

包含常用工具和开发环境的ubuntu镜像Dockerfile

ubuntu-dev-tini 镜像说明

功能

参考

docker容器运行ssh服务

Docker systemd进程管理器

用户目录ssh配置

ssh密钥

ubuntu开发环境

Ubuntu Linux

  • 构建 ubuntu-dev-tini 镜像:

构建包含开发环境的ubuntu镜像

  • 运行 ubuntu-dev-tini :

运行包含开发环境的ubuntu容器