k8s为什么选择containerd,而不在使用docker
早在2020年k8s官方就已经发布了弃用docker的消息,且在k8s的1.24版本以后便不再维护dockershim,直接使用containerd作为容器运行时;
一、什么是容器运行时?
为了深刻了解原因,首先需要知道容器运行时的概念:
容器运行时(Container Runtime)是一种负责在操作系统层面创建和管理容器的软件工具或组件,主要任务包括创建和启动容器、管理容器文件系统、管理和限制资源、协助配置容器网络、保证容器与宿主机间相互隔离;
docker和containerd都是比较常用的容器运行时,而containerd究竟是什么,他与docker之间的关系又是什么?
二、什么是containerd?
在2016年12月14日,Docker公司宣布将containerd从docker 中分离,并由开源社区独立发展、运营;containerd是docker的基础组件之一,相较于docker来说更为纯粹,它更专注于镜像管理和容器执行,具有更小的资源占用,更快的启动时间,以及更好的性能表现;
主要任务包括:
• 管理容器的生命周期(从创建容器到销毁容器)
• 拉取/推送容器镜像
• 存储管理(管理镜像及容器数据的存储)
• 调用 runC 运行容器(与 runC 等容器运行时交互)
• 管理容器网络接口及网络ctr:containerd 的命令行客户端。
既然docker和containerd都曾作为k8s的容器运行时,那我们就详细了解一下k8s运行的原理;
三、k8s是如何管理容器?
k8s为什么选择containerd,而不在使用docker
早在2020年k8s官方就已经发布了弃用docker的消息,且在k8s的1.24版本以后便不再维护dockershim,直接使用containerd作为容器运行时;
一、什么是容器运行时?
为了深刻了解原因,首先需要知道容器运行时的概念:
容器运行时(Container Runtime)是一种负责在操作系统层面创建和管理容器的软件工具或组件,主要任务包括创建和启动容器、管理容器文件系统、管理和限制资源、协助配置容器网络、保证容器与宿主机间相互隔离;
docker和containerd都是比较常用的容器运行时,而containerd究竟是什么,他与docker之间的关系又是什么?
二、什么是containerd?
在2016年12月14日,Docker公司宣布将containerd从docker 中分离,并由开源社区独立发展、运营;containerd是docker的基础组件之一,相较于docker来说更为纯粹,它更专注于镜像管理和容器执行,具有更小的资源占用,更快的启动时间,以及更好的性能表现;
主要任务包括:
• 管理容器的生命周期(从创建容器到销毁容器)
• 拉取/推送容器镜像
• 存储管理(管理镜像及容器数据的存储)
• 调用 runC 运行容器(与 runC 等容器运行时交互)
• 管理容器网络接口及网络ctr:containerd 的命令行客户端。
既然docker和containerd都曾作为k8s的容器运行时,那我们就详细了解一下k8s运行的原理;
三、k8s是如何管理容器?
k8s会调用容器运行时接口(CRI)去操作容器运行时,容器运行时遵循 OCI 规范,并通过 runc 来实现与操作系统内核的交互,以此完成容器的创建、运行、销毁等工作;
CRI:k8s提供的插件接口,是k8s和容器运行时之间通讯的主要协议;其运行时会从k8s获取gRPC请求,再根据OCI规范来创建json配置; OCI:全称为Open Container Initiative,是一个轻量级,开放的治理结构,在 Linux 基金会的支持下成立,致力于围绕容器格式和运行时创建开放的行业标准。 k8s提出CRI时候,docker刚拆出containerd,此时CPI与docker的调用完全不兼容;为了兼容当时已经很成熟的docker,k8s提出了一个折中的方案,称为docker shim;
四、什么是docker shim?
docker shim是k8s与docker两者间的适配器,也成为“垫片”; k8s会通过CRI接口调用docker shim,docker shim作为Server接受来自于CRI的请求,docker shim接受到请求后,再发给docker的dockerd去调用containerd ; 此时在容器退出状态被 docker收集之前,shim 会一直存在,也会一直占用资源;
五、为什么弃用docker而选择contained;
所以很明显,使用docker作为k8s的容器运行时的话,k8s的调用链为: k8s-->CRI-->docker shim-->dockerd-->containerd 而如果直接使用containerd作为容器运行时的话,那调用链可以简化为: k8s-->CRI-->containerd 可以看到,两个调用链的最终效果是完全一致的,但是第二条调用链因为去除了docker shim和dockerd,不仅性能提高,而且因为链路变短,资源占用也会变小,同时也会更为稳定; 从2018年k8s 1.10与containerd1.1集成的相关测试数据中可以看出,对比于docker,containerd1.1作为容器启动时,延迟降低了20%左右,CPU 使用率降低了 68%,内存使用率降低了 12%,这是一个很可观的数据; 再者,k8s使用的只有docker的部分功能,多余的功能比如说网络或者存储卷这些很有可能会带来更多的安全隐患,所以弃用contained而选择docker是一个很明智的选择;
Containerd 命令行工具的使用
六、工具介绍
Containerd 不支持 docker API 和 docker CLI,但是 containerd 可以通过以下这几种命令实现类似的功能。
ctr
这个是 containerd 官方的命令行工具,功能相对简单,但是拉取镜像和创建容器等基础功能都有。
需要注意的是: ctr 支持选择 namespace,这个 namespace 不是 kubernetes 中的 namespace,而是 containerd 中的 namespace,不过这两种 namespace 在概念上是差不多的。一个 namespace 中镜像、容器等资源,在另一个 namespace 中是看不到的。
默认情况下操作的 都是 default namespace 中的容器和镜像资源,kubernetes 集群中的容器、镜像等资源都放置在 k8s.io 这个 namespace 中。
我们可以使用 -n namespace
来指定操作的是哪个 namespace,也可以使用 ctr namespace ls
查看有哪些 namespace。比如,加上 -n k8s.io
选项之后就可以查看 kubernetes 中的容器和镜像资源了。
crictl
crictl 是 kubernetes cri-tools 的一部分,是专门为 kubernetes 使用 containerd 而专门制作的,提供了 Pod、容器和镜像等资源的管理命令。
需要注意的是:使用其他非 kubernetes 创建的容器、镜像,crictl 是无法看到和调试的,比如说 ctr run 在未指定 namespace 情况下运行起来的容器就无法使用 crictl 看到。 当然 ctr 可以使用 -n k8s.io 指定操作的 namespace 为 k8s.io,从而可以看到/操作 kubernetes 集群中容器、镜像等资源。可以理解为:crictl 操作的时候指定了 containerd 的 namespace 为 k8s.io。
nerdctl
ctr 功能简单,而且对已经习惯使用 docker cli 的人来说,ctr 并不友好(比如无法像 docker cli 那样)。这个时候,nerdctl 就可以替代 ctr 了。nerdctl 是一个与 docker cli 风格兼容的 containerd 的 cli 工具,并且已经被作为子项目加入了 containerd 项目中。从 nerdctl 0.8 开始,nerdctl 直接兼容了 docker compose 的语法(不包含 swarm), 这很大程度上提高了直接将 containerd 作为本地开发、测试和单机容器部署使用的体验。
需要注意的是:安装 nerdctl 之后,要想可以使用 nerdctl 还需要安装 CNI 相关工具和插件。 containerd 不包含网络功能的实现,想要实现端口映射这样的容器网络能力,需要额外安装 CNI 相关工具和插件。
另外 nerdctl 也可以使用 -n
指定使用的 namespace。
使用对比图
ctr 或者 nerdctl 中假如没有指定 -n k8s.io
则表示查看非 kubernetes 中的容器、镜像资源;
ctr 或者 nerdctl 中指定了 -n k8s.io
则表示查看 kubernetes 中的容器、镜像资源。
命令 | Docker | Containerd | ||
---|---|---|---|---|
说明 | docker | crictl | ctr | nerdctl |
查看容器列表 | docker ps | crictl ps | ctr c ls(查看非 kubernetes 中的容器)ctr -n k8s.io c ls(查看 kubernetes 集群中的容器) | nerdctl ps(查看非 kubernetes 中的容器)nerdctl -n k8s.io ps(查看 kubernetes 集群中的容器) |
查看容器详情 | docker inspect | crictl inspect | ctr c info | nerdctl inspect |
查看容器日志 | docker logs | crictl logs | 无 | nerdctl logs |
容器内执行命令 | docker exec | crictl exec | ctr t exec | nerdctl exec |
挂载容器 | docker attach | crictl attach | ctr t attach | 无 |
显示容器资源使用情况 | docker stats | crictl stats | 无 | 无 |
创建容器 | docker create | crictl create | ctr c create | 无 |
启动容器 | docker start | crictl start | ctr t start(请看使用例子) | nerdctl start |
运行容器 | docker run | crictl run | ctr run | nerdctl run |
停止容器 | docker stop | crictl stop | ctr t kill (请看使用例子) | nerdctl stop |
删除容器 | docker rm | crictl rm | ctr c rm | nerdctl rm |
查看镜像列表 | docker images | crictl images | ctr i ls | nerdctl images |
查看镜像详情 | docker inspect | crictl inspecti | 无 | nerdctl inspect |
拉取镜像 | docker pull | crictl pull | ctr i pull(请看使用例子) | nerdctl pull |
推送镜像 | docker push | 无 | ctr i push(请看使用例子) | nerdctl push |
删除镜像 | docker rmi | crictl rmi | ctr i rm | nerdctl rmi |
查看Pod列表 | 无 | crictl pods | 无 | 无 |
查看Pod详情 | 无 | crictl inspectp | 无 | 无 |
启动Pod | 无 | crictl runp | 无 | 无 |
停止Pod | 无 | crictl stopp | 无 | 无 |