从这篇文章 可以了解云原生领域CRI与OCI的由来,为了制约docker在容器运行时的垄断地位,Google与IBM、Redhat等公司成立了CNCF,形成以kubernetes服务编排为基础,建设其周边云原生产品的生态,并陆续提出了CRI(容器运行时接口)、CSI(容器存储接口)、CNI(容器网络接口)的规范。
上篇文章开篇的图上可知,当前docker并没有实现CRI的规范,需要通过kubelet中集成了dokcershim(垫片)来进行“翻译”,并且对比后面直接使用containerd或者cri-o的方案来说,docker的这种实现太冗余了,性能会有较大的开销。但对于当前来说,docker依旧是主要的企业级容器运行时。
kubernetes在1.20版本之后废弃docker作为容器运行时,这也符合其产品理念,后续去除掉dockershim支持,还能实现kubernetes与docker的解耦,减少kubernetes社区的负担,也能够使得kubernetes的运行时支持上可以有更好的演进和发展。
类似的事情就如之前kubernetes支持的持久化存储,厂商会将存储插件代码到kubernetes核心代码中,后续改为统一的基于CSI的实现,而kubernetes就专注于CSI就行了,不用再跟很多厂商的代码耦合了。
containerd
Containerd是一个工业标准的容器运行时,重点是它简洁,健壮,便携,在Linux和window上可以作为一个守护进程运行,它可以管理主机系统上容器的完整的生命周期:镜像传输和存储,容器的执行和监控,低级别的存储和网络。
摘自《containerd官方文档》
Containerd 的架构
可以看到 Containerd 仍然采用标准的 C/S 架构,服务端通过 GRPC
协议提供稳定的 API,客户端通过调用服务端的 API 进行高级的操作。
为了解耦,Containerd 将不同的职责划分给不同的组件,每个组件就相当于一个子系统 (subsystem)。连接不同子系统的组件被称为模块。
下图是官方文档中的架构图:
具体的实现细节还需要经过一段时间的研究才能了解。
CRI性能对比图 在安装使用前,看一张别人使用bucketbench对 Docker
、crio
和 Containerd
的性能测试结果,包括启动、停止和删除容器,以比较它们所耗的时间:
可以看到,containerd在其中属于佼佼者。
containerd安装 环境描述
主机名
ip地址
系统环境
系统版本
内核版本
slions_pc2
192.168.100.11
cpu:x2 mem:4GB filesystem:20GB
CentOS Linux release 7.6.1810
3.10.0-957.el7.x86_64
下载与解压资源包 主要有两种方式安装,可以使用yum安装,也可以下载离线包进行安装,另外如果是之前安装过docker的话,containerd也会自动安装的。
首先从官网找到对应的压缩包 ,我这里是使用的1.5.8版本来进行后续实验。
Containerd 提供了两个压缩包,一个叫 containerd-${VERSION}.${OS}-${ARCH}.tar.gz
,另一个叫 cri-containerd-${VERSION}.${OS}-${ARCH}.tar.gz
。其中 cri-containerd-${VERSION}.${OS}-${ARCH}.tar.gz
包含了所有 Kubernetes 需要的二进制文件。Containerd 是需要调用 runc
的,而第一个压缩包是不包含 runc
二进制文件的,如果选择第一个压缩包,还需要提前安装 runc。这里我本地环境是完全干净的,就直接使用 cri-containerd
压缩包。
下载到服务器解压
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 [root@slions_pc2 ~]# ls anaconda-ks.cfg cri-containerd-cni-1.5.8-linux-amd64.tar.gz [root@slions_pc2 ~]# tar -C / -zxf cri-containerd-cni-1.5.8-linux-amd64.tar.gz # 会生成如下文件 etc/ etc/systemd/ etc/systemd/system/ etc/systemd/system/containerd.service etc/crictl.yaml etc/cni/ etc/cni/net.d/ etc/cni/net.d/10-containerd-net.conflist usr/ usr/local/ usr/local/sbin/ usr/local/sbin/runc usr/local/bin/ usr/local/bin/containerd usr/local/bin/crictl usr/local/bin/ctd-decoder usr/local/bin/critest usr/local/bin/containerd-shim-runc-v1 usr/local/bin/containerd-shim-runc-v2 usr/local/bin/containerd-stress usr/local/bin/ctr usr/local/bin/containerd-shim opt/ opt/containerd/ opt/containerd/cluster/ opt/containerd/cluster/version opt/containerd/cluster/gce/ opt/containerd/cluster/gce/configure.sh opt/containerd/cluster/gce/cni.template opt/containerd/cluster/gce/cloud-init/ opt/containerd/cluster/gce/cloud-init/node.yaml opt/containerd/cluster/gce/cloud-init/master.yaml opt/containerd/cluster/gce/env opt/cni/ opt/cni/bin/ opt/cni/bin/dhcp opt/cni/bin/firewall opt/cni/bin/host-local opt/cni/bin/ipvlan opt/cni/bin/sbr opt/cni/bin/vlan opt/cni/bin/vrf opt/cni/bin/tuning opt/cni/bin/bridge opt/cni/bin/macvlan opt/cni/bin/bandwidth opt/cni/bin/portmap opt/cni/bin/host-device opt/cni/bin/ptp opt/cni/bin/flannel opt/cni/bin/static opt/cni/bin/loopback
生成配置文件 Containerd 的默认配置文件为 /etc/containerd/config.toml
,需要通过命令来生成一个默认的配置:
1 2 [root@slions_pc2 ~]# mkdir /etc/containerd [root@slions_pc2 ~]# containerd config default > /etc/containerd/config.toml
配置参数含义参照https://github.com/containerd/containerd/blob/master/docs/man/containerd-config.toml.5.md
存储配置 Containerd 有两个不同的存储路径,一个用来保存持久化数据,一个用来保存运行时状态。
1 2 root = "/var/lib/containerd" state = "/run/containerd"
root
用来保存持久化数据,包括 Snapshots
, Content
, Metadata
以及各种插件的数据。每一个插件都有自己单独的目录,Containerd 本身不存储任何数据,它的所有功能都来自于已加载的插件。
私有仓库 私有仓库字段位置:
1 2 3 4 5 6 7 8 9 [plugins."io.containerd.grpc.v1.cri".registry.mirrors] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."192.168.100.10"] endpoint = ["https://192.168.100.10" ] [plugins."io.containerd.grpc.v1.cri".registry.configs] [plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.100.10".tls] insecure_skip_verify = true [plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.100.10".auth] username = "admin" password = "Harbor12345"
这里我就不修改了,默认是docker.io
OOM 有一项配置需要留意:
Containerd 是容器的守护者,一旦发生内存不足的情况,理想的情况应该是先杀死容器,而不是杀死 Containerd。所以需要调整 Containerd 的 OOM
权重,减少其被 OOM Kill 的几率。最好是将 oom_score
的值调整为比其他守护进程略低的值。这里的 oom_socre 其实对应的是 /proc/<pid>/oom_socre_adj
,在早期的 Linux 内核版本里使用 oom_adj
来调整权重, 后来改用 oom_socre_adj
了。
建议 Containerd 将该值设置为 -999
到 0
之间。如果作为 Kubernetes 的 Worker 节点,可以考虑设置为 -999
。
Systemd 配置 之前解压资源包时已经将containerd的systemd service配置文件放置在了etc/systemd/system/containerd.service
,可以通过 systemd 配置 Containerd 作为守护进程运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 [root@slions_pc2 ~]# systemctl enable containerd && systemctl start containerd Created symlink from /etc/systemd/system/multi-user.target.wants/containerd.service to /etc/systemd/system/containerd.service. [root@slions_pc2 ~]# systemctl status containerd ● containerd.service - containerd container runtime Loaded: loaded (/etc/systemd/system/containerd.service; enabled; vendor preset: disabled) Active: active (running) since 三 2021-12-15 19:32:15 CST; 11s ago Docs: https://containerd.io Process: 10475 ExecStartPre=/sbin/modprobe overlay (code=exited, status=0/SUCCESS) Main PID: 10483 (containerd) Tasks: 9 Memory: 19.2M CGroup: /system.slice/containerd.service └─10483 /usr/local/bin/containerd 12月 15 19:32:15 slions_pc2 containerd[10483]: time="2021-12-15T19:32:15.628370353+08:00" level=info msg="loading plugin \"io.containerd.grpc.v1.introspection\"..." type=io.containerd.grpc.v1 12月 15 19:32:15 slions_pc2 containerd[10483]: time="2021-12-15T19:32:15.629850993+08:00" level=info msg="Start subscribing containerd event" 12月 15 19:32:15 slions_pc2 containerd[10483]: time="2021-12-15T19:32:15.629895514+08:00" level=info msg="Start recovering state" 12月 15 19:32:15 slions_pc2 containerd[10483]: time="2021-12-15T19:32:15.630058843+08:00" level=info msg="Start event monitor" 12月 15 19:32:15 slions_pc2 containerd[10483]: time="2021-12-15T19:32:15.630072189+08:00" level=info msg="Start snapshots syncer" 12月 15 19:32:15 slions_pc2 containerd[10483]: time="2021-12-15T19:32:15.630079486+08:00" level=info msg="Start cni network conf syncer" 12月 15 19:32:15 slions_pc2 containerd[10483]: time="2021-12-15T19:32:15.630084202+08:00" level=info msg="Start streaming server" 12月 15 19:32:15 slions_pc2 containerd[10483]: time="2021-12-15T19:32:15.632856124+08:00" level=info msg=serving... address=/run/containerd/containerd.sock.ttrpc 12月 15 19:32:15 slions_pc2 containerd[10483]: time="2021-12-15T19:32:15.633175755+08:00" level=info msg=serving... address=/run/containerd/containerd.sock 12月 15 19:32:15 slions_pc2 containerd[10483]: time="2021-12-15T19:32:15.633729234+08:00" level=info msg="containerd successfully booted in 0.055757s"
ctr 使用 ctr命令是containerd的客户端工具,下面是一些常见的用法:
命名空间 containerd有namespaces的概念,不同命名空间下的container是互相隔离的,如果不指定,ctr
默认是 default
空间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [root@slions_pc2 ~]# ctr ns -h NAME: ctr namespaces - manage namespaces USAGE: ctr namespaces command [command options] [arguments...] COMMANDS: create, c create a new namespace list, ls list namespaces remove, rm remove one or more namespaces label set and clear labels for a namespace OPTIONS: --help, -h show help [root@slions_pc2 ~]# ctr ns create mytest1 [root@slions_pc2 ~]# ctr ns create mytest2 [root@slions_pc2 ~]# ctr ns ls NAME LABELS mytest1 mytest2
镜像 镜像下载 测试时发现需要写全镜像的名称,无法想docker一样省略默认的仓库名与项目名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [root@slions_pc2 ~]# ctr -n mytest1 images pull docker.io/library/nginx:latest ctr: failed to resolve reference "nginx": object required docker.io/library/nginx:latest: resolved |++++++++++++++++++++++++++++++++++++++| index-sha256:9522864dd661dcadfd9958f9e0de192a1fdda2c162a35668ab6ac42b465f0603: done |++++++++++++++++++++++++++++++++++++++| manifest-sha256:4424e31f2c366108433ecca7890ad527b243361577180dfd9a5bb36e828abf47: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:44be98c0fab60b6cef9887dbad59e69139cab789304964a197c27e2574d37304: done |++++++++++++++++++++++++++++++++++++++| config-sha256:f652ca386ed135a4cbe356333e08ef0816f81b2ac8d0619af01e2b256837ed3e: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:e5ae68f740265288a4888db98d2999a638fdcb6d725f427678814538d253aa4d: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:21e0df283cd68384e5e8dff7e6be1774c86ea3110c1b1e9326014d34c29c0657: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:ed835de16acd8f5821cf3f3ef77a66922510ee6349730d89a8dd4fd6da8437ef: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:881ff011f1c9c14982afc6e95ae70c25e38809843bb7d42abbb24eb540c93ce7: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:77700c52c9695053293be96f9cbcf42c91c5e097daa3829339333f1fe6f9d353: done |++++++++++++++++++++++++++++++++++++++| elapsed: 99.8s total: 53.2 M (545.7 KiB/s) unpacking linux/amd64 sha256:9522864dd661dcadfd9958f9e0de192a1fdda2c162a35668ab6ac42b465f0603... done: 7.663355797s
查看该镜像。
1 2 3 [root@slions_pc2 ~]# ctr -n mytest1 image ls REF TYPE DIGEST SIZE PLATFORMS LABELS docker.io/library/nginx:latest application/vnd.docker.distribution.manifest.list.v2+json sha256:9522864dd661dcadfd9958f9e0de192a1fdda2c162a35668ab6ac42b465f0603 54.1 MiB linux/386,linux/amd64,linux/arm/v5,linux/arm/v7,linux/arm64/v8,linux/mips64le,linux/ppc64le,linux/s390x -
此时mytest2中是没有这个镜像的。
1 2 [root@slions_pc2 ~]# ctr -n mytest2 image ls REF TYPE DIGEST SIZE PLATFORMS LABELS
容器 创建容器 1 2 3 4 5 6 [root@slions_pc2 ~]# ctr -n mytest1 container create docker.io/library/nginx:latest mynginx [root@slions_pc2 ~]# ctr -n mytest1 c ls CONTAINER IMAGE RUNTIME mynginx docker.io/library/nginx:latest io.containerd.runc.v2 [root@slions_pc2 ~]# ps -elf|grep nginx 0 S root 10680 9878 0 80 0 - 28181 pipe_w 20:10 pts/0 00:00:00 grep --color=auto nginx
任务 上面 create
的命令创建了容器后,并没有处于运行状态,只是一个静态的容器。一个 container 对象只是包含了运行一个容器所需的资源及配置的数据结构,这意味着 namespaces、rootfs 和容器的配置都已经初始化成功了,只是用户进程(这里是 nginx
)还没有启动。
然而一个容器真正的运行起来是由 task 对象实现的,task
代表任务的意思,可以为容器设置网卡,还可以配置工具来对容器进行监控等。
1 2 3 4 5 6 7 8 9 10 [root@slions_pc2 ~]# ctr -n mytest1 task start -d mynginx [root@slions_pc2 ~]# ctr -n mytest1 task ls TASK PID STATUS mynginx 11330 RUNNING [root@slions_pc2 ~]# ps -elf|grep nginx 0 S root 11305 1 0 80 0 - 177741 futex_ 21:03 ? 00:00:00 /usr/local/bin/containerd-shim-runc-v2 -namespace mytest1 -id mynginx -address /run/containerd/containerd.sock 4 S root 11330 11305 0 80 0 - 2169 sigsus 21:03 ? 00:00:00 nginx: master process nginx -g daemon off; 5 S 101 11367 11330 0 80 0 - 2270 ep_pol 21:03 ? 00:00:00 nginx: worker process 5 S 101 11368 11330 0 80 0 - 2270 ep_pol 21:03 ? 00:00:00 nginx: worker process 0 S root 11376 9878 0 80 0 - 28182 pipe_w 21:04 pts/0 00:00:00 grep --color=auto nginx
当然,也可以一步到位直接创建并运行容器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 [root@slions_pc2 ~]# ctr -n mytest2 images pull docker.io/library/nginx:latest docker.io/library/nginx:latest: resolved |++++++++++++++++++++++++++++++++++++++| index-sha256:9522864dd661dcadfd9958f9e0de192a1fdda2c162a35668ab6ac42b465f0603: done |++++++++++++++++++++++++++++++++++++++| manifest-sha256:4424e31f2c366108433ecca7890ad527b243361577180dfd9a5bb36e828abf47: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:44be98c0fab60b6cef9887dbad59e69139cab789304964a197c27e2574d37304: done |++++++++++++++++++++++++++++++++++++++| config-sha256:f652ca386ed135a4cbe356333e08ef0816f81b2ac8d0619af01e2b256837ed3e: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:e5ae68f740265288a4888db98d2999a638fdcb6d725f427678814538d253aa4d: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:21e0df283cd68384e5e8dff7e6be1774c86ea3110c1b1e9326014d34c29c0657: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:ed835de16acd8f5821cf3f3ef77a66922510ee6349730d89a8dd4fd6da8437ef: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:881ff011f1c9c14982afc6e95ae70c25e38809843bb7d42abbb24eb540c93ce7: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:77700c52c9695053293be96f9cbcf42c91c5e097daa3829339333f1fe6f9d353: done |++++++++++++++++++++++++++++++++++++++| elapsed: 5.8 s total: 0.0 B (0.0 B/s) unpacking linux/amd64 sha256:9522864dd661dcadfd9958f9e0de192a1fdda2c162a35668ab6ac42b465f0603... done: 2.327616113s [root@slions_pc2 ~]# ctr -n mytest2 run -d docker.io/library/nginx:latest mynginx [root@slions_pc2 ~]# ctr -n mytest2 t ls TASK PID STATUS mynginx 11472 RUNNING [root@slions_pc2 ~]# ps -elf|grep nginx 0 S root 11305 1 0 80 0 - 177901 futex_ 21:03 ? 00:00:00 /usr/local/bin/containerd-shim-runc-v2 -namespace mytest1 -id mynginx -address /run/containerd/containerd.sock 4 S root 11330 11305 0 80 0 - 2169 sigsus 21:03 ? 00:00:00 nginx: master process nginx -g daemon off; 5 S 101 11367 11330 0 80 0 - 2270 ep_pol 21:03 ? 00:00:00 nginx: worker process 5 S 101 11368 11330 0 80 0 - 2270 ep_pol 21:03 ? 00:00:00 nginx: worker process 0 S root 11450 1 0 80 0 - 178173 futex_ 21:09 ? 00:00:00 /usr/local/bin/containerd-shim-runc-v2 -namespace mytest2 -id mynginx -address /run/containerd/containerd.sock 4 S root 11472 11450 0 80 0 - 2169 sigsus 21:09 ? 00:00:00 nginx: master process nginx -g daemon off; 5 S 101 11514 11472 0 80 0 - 2270 ep_pol 21:09 ? 00:00:00 nginx: worker process 5 S 101 11515 11472 0 80 0 - 2270 ep_pol 21:09 ? 00:00:00 nginx: worker process 0 S root 11533 9878 0 80 0 - 28182 pipe_w 21:10 pts/0 00:00:00 grep --color=auto nginx
进入容器 和 docker 的操作类似,但必须要指定 –exec-id ,这个 id 可以随便写,只要唯一就行
1 [root@slions_pc2 ~]# ctr -n mytest1 task exec --exec-id 0 -t mynginx bash
杀死容器 ctr 没有 stop 容器的功能,只能暂停或者杀死容器。
1 [root@slions_pc2 ~]# ctr -n mytest1 task kill mynginx
nerdctl使用 可以看到原生的ctr工具对用户不是很友好,如果是用惯docker命令再用这个体会的更加明显,好在containerd也发现了这个问题,后面出来了nerdctl 工具。
1 2 3 4 5 6 7 8 9 10 11 [root@slions_pc2 ~]# mkdir nerdctl && tar xvf nerdctl-0.15.0-linux-amd64.tar.gz -C nerdctl nerdctl containerd-rootless-setuptool.sh containerd-rootless.sh [root@slions_pc2 ~]# cp -rp nerdctl/* /usr/local/bin/ [root@slions_pc2 ~]# nerdctl ps --namespace mytest1 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES mynginx docker.io/library/nginx:latest "/docker-entrypoint.…" About an hour ago Up [root@slions_pc2 ~]# nerdctl images --namespace mytest1 REPOSITORY TAG IMAGE ID CREATED PLATFORM SIZE nginx latest 9522864dd661 2 hours ago linux/amd64 146.2 MiB