Docker Engine - Containerd
1、背景
Docker 崛起
很久以前,Docker 强势崛起,以 “镜像” 这个大招席卷全球,对其他技术进行致命的降维打击,使其毫无招架之力,就连 Google 也不例外。Google 为了不被拍死在沙滩上,被迫拉下脸面,希望 Docker 公司和自己联合推进一个开源的容器运行时作为 Docker 的核心依赖,然而 Docker 公司的这个决策断送了自己的大好前程,造成了今天的悲剧。
libcontainer 捐赠
Google 联合 Red Hat、IBM 等几位巨佬将 Docker 公司 libcontainer 捐给中立的社区(OCI,Open Container Intiative),并改名为 runc。
CNCF(云原生计算基金会)
为了彻底扭转 Docker 一家独大的局面,几位大佬又合伙成立了 CNCF(Cloud Native Computing Fundation),CNCF 的目标很明确,既然容器的的维度上比不过 Docker,干脆升级到大规模容器编排的维度,以此来击败 Docker。造成了 Swarm 和 Kubernetes PK 的场面,最后的结局 Swarm 战败。
然后 Docker 公司将自己的核心依赖 Containerd 捐给了 CNCF,以此来标榜 Docker 是一个 PaaS 平台。很明显,这个小聪明又加速了 Docker 的失败。
为了表示 Kubernetes 的中立性,当然要有个标准化的容器运行时接口,只要适配了这个接口的容器运行时,都可以和 Kubernetes 集成,第一个支持这个接口的当然就是 Containerd。至于这个标准化的容器运行时接口的名字,它叫 CRI(Container Runntime Interface)。
温水煮青蛙,养肥了再杀
Kubernetes 在自己的组件中集成了一个 Shim,用来将 CRI 的调用翻译成 Docker 的 API,让 Docker 也能和 Kubernetes 集成。就这样,Kubernetes 一边和 Docker 继承,一边不断优化 Containerd 的健壮性以及和 CRI 对接的丝滑性。现在 Containerd 的兼容性已经很好的情况下,是时候和 Docker say bye bye 了,最终在 Kubernetes 1.24 版本中从 Kubelet 中删除了 Dockershim 的代码,至此 Kubernetes 彻底舍弃 Docker。时至今日
时至今日,Containerd 已经变成一个工业级的容器运行时了,连口号都有了:超简单!超健壮!可移植性超强!
当然,为了不让 Docker 误会,Containerd 声称自己的设计目的主要是为了嵌入到一个更大的系统中(暗指 Kubernetes),而不是直接由开发人员或终端用户使用。
事实上,Containerd 现在基本上啥都能干了,开发人员或者终端用户可以在宿主机中管理完整的容器生命周期,包括容器镜像的传输和存储、容器的执行和管理、存储和网络等。
2、Containerd 架构
Containerd 仍然采用标准的 C/S 架构,服务端通过 GRPC 协议提供稳定的 API,客户端通过调用服务端的 API 进行高级的操作。
为了解耦,Containerd 将不同的职责划分给不同的组件,每个组件就相当于一个子系统(subsystem),连接不同子系统的组件被称为模块。
总体上 Containerd 被划分为两个子系统:
Bundle
在 Containerd 中,Bundle 包含了配置、元数据和根文件系统数据,你可以理解为容器的文件系统。而 Bundle 子系统允许用户从镜像中提取和打包 Bundles。Runtime
Runtime 子系统用来执行 Bundles,比如创建容器。
其中,每一个子系统的行为都由一个或多个模块协作完成。每一种类型的模块都以插件的形式集成到 Containerd 中,而且插件之间是相互依赖的。例如,上图中的每一个长虚线的方框都表示一种类型的插件,包括 Service Plugin
、Metadata Plugin
、GC Plugin
、Runtime Plugin
等,其中 Service Plugin
又会依赖 Metadata Plugin
、GC Plugin
和 Runtime Plugin
。每一个小方框都表示一个细分的插件,例如 Metadata Plugin
依赖 Containers Plugin
、Content Plugin
等。总之,万物皆插件,插件就是模块,模块就是插件。
Content Plugin
提供对镜像中可寻址内容的访问,所有不可变的内容都被存储在这里。Snapshot Plugin
用来管理容器镜像的文件系统快照。镜像中的每一个 layer 都会被解压成文件系统快照,类似于 Docker 中的 graphdriver。Metrics
暴露各个组件的监控指标。
从总体来看,Containerd 被分为三个大块:Storage
、Metadata
和 Runtime
,可以将上面的架构图提炼一下:
这是使用 bucketbench 对 Docker、crio 和 Containerd 的性能测试结果,包括启动、停止和删除容器,以比较它们所耗的时间:
3、Containerd 安装
3.1 环境
操作系统:Ubuntu 20.04
3.2 安装
先决条件
$ cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf overlay br_netfilter EOF $ sudo modprobe overlay $ sudo modprobe br_netfilter "设置必需的sysctl参数,这些参数在重新启动后仍然存在" $ cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf net.bridge.bridge-nf-call-iptables = 1 net.ipv4.ip_forward = 1 net.bridge.bridge-nf-call-ip6tables = 1 EOF "应用sysctl参数而无需重新启动" $ sudo sysctl --system
卸载原有 docker or containerd
$ sudo apt-get remove docker docker-engine docker.io containerd runc
更新 apt 程序包并安装程序包,以允许 apt 通过 https 使用存储库
$ sudo apt-get update && sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common
添加 docker 的官方 GPG 密钥
$ sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key --keyring /etc/apt/trusted.gpg.d/docker.gpg add -
设置存储库
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable"
下载并安装
$ wget https://github.com/containerd/containerd/releases/download/v1.6.1/cri-containerd-cni-1.6.1-linux-amd64.tar.gz $ sudo tar --no-overwrite-dir -C / -xzf cri-containerd-cni-1.6.1-linux-amd64.tar.gz $ sudo systemctl daemon-reload $ sudo systemctl enable --now containerd $ sudo mkdir -p /etc/containerd $ sudo su # containerd config default > /etc/containerd/config.toml # exit "配置systemd cgroup为驱动程序:编辑/etc/containerd/config.toml设置SystemdCgroup = true" $ sudo vim /etc/containerd/config.toml "启动containerd" $ sudo systemctl start containerd $ sudo systemctl enable containerd $ sudo systemctl status containerd "验证containerd" $ sudo ctr version Client: Version: v1.6.1 Revision: 10f428dac7cec44c864e1b830a4623af27a9fc70 Go version: go1.17.2 Server: Version: v1.6.1 Revision: 10f428dac7cec44c864e1b830a4623af27a9fc70 UUID: 5dc8deba-a794-4b39-bacb-aa9c790f7465
4、Containerd 使用
4.1 命名空间
Containerd 也支持命名空间,如果不指定,ctr 默认是 default 空间。
# 列出命名空间 $ sudo ctr ns list # 创建命名空间 $ sudo ctr ns create myns # 删除命名空间 $ sudo ctr ns remove myns # 为命名空间设置标签 $ sudo ctr ns label myns env=prod
4.2 镜像操作
# 拉取镜像 $ sudo ctr images pull docker.io/library/nginx:alpine # 列出镜像 $ sudo ctr images list # 挂载镜像 $ sudo ctr images mount docker.io/library/nginx:alpine /mnt/nginx # 卸载镜像 $ sudo ctr images unmount /mnt/nginx # 导入镜像 $ sudo ctr -n=k8s.io images import kube-apiserver.tar
4.3 容器操作
# 创建容器 $ sudo ctr containers create docker.io/library/nginx:alpine nginx # 查看容器 $ sudo ctr containers list # 查看容器详细配置 $ sudo ctr containers info nginx # 删除容器 $ sudo ctr containers delete nginx
4.4 任务
上面 create 的命令创建了容器后,并没有处于运行状态,只是一个静态的容器。一个 container 对象只是包含了运行一个容器所需的资源及配置的数据结构,这意味着 namespaces、rootfs 和容器的配置都已经初始化成功了,只是用户进程还没有启动。
然而一个容器真正的运行起来是由 Task 对象实现的,task 代表任务的意思,可以为容器设置网卡,还可以配置工具来对容器进行监控等。
所以还需要通过 Task 启动容器:
# 启动容器 $ sudo ctr task start -d nginx # 查看容器 $ sudo ctr task list # 进入容器:--exec-id,这个id可以随便写,只要唯一就行 $ sudo ctr task exec --exec-id 0 -t nginx sh # 暂停容器 $ sudo ctr task pause nginx # 恢复容器 $ sudo ctr task resume nginx # 杀掉容器 $ sudo ctr task kill nginx # 获取容器cgroup信息 $ sudo ctr task metrics nginx
当然,也可以一步到位直接创建并运行容器:
$ sudo ctr run -d docker.io/library/nginx:alpine nginx
4.5 nerdctl
nerdctl 是 containerd 的 cli 客户端,与 docker cli 大部分兼容。
nerdctl 是 containerd 的非核心子项目。
nerdctl 优点:
与 docker 相同的体验
支持 docker compose (nerdctl compose up)
支持 rootless mode
支持 lazy-puling
支持 image 加密
4.5.1 安装
下载地址:https://github.com/containerd/nerdctl/releases
精简 ( nerdctl-0.19.0-linux-amd64.tar.gz):仅 nerdctl
完整 ( nerdctl-full-0.19.0-linux-amd64.tar.gz):包含 containerd、runc、CNI 等依赖
下载完整压缩包解压,配置环境变量即可。
4.5.2 使用
nerdctl 使用与 docker 使用类似,请参考 docker 客户端使用。