[TOC]
0x00 前言简述 描述: 目前Docker是Kubernetes默认的容器运行时(Container Runtime), 由于k8s在2020年宣布1.20版本之后将弃用 dockershim (其中也有kubernetes与Docker爱恨情仇)时,才把containerd拉回大众的视野之中,所以本章主要讲解containerd基础入门。
1.基础介绍 Q: 什么是Containerd?
答: Containerd是从Docker中分类出的容器运行时与runc一样被分解为Docke的高级运行时部分,它支持 OCI 的镜像标准、可以实现拉取和推送镜像、管理操作镜像负责容器的整个生命周期。 例如当它需要运行一个容器时,它会将映像解压到一个OCI运行时包中,并将其发送给runc来运行它,Containerd还提供了一个API和客户端应用程序可以用来与之交互,containerd命令行客户端是ctr命令。 官方介绍: An industry-standard container runtime with an emphasis on simplicity, robustness and portability - 业界标准的容器运行时,强调简单性、健壮性和可移植性。
Q: Containerd 有何特点?
OCI Image Spec support - OCI图像规范支持
OCI Runtime Spec support - OCI运行时规范支持
Image push and pull support - 图像推拉支持
Container runtime and lifecycle support - 容器运行时和生命周期支持
Network primitives for creation, modification, and deletion of interfaces - 用于创建、修改和删除接口的网络原语
Management of network namespaces containers to join existing namespaces - 管理连接现有名称空间的网络名称空间容器
Multi-tenant supported with CAS storage for global images - CAS存储支持用于全局映像的多租户
发展历史 : ( Containerd 和 Docker 的前世今生以及爱恨情仇) 在几年之前 Docker 公司在容器技术领域强势崛起一家独大,Google、RedHat 这样的巨头们都产生了很大的危机感,因此他们想与 Docker 公司一起联合研发推进一个开源的容器运行时作为 Docker 技术的核心依赖。然鹅 Docker 公司很高冷的表示:我不干!巨头们听到这个反馈就很不爽啊,因此通过一些手段对 Docker 公司软硬兼施,使其将 libcontainer 捐给了开源社区,也就是现在的 runc,一个 low level 的容器运行时。此外巨头们又成立了 CNCF 去对抗 Docker 公司的一家独大,CNCF 成立的思路很明确:在容器领域干不过 Docker,那就搞容器上层的建设——容器编排,从此 K8s 诞生了。虽然 Docker 公司也尝试使用 Swarm 去对抗 K8s但最终也失败了。
自此K8s慢慢成为云原生领域的标配,其生态也越做越大、越做越完善。Docker 公司为了融入生态,开源了 Docker 的核心依赖 containerd 。2019年2月28日containerd正式成为云原生计算基金会(Cloud Native Computing Foundation -CNCF)的一个毕业项目,紧随Kubernetes、Prometheus、Envoy和CoreDNS
之后,在从containerd1.5开始,Kubernetes容器运行时接口(CRI)的containerd插件已经合并到containerd中。。
此外 K8s 为了对接下一层的容器,也因为其中立性搞了一个运行时接口,也就是 CRI(Container Runtime Interface)
,runc、containerd 等运行时都去支持此接口。由于当时确实没有啥 high level 的 runtime,oci-o 虽然支持 CRI 接口,但其功能太弱;containerd 也尚未成熟,而且其当时的定位是嵌入到系统中,并非给终端用户使用;rkt 有自己的一套体系后来这个项目也失败了。只能暂时为了适配 Docker 搞了个 shim,将 CRI 转换为 Docker API。K8s 和 Docker 进入了冷静期,双方都在各自优化自己,互不干扰。然而平静之下仍是暗潮汹涌,CNCF 社区一直在不断完善 containerd,其定位也发生了改变,由原来的系统嵌入组件,变成了今天的"工业标准容器运行时"
,并提供给终端用户使用。直到2020年12月 K8s 宣布废弃使用 Docker,而改用 Containerd。
总结: 其实kubernetes宣布废弃dockershim,一方面是因为商业因素,而另一方面 K8s 已经提供了标准接口对接底层容器运行时,不再想单独维护一个类似于Dockershim
的适配层去迎合不同的运行时。
Q: 那什么是dockershim? 描述: dockershim 是 Kubernetes 的一个组件,其作用是为了操作Docker。Docker是在2013年面世的而Kubernetes是在2016年,所以Docker刚开始并没有想到编排,也不会知道会出现Kubernetes这个庞然大物(它要是知道,也不会败的那么快)。但是Kubernetes在创建的时候就是以Docker作为容器运行时,很多操作逻辑都是针对的Docker,随着社区越来越健壮,为了兼容更多的容器运行时,才将Docker的相关逻辑独立出来组成了dockershim。
正因为这样,只要Kubernetes的任何变动或者Docker的任何变动,都必须维护dockershim,这样才能保证足够的支持,但是通过dockershim操作Docker,其本质还是操作Docker的底层运行时Containerd,而且Containerd自身也是支持CRI(Container Runtime Interface)交互,所以正如前面所说只是kubernetes不想单独维护一个类似于Dockershim
的适配层去迎合不同的运行时。
相关参考 Containerd 官网: https://containerd.io/ Containerd 帮助文档: https://containerd.io/docs/ CNI 网络项目: https://github.com/containernetworking
Tips: 在github存储库中的 containerd 基本项目级信息:
containerd/containerd :containerd的主要项目repo,包括container运行时以及从containerd 1.5开始,用于Kubernetes容器运行时接口(CRI) 已并入containerd。
containerd/containerd.io : 用于构建containerd网站和文档的资产(即您当前正在阅读的内容)
containerd/project : 跨containerd存储库使用的实用程序,如脚本、公共文件和核心文档
containerd/ttrpc : containerd使用的gRPC版本(为低内存环境设计)
2.专业术语 描述: 在介绍容器运行时相关概念及组件原理,梳理下我们常听到的 OCI、runc、containerd 等名词之间的关系。
OCI Docker 公司与 CoreOS 和 Google 共同创建了 OCI (Open Container Initial)
并提供了两种规范:
Tips: 文件系统束(filesystem bundle)
: 定义了一种将容器编码为文件系统束的格式,即以某种方式组织的一组文件,并包含所有符合要求的运行时对其执行所有标准操作的必要数据和元数据,即 config.json 与 根文件系统。
Tips: CRI(Container Runtime Interface,容器运行时接口)
: 它是为了解决这些容器运行时和Kubernetes的集成问题在Kubernetes 1.5版本中推出。
Docker、Google 等开源了用于运行容器的工具和库 runC 作为 OCI 的一种实现参考, 随后各种运行时和库也慢慢出现例如 rkt、containerd(今天的主角)、cri-o
,然而这些工具所拥有的功能却不尽相同,有的只有运行容器(runc、lxc),而有的除此之外也可以对镜像进行管理(containerd、cri-o), 按照前面容器运行时进行分为两类, 其不同容器运行时工具分类关系图如下。
Runtime
1) 容器运行时(Container Runtime): 运行于Docker或者Kubernetes集群的每个节点中, 负责容器的整个生命周期,包括构建、创建、运行、管理、删除等对容器的操作。其中Docker是目前应用最广的,随着容器云的发展,越来越多的容器运行时涌现。
2) 容器运行时分成了 low-level
和 high-level
两类。
(1) low-level : 指的是仅关注运行容器的容器运行时,调用操作系统,使用 namespace 和 cgroup 实现资源隔离和限制
(2) high-level :指包含了更多上层功能,例如 grpc调用,镜像存储管理等。
weiyigeek.top-不同运行时工具间关系
(1) low-level runtime
: 主要关注如何与操作系统资源交互和创建并运行容器。目前常见的 low-level runtime有:
lmctfy – Google的一个项目它是Borg使用的容器运行时。
runc – 目前使用最广泛的容器运行时。它最初是作为Docker的一部分开发的,后来被提取出来作为一个单独的工具和库, 其实现了 OCI 规范包含config.json文件和容器的根文件系统。
rkt – CoreOS开发的Docker/runc的一个流行替代方案,提供了其他 low-level runtimes (如runc)所提供的所有特性。
(2) high-level runtime
: 主要负责传输和管理容器镜像,解压镜像,并传递给low-level runtimes来运行容器。目前主流的 high-level runtime 有:
docker – 老熟人完整的集装箱(Container)解决方案
containerd – 本章主角
rkt – 与Docker类似的容器引擎更加专注于解决安全、兼容、执行效率等方面的问题。
CRI 描述: CRI是Kubernetes定义的一组gRPC服务。Kubelet作为客户端,基于gRPC框架,通过Socket和容器运行时通信。它包括两类服务:镜像服务(Image Service)和运行时服务(Runtime Service)。镜像服务提供下载、检查和删除镜像的远程程序调用。运行时服务包含用于管理容器生命周期,以及与容器交互的调用 ( exec / attach / port-forward )
的远程程序调用。
CRI接口客户端工具
3.架构简述 CRI 的架构 描述: CRI 插件是 Kubernetes 容器运行时接口 (CRI) 的实现, Containerd 与 Kubelet 在同一节点上运行。
containerd 中的插件处理来自 Kubelet 的所有 CRI 服务请求,并使用容器内部来管理容器和容器映像,该插件使用 containerd 来管理整个容器生命周期和所有容器映像,并通过CNI(另一个CNCF项目)管理pod网络,如下图所示
weiyigeek.top-CRI 插件
让我们用一个例子来演示当 Kubelet 创建一个单容器 pod 时,插件是如何工作的
Kubelet 通过 CRI 运行时服务 API 调用插件来创建 pod;
cri 创建 Pod 的网络命名空间,然后使用 CNI 对其进行配置;
cri 使用 containerd internal 创建和启动一个特殊的暂停容器(沙盒容器),并将该容器放在 pod 的 cgroup 和命名空间中(为简洁起见省略了步骤);
Kubelet随后通过CRI镜像服务API调用插件,拉取应用容器镜像;
cri 进一步使用容器来拉取图像,如果图像不存在于节点上;
然后 Kubelet 通过 CRI 运行时服务 API 调用,使用拉取的容器映像在 pod 内创建和启动应用程序容器;cri
cri 最后使用 containerd internal 创建应用程序容器,将其放在 pod 的 cgroup 和命名空间中,然后启动 pod 的新应用程序容器。完成这些步骤后,将创建并运行 Pod 及其相应的应用程序容器。
Containerd 的架构 描述: containerd是Linux和Windows的守护程序。它管理其主机系统的完整容器生命周期,从图像传输和存储到容器执行和监督,再到低级存储到网络附件等等。
Containerd 的设计是一个大的插件系统,下图中每一个虚线框其实就对应一个插件。
1) 底层系统支持 Linux 、Windows操作系统 和 支持 arm 和 x86 架构
2) 中间 containerd 包含 Backend、core、API 三层
Backend 层: Runtime plugin 提供了容器运行时的具体操作,为了支持不同的容器运行时 containerd 也提供了一系列的 containerd-shim
Core 层: 则是核心部分,提供了各种功能的服务,其中比较常用的是 Content service ,提供对镜像中可寻址内容的访问,所有不可变的内容都被存储在这里;Images Service 提供镜像相关的操作;Snapshot Plugin : 用来管理容器镜像的文件系统快照。镜像中的每一个 layer 都会被解压成文件系统快照,类似于 Docker 中的 graphdriver
API 层: 通过 GRPC 与客户端连接,例如提供了给 Prometheus 使用 API 来进行监控,给kubernetes提供了CRI接口,给containerd提供了服务处理程序。
3) 高层提供各种客户端,包括 K8s 的 kubelet,containerd 自己的命令行 ctr 等。
先来看看 Containerd 的架构:
weiyigeek.top-containerd架构体系
可以看到 Containerd 仍然采用标准的 C/S 架构,服务端通过 GRPC 协议提供稳定的 API,客户端通过调用服务端的 API 进行高级的操作。
为了解耦,Containerd 将不同的职责划分给不同的组件,每个组件就相当于一个子系统(subsystem)。连接不同子系统的组件被称为模块。
总体上 Containerd 被划分为两个子系统:
Bundle : 在 Containerd 中,Bundle 包含了配置、元数据和根文件系统数据,你可以理解为容器的文件系统。而 Bundle 子系统允许用户从镜像中提取和打包 Bundles。
Runtime : Runtime 子系统用来执行 Bundles,比如创建容器。
其中,每一个子系统的行为都由一个或多个模块协作完成(架构图中的 Core 部分)。每一种类型的模块都以插件的形式集成到 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】由 Task 触发运行时
, 并对外提供 GRPC 方式的 API 以及 Metrics 接口。
weiyigeek.top-架构图简化
containerd-shim 描述: 主要是用于剥离 containerd 守护进程与容器进程。目前已有 shim v1 和 shim v2 两个版本;它是containerd中的一个组件,其通过 shim 调用 runc 的包函数来启动容器。直白的说引入shim是允许runc在创建和运行容器之后退出, 并将shim作为容器的父进程, 而不是containerd作为父进程。
[TOC]
0x00 前言简述 描述: 目前Docker是Kubernetes默认的容器运行时(Container Runtime), 由于k8s在2020年宣布1.20版本之后将弃用 dockershim (其中也有kubernetes与Docker爱恨情仇)时,才把containerd拉回大众的视野之中,所以本章主要讲解containerd基础入门。
1.基础介绍 Q: 什么是Containerd?
答: Containerd是从Docker中分类出的容器运行时与runc一样被分解为Docke的高级运行时部分,它支持 OCI 的镜像标准、可以实现拉取和推送镜像、管理操作镜像负责容器的整个生命周期。 例如当它需要运行一个容器时,它会将映像解压到一个OCI运行时包中,并将其发送给runc来运行它,Containerd还提供了一个API和客户端应用程序可以用来与之交互,containerd命令行客户端是ctr命令。 官方介绍: An industry-standard container runtime with an emphasis on simplicity, robustness and portability - 业界标准的容器运行时,强调简单性、健壮性和可移植性。
Q: Containerd 有何特点?
OCI Image Spec support - OCI图像规范支持
OCI Runtime Spec support - OCI运行时规范支持
Image push and pull support - 图像推拉支持
Container runtime and lifecycle support - 容器运行时和生命周期支持
Network primitives for creation, modification, and deletion of interfaces - 用于创建、修改和删除接口的网络原语
Management of network namespaces containers to join existing namespaces - 管理连接现有名称空间的网络名称空间容器
Multi-tenant supported with CAS storage for global images - CAS存储支持用于全局映像的多租户
发展历史 : ( Containerd 和 Docker 的前世今生以及爱恨情仇) 在几年之前 Docker 公司在容器技术领域强势崛起一家独大,Google、RedHat 这样的巨头们都产生了很大的危机感,因此他们想与 Docker 公司一起联合研发推进一个开源的容器运行时作为 Docker 技术的核心依赖。然鹅 Docker 公司很高冷的表示:我不干!巨头们听到这个反馈就很不爽啊,因此通过一些手段对 Docker 公司软硬兼施,使其将 libcontainer 捐给了开源社区,也就是现在的 runc,一个 low level 的容器运行时。此外巨头们又成立了 CNCF 去对抗 Docker 公司的一家独大,CNCF 成立的思路很明确:在容器领域干不过 Docker,那就搞容器上层的建设——容器编排,从此 K8s 诞生了。虽然 Docker 公司也尝试使用 Swarm 去对抗 K8s但最终也失败了。
自此K8s慢慢成为云原生领域的标配,其生态也越做越大、越做越完善。Docker 公司为了融入生态,开源了 Docker 的核心依赖 containerd 。2019年2月28日containerd正式成为云原生计算基金会(Cloud Native Computing Foundation -CNCF)的一个毕业项目,紧随Kubernetes、Prometheus、Envoy和CoreDNS
之后,在从containerd1.5开始,Kubernetes容器运行时接口(CRI)的containerd插件已经合并到containerd中。。
此外 K8s 为了对接下一层的容器,也因为其中立性搞了一个运行时接口,也就是 CRI(Container Runtime Interface)
,runc、containerd 等运行时都去支持此接口。由于当时确实没有啥 high level 的 runtime,oci-o 虽然支持 CRI 接口,但其功能太弱;containerd 也尚未成熟,而且其当时的定位是嵌入到系统中,并非给终端用户使用;rkt 有自己的一套体系后来这个项目也失败了。只能暂时为了适配 Docker 搞了个 shim,将 CRI 转换为 Docker API。K8s 和 Docker 进入了冷静期,双方都在各自优化自己,互不干扰。然而平静之下仍是暗潮汹涌,CNCF 社区一直在不断完善 containerd,其定位也发生了改变,由原来的系统嵌入组件,变成了今天的"工业标准容器运行时"
,并提供给终端用户使用。直到2020年12月 K8s 宣布废弃使用 Docker,而改用 Containerd。
总结: 其实kubernetes宣布废弃dockershim,一方面是因为商业因素,而另一方面 K8s 已经提供了标准接口对接底层容器运行时,不再想单独维护一个类似于Dockershim
的适配层去迎合不同的运行时。
Q: 那什么是dockershim? 描述: dockershim 是 Kubernetes 的一个组件,其作用是为了操作Docker。Docker是在2013年面世的而Kubernetes是在2016年,所以Docker刚开始并没有想到编排,也不会知道会出现Kubernetes这个庞然大物(它要是知道,也不会败的那么快)。但是Kubernetes在创建的时候就是以Docker作为容器运行时,很多操作逻辑都是针对的Docker,随着社区越来越健壮,为了兼容更多的容器运行时,才将Docker的相关逻辑独立出来组成了dockershim。
正因为这样,只要Kubernetes的任何变动或者Docker的任何变动,都必须维护dockershim,这样才能保证足够的支持,但是通过dockershim操作Docker,其本质还是操作Docker的底层运行时Containerd,而且Containerd自身也是支持CRI(Container Runtime Interface)交互,所以正如前面所说只是kubernetes不想单独维护一个类似于Dockershim
的适配层去迎合不同的运行时。
相关参考 Containerd 官网: https://containerd.io/ Containerd 帮助文档: https://containerd.io/docs/ CNI 网络项目: https://github.com/containernetworking
Tips: 在github存储库中的 containerd 基本项目级信息:
containerd/containerd :containerd的主要项目repo,包括container运行时以及从containerd 1.5开始,用于Kubernetes容器运行时接口(CRI) 已并入containerd。
containerd/containerd.io : 用于构建containerd网站和文档的资产(即您当前正在阅读的内容)
containerd/project : 跨containerd存储库使用的实用程序,如脚本、公共文件和核心文档
containerd/ttrpc : containerd使用的gRPC版本(为低内存环境设计)
2.专业术语 描述: 在介绍容器运行时相关概念及组件原理,梳理下我们常听到的 OCI、runc、containerd 等名词之间的关系。
OCI Docker 公司与 CoreOS 和 Google 共同创建了 OCI (Open Container Initial)
并提供了两种规范:
Tips: 文件系统束(filesystem bundle)
: 定义了一种将容器编码为文件系统束的格式,即以某种方式组织的一组文件,并包含所有符合要求的运行时对其执行所有标准操作的必要数据和元数据,即 config.json 与 根文件系统。
Tips: CRI(Container Runtime Interface,容器运行时接口)
: 它是为了解决这些容器运行时和Kubernetes的集成问题在Kubernetes 1.5版本中推出。
Docker、Google 等开源了用于运行容器的工具和库 runC 作为 OCI 的一种实现参考, 随后各种运行时和库也慢慢出现例如 rkt、containerd(今天的主角)、cri-o
,然而这些工具所拥有的功能却不尽相同,有的只有运行容器(runc、lxc),而有的除此之外也可以对镜像进行管理(containerd、cri-o), 按照前面容器运行时进行分为两类, 其不同容器运行时工具分类关系图如下。
Runtime
1) 容器运行时(Container Runtime): 运行于Docker或者Kubernetes集群的每个节点中, 负责容器的整个生命周期,包括构建、创建、运行、管理、删除等对容器的操作。其中Docker是目前应用最广的,随着容器云的发展,越来越多的容器运行时涌现。
2) 容器运行时分成了 low-level
和 high-level
两类。
(1) low-level : 指的是仅关注运行容器的容器运行时,调用操作系统,使用 namespace 和 cgroup 实现资源隔离和限制
(2) high-level :指包含了更多上层功能,例如 grpc调用,镜像存储管理等。
weiyigeek.top-不同运行时工具间关系
(1) low-level runtime
: 主要关注如何与操作系统资源交互和创建并运行容器。目前常见的 low-level runtime有:
lmctfy – Google的一个项目它是Borg使用的容器运行时。
runc – 目前使用最广泛的容器运行时。它最初是作为Docker的一部分开发的,后来被提取出来作为一个单独的工具和库, 其实现了 OCI 规范包含config.json文件和容器的根文件系统。
rkt – CoreOS开发的Docker/runc的一个流行替代方案,提供了其他 low-level runtimes (如runc)所提供的所有特性。
(2) high-level runtime
: 主要负责传输和管理容器镜像,解压镜像,并传递给low-level runtimes来运行容器。目前主流的 high-level runtime 有:
docker – 老熟人完整的集装箱(Container)解决方案
containerd – 本章主角
rkt – 与Docker类似的容器引擎更加专注于解决安全、兼容、执行效率等方面的问题。
CRI 描述: CRI是Kubernetes定义的一组gRPC服务。Kubelet作为客户端,基于gRPC框架,通过Socket和容器运行时通信。它包括两类服务:镜像服务(Image Service)和运行时服务(Runtime Service)。镜像服务提供下载、检查和删除镜像的远程程序调用。运行时服务包含用于管理容器生命周期,以及与容器交互的调用 ( exec / attach / port-forward )
的远程程序调用。
CRI接口客户端工具
3.架构简述 CRI 的架构 描述: CRI 插件是 Kubernetes 容器运行时接口 (CRI) 的实现, Containerd 与 Kubelet 在同一节点上运行。
containerd 中的插件处理来自 Kubelet 的所有 CRI 服务请求,并使用容器内部来管理容器和容器映像,该插件使用 containerd 来管理整个容器生命周期和所有容器映像,并通过CNI(另一个CNCF项目)管理pod网络,如下图所示
weiyigeek.top-CRI 插件
让我们用一个例子来演示当 Kubelet 创建一个单容器 pod 时,插件是如何工作的
Kubelet 通过 CRI 运行时服务 API 调用插件来创建 pod;
cri 创建 Pod 的网络命名空间,然后使用 CNI 对其进行配置;
cri 使用 containerd internal 创建和启动一个特殊的暂停容器(沙盒容器),并将该容器放在 pod 的 cgroup 和命名空间中(为简洁起见省略了步骤);
Kubelet随后通过CRI镜像服务API调用插件,拉取应用容器镜像;
cri 进一步使用容器来拉取图像,如果图像不存在于节点上;
然后 Kubelet 通过 CRI 运行时服务 API 调用,使用拉取的容器映像在 pod 内创建和启动应用程序容器;cri
cri 最后使用 containerd internal 创建应用程序容器,将其放在 pod 的 cgroup 和命名空间中,然后启动 pod 的新应用程序容器。完成这些步骤后,将创建并运行 Pod 及其相应的应用程序容器。
Containerd 的架构 描述: containerd是Linux和Windows的守护程序。它管理其主机系统的完整容器生命周期,从图像传输和存储到容器执行和监督,再到低级存储到网络附件等等。
Containerd 的设计是一个大的插件系统,下图中每一个虚线框其实就对应一个插件。
1) 底层系统支持 Linux 、Windows操作系统 和 支持 arm 和 x86 架构
2) 中间 containerd 包含 Backend、core、API 三层
Backend 层: Runtime plugin 提供了容器运行时的具体操作,为了支持不同的容器运行时 containerd 也提供了一系列的 containerd-shim
Core 层: 则是核心部分,提供了各种功能的服务,其中比较常用的是 Content service ,提供对镜像中可寻址内容的访问,所有不可变的内容都被存储在这里;Images Service 提供镜像相关的操作;Snapshot Plugin : 用来管理容器镜像的文件系统快照。镜像中的每一个 layer 都会被解压成文件系统快照,类似于 Docker 中的 graphdriver
API 层: 通过 GRPC 与客户端连接,例如提供了给 Prometheus 使用 API 来进行监控,给kubernetes提供了CRI接口,给containerd提供了服务处理程序。
3) 高层提供各种客户端,包括 K8s 的 kubelet,containerd 自己的命令行 ctr 等。
先来看看 Containerd 的架构:
weiyigeek.top-containerd架构体系
可以看到 Containerd 仍然采用标准的 C/S 架构,服务端通过 GRPC 协议提供稳定的 API,客户端通过调用服务端的 API 进行高级的操作。
为了解耦,Containerd 将不同的职责划分给不同的组件,每个组件就相当于一个子系统(subsystem)。连接不同子系统的组件被称为模块。
总体上 Containerd 被划分为两个子系统:
Bundle : 在 Containerd 中,Bundle 包含了配置、元数据和根文件系统数据,你可以理解为容器的文件系统。而 Bundle 子系统允许用户从镜像中提取和打包 Bundles。
Runtime : Runtime 子系统用来执行 Bundles,比如创建容器。
其中,每一个子系统的行为都由一个或多个模块协作完成(架构图中的 Core 部分)。每一种类型的模块都以插件的形式集成到 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】由 Task 触发运行时
, 并对外提供 GRPC 方式的 API 以及 Metrics 接口。
weiyigeek.top-架构图简化
containerd-shim 描述: 主要是用于剥离 containerd 守护进程与容器进程。目前已有 shim v1 和 shim v2 两个版本;它是containerd中的一个组件,其通过 shim 调用 runc 的包函数来启动容器。直白的说引入shim是允许runc在创建和运行容器之后退出, 并将shim作为容器的父进程, 而不是containerd作为父进程。
1 2 3 4 5 6 7 8 pstree systemd─┬─VGAuthService ├─containerd───15*[{containerd}] ├─containerd-shim─┬─sh │ └─13*[{containerd-shim}] ├─2*[containerd-shim─┬─sh] │ └─12*[{containerd-shim}]]
此种方式的目的是当containerd进程挂掉时保证容器不受影响, 此外 shim 也可以收集和报告容器的退出状态,不需要 containerd 来 wait 容器进程。
Tips:我们有需求去替换 runc 运行时工具库时,例如 替换为安全容器 kata container 或 Google 研发的 gViser,则需要增加对应的 shim(kata-shim)
以上两者均有自己实现的 shim。
CRI & OCI 如下图所示 dockershim,containerd 和 cri-o 都是遵循CRI的容器运行时,我们称他们为高层级运行时(High-level Runtime)。
weiyigeek.top-CRI
Containerd vs Cri-o 描述: 前面我们说到过kubernetes为啥会替换掉Docker呢?主要原因就是其复杂性,由于Docker的多层封装和调用,导致其在可维护性上略逊一筹,增加了线上问题的定位难度(貌似除了重启Docker,我们就毫无他法了);
如下图所示,我们总结了Docker,containerd以及cri-o的详细调用层级, Containerd和cri-o的方案比起 Docker 简洁很多, 因此我们更偏向于选用更加简单和纯粹的 containerd 和 cri-o 作为我们的容器运行时,kubernetes 1.20.x 之上的版本建议使用containerd作为容器运行时。
weiyigeek.top-容器运行时调用层级
如下图所示,我们对containerd和cri-o进行了一组性能测试,包括创建、启动、停止和删除容器,得出它们所耗的时间。 Tips : containerd在各个方面都表现良好,除了启动容器这项。从总用时来看 containerd的用时还是要比cri-o要短的。
weiyigeek.top-containerd和crio的性能比较
如下图所示, 从功能性来讲containerd和cri-o都符合CRI和OCI
的标准。从稳定性来说,单独使用containerd和cri-o都没有足够的生产环境经验。但庆幸的是,containerd一直在Docker里使用,而Docker的生产环境经验可以说比较充足。可见在稳定性上containerd略胜一筹。所以我们最终选用了containerd。
-
Containerd
CRI-O
备注
性能
更优
优
-
功能
优
优
CRI与OCI兼容
稳定性
稳定
未知
-
Device Mapper vs. Overlayfs 描述: 容器运行时使用存储驱动程序(storage driver)来管理镜像和容器的数据。目前我们生产环境选用的是Device Mapper。然而目前Device Mapper在新版本的Docker中已经被弃用,containerd也放弃对Device Mapper的支持。
当初选用 Device Mapper,也是有历史原因的。我们大概是在2014年开始Kubernetes这个项目的。那时候Overlayfs都还没合进kernel。当时我们评估了Docker支持的存储驱动程序,发现Device Mapper是最稳定的。所以我们选用了Device Mapper。但是实际使用下来,由Device Mapper引起的Docker问题也不少。所以我们也借这个契机把Device Mapper给换掉,换成现在containerd和Docker都默认的Overlayfs。
从下图的测试结果来看,Overlayfs 的 IO性能比 Device Mapper 好很多。Overlayfs的IOPS大体上能比Device Mapper高20%,和直接操作主机路径差不多。最终,我们选用了containerd,并以Overlayfs作为存储后端的文件系统,替换了原有的Docker加Device Mapper的搭配。
weiyigeek.top-后端存储文件系统性能比较
我们在同一个节点上同时起10,30,50和80的Pod,然后再同时删除,去比较迁移前后创建和删除的用时。从下图可知,containerd用时明显优于Docker。
weiyigeek.top-创建与删除Pod的用时比较
Docker和containerd除了上述常用命令有些区别外,在容器日志及相关参数配置方面也存在一些差异
weiyigeek.top-日志与cni参数配置
0x01 安装配置 1.Ubuntu安装Containerd.io流程 安装环境: 已进行安全基线加固以及系统优化。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $ lsb_release -a $ uname -a $ apt-cache madison containerd.io | cut -d '|' -f 2 | sed 's/^[ \t]*//g'
安装流程
Step 1.参考Kubernetes容器运行时接口 进行安装前的基础环境准备.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf overlay br_netfilter EOF sudo modprobe overlay sudo modprobe br_netfilter cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf net.ipv4.ip_forward = 1 net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 EOF sudo sysctl --system
Step 2.在Ubuntu中安装containerd.io,它来自官方Docker存储库的软件包我们可以参考安装Install Docker Engine
(https://docs.docker.com/engine/install/ubuntu/)中的安装命令。
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 sudo apt-get remove docker docker-engine docker.io containerd runc sudo apt-get update sudo apt-get install \ apt-transport-https \ ca-certificates \ curl \ gnupg \ lsb-release curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo \ "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/nullsudo apt-get update apt install containerd.io=1.4.6-1 $ ctr --version
Step 3.初始化Containerd.io基础配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 sudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml sed -ir 's#sandbox_image = \S\w+.*$#sandbox_image = "registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.2"#g' /etc/containerd/config.toml sed -ir "s#https://registry-1.docker.io#https://xlx9erfu.mirror.aliyuncs.com#g" /etc/containerd/config.toml systemctl daemon-reload && systemctl restart containerd systemctl status containerd.service
Tips : 查看默认的配置文件,其中root与state 分别表示 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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 $ cat /etc/containerd/config.toml version = 2 root = "/var/lib/containerd" state = "/run/containerd" plugin_dir = "" disabled_plugins = [] required_plugins = [] oom_score = 0 [grpc] address = "/run/containerd/containerd.sock" tcp_address = "" tcp_tls_cert = "" tcp_tls_key = "" uid = 0 gid = 0 max_recv_message_size = 16777216 max_send_message_size = 16777216 [ttrpc] address = "" uid = 0 gid = 0 [debug] address = "" uid = 0 gid = 0 level = "" [metrics] address = "" grpc_histogram = false [cgroup] path = "" [timeouts] "io.containerd.timeout.shim.cleanup" = "5s" "io.containerd.timeout.shim.load" = "5s" "io.containerd.timeout.shim.shutdown" = "3s" "io.containerd.timeout.task.state" = "2s" [plugins] [plugins."io.containerd.gc.v1.scheduler" ] pause_threshold = 0.02 deletion_threshold = 0 mutation_threshold = 100 schedule_delay = "0s" startup_delay = "100ms" [plugins."io.containerd.grpc.v1.cri" ] disable_tcp_service = true stream_server_address = "127.0.0.1" stream_server_port = "0" stream_idle_timeout = "4h0m0s" enable_selinux = false selinux_category_range = 1024 sandbox_image = "registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0" stats_collect_period = 10 systemd_cgroup = false enable_tls_streaming = false max_container_log_line_size = 16384 disable_cgroup = false disable_apparmor = false restrict_oom_score_adj = false max_concurrent_downloads = 3 disable_proc_mount = false unset_seccomp_profile = "" tolerate_missing_hugetlb_controller = true disable_hugetlb_controller = true ignore_image_defined_volumes = false [plugins."io.containerd.grpc.v1.cri" .containerd] snapshotter = "overlayfs" default_runtime_name = "runc" no_pivot = false disable_snapshot_annotations = true discard_unpacked_layers = false [plugins."io.containerd.grpc.v1.cri" .containerd.default_runtime] runtime_type = "" runtime_engine = "" runtime_root = "" privileged_without_host_devices = false base_runtime_spec = "" [plugins."io.containerd.grpc.v1.cri" .containerd.untrusted_workload_runtime] runtime_type = "" runtime_engine = "" runtime_root = "" privileged_without_host_devices = false base_runtime_spec = "" [plugins."io.containerd.grpc.v1.cri" .containerd.runtimes] [plugins."io.containerd.grpc.v1.cri" .containerd.runtimes.runc] runtime_type = "io.containerd.runc.v2" runtime_engine = "" runtime_root = "" privileged_without_host_devices = false base_runtime_spec = "" [plugins."io.containerd.grpc.v1.cri" .containerd.runtimes.runc.options] [plugins."io.containerd.grpc.v1.cri" .cni] bin_dir = "/opt/cni/bin" conf_dir = "/etc/cni/net.d" max_conf_num = 1 conf_template = "" [plugins."io.containerd.grpc.v1.cri" .registry] [plugins."io.containerd.grpc.v1.cri" .registry.mirrors] [plugins."io.containerd.grpc.v1.cri" .registry.mirrors."docker.io" ] endpoint = ["https://registry-1.docker.io" ] [plugins."io.containerd.grpc.v1.cri" .image_decryption] key_model = "" [plugins."io.containerd.grpc.v1.cri" .x509_key_pair_streaming] tls_cert_file = "" tls_key_file = "" [plugins."io.containerd.internal.v1.opt" ] path = "/opt/containerd" [plugins."io.containerd.internal.v1.restart" ] interval = "10s" [plugins."io.containerd.metadata.v1.bolt" ] content_sharing_policy = "shared" [plugins."io.containerd.monitor.v1.cgroups" ] no_prometheus = false [plugins."io.containerd.runtime.v1.linux" ] shim = "containerd-shim" runtime = "runc" runtime_root = "" no_shim = false shim_debug = false [plugins."io.containerd.runtime.v2.task" ] platforms = ["linux/amd64" ] [plugins."io.containerd.service.v1.diff-service" ] default = ["walking" ] [plugins."io.containerd.snapshotter.v1.devmapper" ] root_path = "" pool_name = "" base_image_size = "" async_remove = false
温馨提示: root用来保存持久化数据,包括 Snapshots, Content, Metadata 以及各种插件的数据。每一个插件都有自己单独的目录,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 $ tree -L 2 /var/lib/containerd/ /var/lib/containerd/ ├── io.containerd.content.v1.content │ ├── blobs │ └── ingest ├── io.containerd.grpc.v1.cri │ ├── containers │ └── sandboxes ├── io.containerd.metadata.v1.bolt │ └── meta.db ├── io.containerd.runtime.v1.linux │ └── k8s.io ├── io.containerd.runtime.v2.task ├── io.containerd.snapshotter.v1.aufs │ └── snapshots ├── io.containerd.snapshotter.v1.btrfs ├── io.containerd.snapshotter.v1.native │ └── snapshots ├── io.containerd.snapshotter.v1.overlayfs │ ├── metadata.db │ └── snapshots └── tmpmounts 18 directories, 2 files
温馨提示: 用来保存临时数据,包括 sockets、pid、挂载点、运行时状态以及不需要持久化保存的插件数据。1 2 3 4 5 6 7 8 9 10 11 12 13 14 🐳 → tree -L 2 /run/containerd/ /run/containerd/ ├── containerd.sock ├── containerd.sock.ttrpc ├── io.containerd.grpc.v1.cri │ ├── containers │ └── sandboxes ├── io.containerd.runtime.v1.linux │ └── k8s.io ├── io.containerd.runtime.v2.task └── runc └── k8s.io 8 directories, 2 files
温馨提示: 值得注意的是 OOM 这一配置项 oom_score = 0
。 Containerd 是容器的守护者,一旦发生内存不足的情况,理想的情况应该是先杀死容器,而不是杀死 Containerd。所以需要调整 Containerd 的 OOM 权重,减少其被 OOM Kill 的几率,最好是将 oom_score 的值调整为比其他守护进程略低的值。这里的 oom_socre 其实对应的是 /proc/<pid>/oom_socre_adj
,在早期的 Linux 内核版本里使用 oom_adj 来调整权重, 后来改用 oom_socre_adj
了。在计算最终的 badness score
时,会在计算结果是中加上 oom_score_adj ,这样用户就可以通过该在值来保护某个进程不被杀死或者每次都杀某个进程。其取值范围为 -1000 到 1000。
如果将该值设置为 -1000,则进程永远不会被杀死,因为此时 badness score 永远返回0, 建议 Containerd 将该值设置为 -999 到 0 之间。如果作为 Kubernetes 的 Worker 节点,可以考虑设置为 -999。
Step 4.查看containerd Client与Server端的信息。1 2 3 4 5 6 7 8 9 10 $ ctr version Client: Version: 1.4.6 Revision: d71fcd7d8303cbf684402823e425e9dd2e99285d Go version: go1.13.15 Server: Version: 1.4.6 Revision: d71fcd7d8303cbf684402823e425e9dd2e99285d UUID: 71a28bbb-6ed6-408d-a873-e394d48b35d8
Step 5.查看 Containerd 的 systemd 配置与自启设置。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 $ cat /lib/systemd/system/containerd.service | grep -v "#" [Unit] Description=containerd container runtime Documentation=https://containerd.io After=network.target local -fs.target [Service] ExecStartPre=-/sbin/modprobe overlay ExecStart=/usr/bin/containerd Type=notify Delegate=yes KillMode=process Restart=always RestartSec=5 LimitNPROC=infinity LimitCORE=infinity LimitNOFILE=1048576 TasksMax=infinity OOMScoreAdjust=-999 [Install] WantedBy=multi-user.target systemctl enable containerd --now
上述配置中有两个重要参数即 Delegate 和 KillMode。
Delegate : 这个选项允许 Containerd 以及运行时自己管理自己创建的容器的 cgroups。如果不设置这个选项,systemd 就会将进程移到自己的 cgroups 中,从而导致 Containerd 无法正确获取容器的资源使用情况。
KillMode : 这个选项用来处理 Containerd 进程被杀死的方式。默认情况下,systemd 会在进程的 cgroup 中查找并杀死 Containerd 的所有子进程,这肯定不是我们想要的。KillMode字段可以设置的值如下。
control-group
(默认值):当前控制组里面的所有子进程,都会被杀掉
process
:只杀主进程
mixed
:主进程将收到 SIGTERM 信号,子进程收到 SIGKILL 信号
none
:没有进程会被杀掉,只是执行服务的 stop 命令。
我们需要将 KillMode 的值设置为 process,这样可以确保升级或重启 Containerd 时不杀死现有的容器。
2.Containerd 快速进行安装部署 (最新) 步骤 01.【所有节点】在各主机中安装 containerd.io 运行时服务,Kubernertes 通过 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 sudo apt-get remove docker docker-engine docker-ce docker.io containerd containerd.io runc curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo \ "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/container.list > /dev/nullsudo apt-get update sudo apt-get install apt-transport-https ca-certificates curl gnupg lsb-release apt-cache madison containerd.io | head -n 2 apt install -y containerd.io=1.5.11-1 apt-mark hold containerd.io
步骤 02.【所有节点】进行containerd 配置创建并修改 config.toml .
1 2 3 4 5 6 mkdir -vp /etc/containerd/ containerd config default > /etc/containerd/config.toml sed -i "s#k8s.gcr.io/pause#registry.cn-hangzhou.aliyuncs.com/google_containers/pause#g" /etc/containerd/config.toml sed -i 's#SystemdCgroup = false#SystemdCgroup = true#g' /etc/containerd/config.toml sed -i '/registry.mirrors]/a\ \ \ \ \ \ \ \ [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]' /etc/containerd/config.toml sed -i '/registry.mirrors."docker.io"]/a\ \ \ \ \ \ \ \ \ \ endpoint = ["https://xlx9erfu.mirror.aliyuncs.com"]' /etc/containerd/config.toml
补充说明: 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."docker.io"] endpoint = ["https://xlx9erfu.mirror.aliyuncs.com","https://docker.mirrors.ustc.edu.cn"] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."gcr.io"] endpoint = ["https://gcr.mirrors.ustc.edu.cn"] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"] endpoint = ["https://gcr.mirrors.ustc.edu.cn/google-containers/"] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."quay.io"] endpoint = ["https://quay.mirrors.ustc.edu.cn"]
步骤 03.containerd 运行时与镜像端点设置1 2 3 4 5 6 7 8 9 10 11 cat <<EOF > /etc/crictl.yaml runtime-endpoint: unix:///run/containerd/containerd.sock image-endpoint: unix:///run/containerd/containerd.sock timeout: 10 debug: false EOF
步骤 05.重载 systemd自启和启动containerd.io服务。1 2 3 4 5 6 7 8 9 10 11 12 systemctl daemon-reload && systemctl enable --now containerd.service systemctl status containerd.service ctr version
步骤 06.验证 CRI 默认的 runtime 与 image 配置1 2 3 4 5 crictl config --get runtime-endpoint crictl config --get image-endpoint
0x02 Containerd 简单尝试使用 描述: 此处以上面Ubuntu的安装环境进行演示。
1.镜像拉取与运行 Tips : containerd 默认的三个名称空间(Namespace)是default,docker.io,k8s.io
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 57 58 59 60 61 ctr image pull docker.io/library/hello-world:latest ctr -n k8s.io image pull docker.io/library/busybox:latest ctr -n k8s.io image pull registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0 ctr image ls && ctr -n k8s.io image ls ctr run docker.io/library/hello-world:latest hello-world ctr -n k8s.io container create docker.io/library/busybox:latest busybox ctr -n k8s.io task start -d busybox ctr container ls && ctr -n k8s.io container ls ctr -n k8s.io task ls ps ajxf|grep "containerd-shim-runc\|25856" |grep -v grep 1 25828 25828 3097 ? -1 Sl 0 0:00 /usr/bin/containerd-shim-runc-v2 -namespace k8s.io -id busybox -address /run/containerd/containerd.sock 25828 25856 25856 25856 ? -1 Ss 0 0:00 \_ sh ctr -n k8s.io t exec --exec -id $RANDOM -t busybox sh ctr -n k8s.io t kill -s SIGKILL busybox ctr -n k8s.io task rm busybox ctr -n k8s.io snapshots rm busybox ctr -n k8s.io container rm busybox ctr container rm hello-world
2.创建和使用网络 描述: 在前面我们进入busybox容器中会发现里面只有一张网卡而且无法连接到外部网络之中,所以我们需要借助于CNI(Container Network Interface 它是一个云计算基础项目来实现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 mkdir -p /etc/cni/net.d cat >/etc/cni/net.d/10-mynet.conf <<EOF { "cniVersion" : "0.2.0" , "name" : "mynet" , "type" : "bridge" , "bridge" : "cni0" , "isGateway" : true , "ipMasq" : true , "ipam" : { "type" : "host-local" , "subnet" : "10.22.0.0/16" , "routes" : [ { "dst" : "0.0.0.0/0" } ] } } EOF cat >/etc/cni/net.d/99-loopback.conf <<EOF { "cniVersion" : "0.2.0" , "name" : "lo" , "type" : "loopback" } EOF apt install jq -y cd /usr/local /cni-0.8.1/scripts//usr/local /cni-0.8.1/scripts Hello World! CNI Plugins. /usr/local /cni-0.8.1/scripts 3: cni0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000 link/ether ea:3e:1b:23:66:3a brd ff:ff:ff:ff:ff:ff inet 10.22.0.1/16 brd 10.22.255.255 scope global cni0 valid_lft forever preferred_lft forever
Step 3.使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 ctr -n k8s.io run -d docker.io/library/busybox:latest busybox ctr -n k8s.io task ls pid=$(ctr -n k8s.io t ls|grep busybox|awk '{print $2}' ) netnspath=/proc/$pid /ns/net CNI_PATH=/usr/local /cni-plugins /usr/local /cni-0.8.1/scripts/exec -plugins.sh add $pid $netnspath ctr -n k8s.io task exec --exec -id $RANDOM -t busybox sh - PING 223.6.6.6 (223.6.6.6): 56 data bytes 64 bytes from 223.6.6.6: seq=0 ttl=113 time=7.902 ms 64 bytes from 223.6.6.6: seq=1 ttl=113 time=7.102 ms ctr -n k8s.io run -d docker.io/library/busybox:latest busybox-1 pid=$(ctr -n k8s.io t ls|grep busybox-1|awk '{print $2}' ) netnspath=/proc/$pid /ns/net CNI_PATH=/usr/local /cni-plugins /usr/local /cni-0.8.1/scripts/exec -plugins.sh add $pid $netnspath ps ajxf|egrep "containerd-shim-runc|43908|48850" |grep -v grep ctr -n k8s.io task exec --exec -id $RANDOM -t busybox-1 sh -
Tips : 采用nc -l -v -p 8080
监听在另外的一个容器里面进行通信链接。
weiyigeek.top-CNI-Plugins
3.与宿主机和其它容器共享文件 描述: 在Docker我们常常需要将配置文件或者各类数据映射进入到docker容器之中,便于容器内部程序使用或者数据的持久化。
下面分别简单演示与宿主机或者其它容器共享目录;
首先我们对docker的ns共享进行实验:1 2 3 4 5 6 7 8 9 10 11 12 13 14 [root@docker scripts] 687c80243ee15e0a2171027260e249400feeeee2607f88d1f029cc270402cdd1 [root@docker scripts] fa2c09bd9c042128ebb2256685ce20e265f4c06da6d9406bc357d149af7b83d2 [root@docker scripts] CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fa2c09bd9c04 busybox "cat" 2 seconds ago Up 1 second pedantic_goodall 687c80243ee1 busybox "sh" 22 seconds ago Up 21 seconds hopeful_franklin [root@docker scripts] / PID USER TIME COMMAND 1 root 0:00 sh 8 root 0:00 cat 15 root 0:00 sh
然后基于containerd的方式实现pid ns
共享:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $ ctr -n k8s.io t ls $ ctr -n k8s.io c create --with-ns "pid:/proc/48850/ns/pid" v4ehxdz8.mirror.aliyuncs.com/library/python:3.6-slim python $ ctr -n k8s.io t start -d python python $ ctr -n k8s.io t exec -t --exec -id $RANDOM busybox-1 sh / PID USER TIME COMMAND 1 root 0:00 sh 34 root 0:00 python3 41 root 0:00 sh 47 root 0:00 ps aux
4.Docker 与 Containerd 并用配置 描述: 事实上,Docker 和 Containerd 是可以同时使用的,只不过 Docker 默认使用的 Containerd 的命名空间不是 default,而是 moby,此处为了更方便我们学习可以将 Docker 与 Containerd 联合使用。 Docker-CE 安装参考链接:https://docs.docker.com/engine/reference/commandline/dockerd/
Step 1.在前面我们完成 containerd 的安装配置并启动后,我们可以在宿主机中安装docker客户端及服务。
1 2 3 4 5 6 7 8 echo \ "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/nullapt-update apt-cache madison docker-ce sudo apt-get install docker-ce=5:20.10.7~3-0~ubuntu-focal docker-ce-cli=5:20.10.7~3-0~ubuntu-focal
Step 2.编辑/etc/systemd/system/multi-user.target.wants/docker.service
文件并为其新增 --containerd
如下启动项:--containerd /run/containerd/containerd.sock
1 2 3 4 5 vim /etc/systemd/system/multi-user.target.wants/docker.service ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --cri-containerd --debug systemctl daemon-reload
Step 3.添加低权限用户到docker组里并自启Docker。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 sudo gpasswd -a ${USER} docker systemctl enable docker && systemctl status docker ps aux|grep docker
Step 4.打开两个Shell终端一个采用 docker 运行一个 busybox 容器,另外一个查看对比。
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 $ docker run --rm -it busybox sh $ ctr -n moby c ls $ ctr -n moby t ls $ grep "172.17.0.2" /proc/131926/net/fib_trie |-- 172.17.0.2 |-- 172.17.0.2 $ ctr -n moby t exec -t --exec -id $RANDOM 51dcdf68d3b8bf4c9707c9059e33f9570cb83725355595e77cf433dd3e25b64b sh
5.镜像加速配置 描述: 镜像加速的配置就在 cri 插件配置块下面的 registry 配置块,所以需要修改的部分如下:1 2 3 4 5 6 7 8 9 $ vim /etc/containerd/config.toml [plugins."io.containerd.grpc.v1.cri" .registry] [plugins."io.containerd.grpc.v1.cri" .registry.mirrors] [plugins."io.containerd.grpc.v1.cri" .registry.mirrors."docker.io" ] endpoint = ["https://xlx9erfu.mirror.aliyuncs.com" ] [plugins."io.containerd.grpc.v1.cri" .registry.mirrors."k8s.gcr.io" ] endpoint = ["https://registry.aliyuncs.com/k8sxio" ] [plugins."io.containerd.grpc.v1.cri" .registry.mirrors."gcr.io" ] endpoint = ["xxx" ]
插件及其参数解释:
registry.mirrors."xxx"
: 表示需要配置 mirror 的镜像仓库。例如,registry.mirrors.”docker.io” 表示配置 docker.io 的 mirror。
endpoint
: 表示提供 mirror 的镜像加速服务。例如,这里推荐使用西北农林科技大学提供的镜像加速服务作为 docker.io 的 mirror, 或者阿里云的
0x03 Containerd 与 Docker CLI 工具命令表 描述: Containerd 和 Docker 在命令使用上的一些区别主要如下:
功能说明
Containerd CLI
Docker CLI
Crictl CLI
镜像列举
ctr image [ls/list]
docker images [ls]
crictl images [ls]
导出镜像
ctr image export app.tar weiyigeek.top/app:1.2.0
docker save -o app.tar app:1.2.0
导入镜像
ctr image import app.tar
docker load -i app.tar
拉取镜像
ctr -n k8s.io images pull docker.io/library/redis:latest
docker pull redis:latest
crictl pull redis:latest
上传镜像
ctr -n k8s.io images push docker.io/library/redis:latest
docker push
crictl push
更改标记
ctr -n k8s.io images tag docker.io/library/redis:latest weiyigeek.top/redis:latest
docker tag redis:latest weiyigeek.top/redis:latest
删除镜像
ctr -n k8s.io images rm docker.io/library/redis:latest
docker rmi
crictl rmi
创建容器
ctr -n k8s.io container create docker.io/library/redis:latest redis
docker create
crictl create
创建并运行容器
ctr run -d –env name=WeiyiGeek application weiyigeek.top/app:1.2.0
docker run
查看容器
ctr -n k8s.io container list
docker ps
crictl ps
启动容器
ctr -n k8s.io task start
docker start
crictl start
停止容器
ctr -n k8s.io task pause
docker stop
crictl stop
删除容器
ctr -n k8s.io container rm
docker rm
crictl rm
容器详情
ctr -n k8s.io c info 39d36ef08456
docker inspect 39d36ef08456
crictl inspect 39d36ef08456
容器连接
ctr -n k8s.io task attach
docker attach
crictl attach
进入容器
ctr -n k8s.io task exec
docker exec
crictl exec
stats(状态)
ctr -n k8s.io task metric 39d36ef08456
docker top
crictl stats
日志查看
ctr -n k8s.io event
docker logs –tail 50 8db74c2bf7595
crictl logs –tail 50 8db74c2bf7595
ctr 命令 - 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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 ctr [global options] command [command options] [arguments...] --debug enable debug output in logs --address value, -a value address for containerds GRPC server (default: "/run/containerd/containerd.sock" ) [$CONTAINERD_ADDRESS ] --timeout value total timeout for ctr commands (default: 0s) --connect-timeout value timeout for connecting to containerd (default: 0s) --namespace value, -n value namespace to use with commands (default: "default" ) [$CONTAINERD_NAMESPACE ] --version, -v print the version plugins, plugin provides information about containerd plugins version print the client and server versions containers, c, container manage containers content manage content events, event display containerd events images, image, i manage images leases manage leases namespaces, namespace, ns manage namespaces pprof provide golang pprof outputs for containerd run run a container snapshots, snapshot manage snapshots tasks, t, task manage tasks install install a new package oci OCI tools shim interact with a shim directly (直接与shim交互) help , h Shows a list of commands or help for one command check check that an image has all content available locally(找到发生故障的容器后,可以使用以下工具检查其日志) export export images import import images list, ls list images known to containerd mount mount an image to a target path unmount unmount the image from the target pull pull an image from a remote push push an image to a remote remove, rm remove one or more images by reference tag tag an image label set and clear labels for an image create create container delete, del, rm delete one or more existing containers info get info about a container list, ls list containers label set and clear labels for a container checkpoint checkpoint a container (检查点容器) restore restore a container from checkpoint(从检查点还原容器) --rm remove the container after running --null-io send all IO to /dev/null (将容器内标准输出重定向到/dev/null) --log -uri value log uri --detach, -d detach from the task after it has started execution(在任务开始执行后从中分离,如没有选项则会等待用户输入并定向到容器内) --fifo-dir value directory used for storing IO FIFOs --cgroup value cgroup path (To disable use of cgroup, set to "" explicitly) --platform value run image for specific platform --runc-binary value specify runc-compatible binary --runc-systemd-cgroup start runc with systemd cgroup manager --uidmap container-uid:host-uid:length run inside a user namespace with the specified UID mapping range; specified with the format container-uid:host-uid:length --gidmap container-gid:host-gid:length run inside a user namespace with the specified GID mapping range; specified with the format container-gid:host-gid:length --remap-labels provide the user namespace ID remapping to the snapshotter via label options; requires snapshotter support --cpus value set the CFS cpu qouta (default: 0) --snapshotter value snapshotter name. Empty value stands for the default value. [$CONTAINERD_SNAPSHOTTER ] --config value, -c value path to the runtime-specific spec config file --cwd value specify the working directory of the process --env value specify additional container environment variables (i.e. FOO=bar) --env-file value specify additional container environment variables in a file(i.e. FOO=bar, one per line) --label value specify additional labels (i.e. foo=bar) --mount value specify additional container mount (ex: type =bind ,src=/tmp,dst=/host,options=rbind:ro) --net-host enable host networking for the container --privileged run privileged container --read -only set the containers filesystem as readonly --runtime value runtime name (default: "io.containerd.runc.v2" ) --tty, -t allocate a TTY for the container --with-ns value specify existing Linux namespaces to join at container runtime (format '<nstype>:<path>' ) --pid-file value file path to write the task's pid --gpus value add gpus to the container (default: 0) --allow-new-privs turn off OCI spec' s NoNewPrivileges feature flag --memory-limit value memory limit (in bytes) for the container (default: 0) --device value add a device to a container --seccomp enable the default seccomp profile --rootfs use custom rootfs that is not managed by containerd snapshotter --no-pivot disable use of pivot-root (linux only) --cpu-quota value Limit CPU CFS quota (default: -1) --cpu-period value Limit CPU CFS period (default: 0) attach attach to the IO of a running container checkpoint checkpoint a container delete, rm delete one or more tasks exec execute additional processes in an existing container list, ls list tasks kill signal a container (default: SIGTERM) pause pause an existing container ps list processes for container resume resume a paused container start start a container that has been created metrics, metric get a single data point of metrics for a task with the built-in Linux runtime
Tips : containerd 引入了 namespace 概念, 每个image和container都会在各自的namespace下可见, 目前k8s会使用 k8s.io 以及 default 作为命名空间。 Tips : 当导入本地镜像时ctr不支持压缩。 Tips : 通过ctr containers create
创建容器后,只是一个静态的容器,容器中的用户进程并没有启动,所以还需要通过ctr task start
来启动容器进程。 当然也可以用ctr run
的命令直接创建并运行容器。在进入容器操作时与docker不同的是,必须在ctr task exec
命令后指定--exec-id
参数,这个id可以随便写只要唯一就行。 Tips : ctr没有stop容器的功能,只能暂停(ctr task pause)或者杀死(ctr task kill)容器
实际案例: 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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 ctr namespace create devops ctr namespace ls -q ctr namespace remove devops ctr -n k8s.io images ls | grep "busybox" ctr -n k8s.io images ls -q | grep "busybox" ctr -n k8s.io i rm k8s.gcr.io/pause:3.2 ctr -n k8s.io i pull -k k8s.gcr.io/pause:3.2 ctr -n k8s.io i push -k k8s.gcr.io/pause:3.2 ctr -n k8s.io i export pause.tar k8s.gcr.io/pause:3.2 ctr -n k8s.io i import pause.tar ctr -n k8s.io images check | grep "busybox" ctr -n k8s.io i tag registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.2 k8s.gcr.io/pause:3.2 ctr -n k8s.io i tag --force registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.2 k8s.gcr.io/pause:3.2 ctr -n k8s.io i label docker.io/library/busybox:1.33.1 name=weiyigeek-test ctr -n k8s.io i label docker.io/library/busybox:1.33.1 name="" ctr i mount docker.io/library/nginx:alpine /mnt ctr i unmount /mnt → ctr content ls → ctr content edit --editor vim sha256:fdd7fff110870339d34cf071ee90fbbe12bdbf3d1d9a14156995dfbdeccd7923 ctr -n k8s.io container ls ctr -n k8s.io container create docker.io/library/busybox:latest busybox ctr -n k8s.io container info 39d36ef08456bed24a52bf1c845facbd9f93c35cbd65b16739b4746ab1811cb5 ctr -n k8s.io container rm 39d36ef08456bed24a52bf1c845facbd9f93c35cbd65b16739b4746ab1811cb5 ctr -n k8s.io container label busybox name=weiyigeek-test ctr -n k8s.io task start -d nginx ctr -n k8s.io tasks ls ctr -n k8s.io task pause nginx ctr -n k8s.io task resume nginx ctr -n k8s.io task ps nginx ctr -n k8s.io task exec --exec -id 0 -t nginx sh ctr -n k8s.io tasks kill -a -s 9 {id} ctr -n k8s.io task metric 37d0ff9d8df20d34c652ee286c17e0626d8838d6d546a28e8246151fffd99b98 ctr -n k8s.io run --null-io --net-host -d –env PASSWORD=$drone_password –mount type =bind ,src=/etc,dst=/host-etc,options=rbind:rw –mount type =bind ,src=/root/.kube,dst=/root/.kube,options=rbind:rw -log -uri file:///var/log /xx.log WeiyiGeek:Test sysreport bash /sysreport/run.sh ctr plugin ls ctr run --null-io --net-host -d --mount type =bind ,src=/home/registry,dst=/var/lib/registry,options=rbind:rw --mount type =bind ,src=/home/registry/certs,dst=/certs,options=rbind:rw --mount type =bind ,src=/home/registry/config.yml,dst=/etc/docker/registry/config.yml,options=rbind:rw docker.io/library/registry:latest registry-v2 ctr -n default run --rm --net-host --env DOCKERHUB=docker.io \ --mount type =bind ,src=/storage/dev/soft/kaniko/config,dst=/kaniko/.docker,options=rbind:ro \ --mount type =bind ,src=/storage/dev/soft/kaniko/demo1,dst=/workspace,options=rbind:rw \ registry.cn-hangzhou.aliyuncs.com/weiyigeek/kaniko-executor:latest kaniko-executor \ /kaniko/executor --dockerfile=/workspace/dockerfile --context=dir://workspace --destination=docker.io/weiyigeek/busybox:1.35.0
Tips: 注意容器默认使用fifo创建日志文件, 有可能因为fifo容量导致业务运行阻塞。 Tips: 注意需要停止容器时需要先停止容器内的Task再删除容器。 Tips: 注意ctr没有stop容器的功能,只能暂停或者杀死容器。 Tips: Container 创建阶段
: 一个 container 对象只是包含了运行一个容器所需的资源及配置的数据结构,意味着 namespaces、rootfs 和容器的配置都已经初始化成功了,只是用户进程(这里是nginx)还没有启动。 Tips: Container 运行阶段
: 一个容器真正的运行起来是由 Task 对象实现的,task 代表任务的意思,可以为容器设置网卡,还可以配置工具来对容器进行监控等。
crictl 命令 - Kubernetes 管理命令详解 描述:crictl 是 CRI 兼容的容器运行时命令行对接客户端, 你可以使用它来检查和调试 Kubernetes 节点上的容器运行时和应用程序。由于该命令是为k8s通过CRI使用containerd而开发的(主要是调试工具
), 其他非k8s的创建的 crictl 是无法看到和调试的, 简单的说用 ctr run
运行的容器无法使用 crictl 看到。
Tips: crictl 命令工具 和 它的源代码在 cri-tools 代码库 。 Tips: crictl 默认使用命名空间 k8s.io. Tips: cri plugin区别对待pod和container
官方参考: https://github.com/kubernetes-sigs/cri-tools/blob/master/docs/crictl.md
基础配置 描述: 在 k8s 1.19.x 之前 crictl 默认连接到 unix:///var/run/dockershim.sock
,而在1.20.x起默认采用 /run/containerd/containerd.sock
运行时,当然除此之外还是支持cri-o运行时。1 2 3 4 5 6 7 8 9 10 11 12 unix:///var/run/dockershim.sock or unix:///run/containerd/containerd.sock or unix:///run/crio/crio.sock npipe:////./pipe/dockershim or npipe:////./pipe/containerd or npipe:////./pipe/crio frakti: unix:///var/run/frakti.sock
查看或编辑 /etc/crictl.yaml 的内容并设置指定的容器运行时runtime。1 2 3 4 5 6 $ cat /etc/crictl.yaml runtime-endpoint: "unix:///run/containerd/containerd.sock" image-endpoint: "unix:///run/containerd/containerd.sock" timeout: 0 debug: false pull-image-on-create: false
Tips: 除了上面的设置端点外,我们还可以利用其它方式进行临时设置或者指定配置文件。1 2 3 4 5 crictl --config=/etc/crictl-demo.yaml crictl --runtime-endpoint="unix:///run/containerd/containerd.sock" --image-endpoint="unix:///run/containerd/containerd.sock"
基础语法: 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 crictl [global options] command [command options] [arguments...] attach Attach to a running container create Create a new container (创建容器这里需要先创建 sandbox, 获取sandbox容器的id后再用此id创建业务容器) exec Run a command in a running container version Display runtime version information images List images inspect Display the status of one or more containers (显示一个或多个容器的状态) inspecti Return the status of one or more images (返回一个或者多个镜像的状态) inspectp Display the status of one or more pods (返回一个或者多个Pod的状态) imagefsinfo: Return image filesystem info logs Fetch the logs of a container port-forward Forward local port to a pod ps List containers (列出在 k8s.io 命名空间下的业务容器) pull Pull an image from a registry runp Run a new pod rm Remove one or more containers rmi Remove one or more images rmp Remove one or more pods pods List pods (列出在 k8s.io 命名空间下的 sandbox 容器, 在k8s里通常是pause容器) start Start one or more created containers info Display information of the container runtime stop Stop one or more running containers stopp Stop one or more running pods update Update one or more running containers config Get and set crictl options stats List container(s) resource usage statistics completion Output bash shell completion code help , h Shows a list of commands or help for one command --config value, -c value Location of the client config file (default: "/etc/crictl.yaml" ) [$CRI_CONFIG_FILE ] --debug, -D Enable debug mode --image-endpoint value, -i value Endpoint of CRI image manager service [$IMAGE_SERVICE_ENDPOINT ] --runtime-endpoint value, -r value Endpoint of CRI container runtime service (default: "unix:///var/run/dockershim.sock" ) [$CONTAINER_RUNTIME_ENDPOINT ] --timeout value, -t value Timeout of connecting to the server (default: 10s)
实践案例: 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 crictl images --digests crictl images nginx crictl images -q crictl images -o [json|yaml|table] crictl inspecti busybox crictl pull busybox crictl push weiyigeek.top/busybox crictl rmi docker.io/library/busybox crictl ps -a crictl --runtime-endpoint /run/containerd/containerd.sock ps -a | grep kube | grep -v pause crictl create f84dd361f8dc51518ed291fbadd6db537b0496536c1d2d6c05ff943ce8c9a54f[创建的 Pod 的 ID] container-config.json pod-config.json crictl start 3e025dd50a72d956c4f14881fbb5b1080c9275674e95fb67f965f6478a957d60 crictl stop 3e025dd50a72d956c4f14881fbb5b1080c9275674e95fb67f965f6478a957d60 crictl exec -i -t 1f73f2d81bf98 ls $ crictl pods $ crictl pods --name nginx-65899c769f-wv2gp $ crictl pods --label run=nginx $ crictl runp pod-config.json $ crictl inspectp --output table $POD_ID $ crictl stopp $POD_ID $ crictl rmp $POD_ID crictl --runtime-endpoint /run/containerd/containerd.sock logs --tail 50 8db74c2bf7595 crictl stats crictl version crictl info -o go-template --template "{{index .status}}"
Tips : crictl pods
列出的是pod的信息,包括pod所在的命名空间以及状态。
Tips : crictl ps
列出的是应用容器的信息,而docker ps列出的是初始化容器(pause容器)和应用容器的信息,初始化容器在每个pod启动时都会创建,通常不会关注,从这一点上来说,crictl使用起来更简洁明了一些。
weiyigeek.top-pods与ps
Tips: 用 crictl 创建容器对容器运行时排错很有帮助。 在运行的 Kubernetes 集群中,沙盒会随机的被 kubelet 停止和删除, 下面通过实例进行演示crictl使用。
Step 1.编写运行 Pod 沙盒的 JSON 文件,使用 crictl runp 命令应用 JSON 文件并运行沙盒。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $ cat pod-config.json { "metadata" : { "name" : "nginx-sandbox" , "namespace" : "default" , "attempt" : 1 , "uid" : "hdishd83djaidwnduwk28bcsb" }, "log_directory" : "/tmp" , "linux" : { } } $ crictl runp pod-config.json f84dd361f8dc51518ed291fbadd6db537b0496536c1d2d6c05ff943ce8c9a54f
Step 2.检查沙盒是否处于就绪状态:
1 2 3 $ crictl pods POD ID CREATED STATE NAME NAMESPACE ATTEMPT f84dd361f8dc5 17 seconds ago Ready busybox-sandbox default 1
Step 3.使用运行时处理程序运行pod沙盒,运行时处理程序需要运行时支持。下面的示例显示如何在containerd运行时上使用runsc处理程序运行pod沙盒。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 $ cat pod-config.json { "metadata" : { "name" : "nginx-runsc-sandbox" , "namespace" : "default" , "attempt" : 1, "uid" : "hdishd83djaidwnduwk28bcsb" }, "log_directory" : "/tmp" , "linux" : { } } $ crictl runp --runtime=runsc pod-config.json c112976cb6caa43a967293e2c62a2e0d9d8191d5109afef230f403411147548c $ crictl inspectp c112976cb6caa43a967293e2c62a2e0d9d8191d5109afef230f403411147548c ... "runtime" : { "runtimeType" : "io.containerd.runtime.v1.linux" , "runtimeEngine" : "/usr/local/sbin/runsc" , "runtimeRoot" : "/run/containerd/runsc" }, ...
1 2 3 4 5 6 7 8 9 10 $ crictl ps -a CONTAINER ID IMAGE CREATED STATE NAME ATTEMPT 3e025dd50a72d busybox 32 seconds ago Created busybox 0 $ crictl start 3e025dd50a72d956c4f14881fbb5b1080c9275674e95fb67f965f6478a957d60 3e025dd50a72d956c4f14881fbb5b1080c9275674e95fb67f965f6478a957d60 $ crictl ps CONTAINER ID IMAGE CREATED STATE NAME ATTEMPT 3e025dd50a72d busybox About a minute ago Running busybox 0
Step 6.在容器中执行命令
1 2 crictl exec -i -t 3e025dd50a72d956c4f14881fbb5b1080c9275674e95fb67f965f6478a957d60 ls bin dev etc home proc root sys tmp usr var
Step 7.显示容器的统计信息
1 2 3 $ crictl stats CONTAINER CPU % MEM DISK INODES 3e025dd50a72d 0.00 983kB 16.38kB 6
Containerd 跟 Docker 的区别说明 Docker 对容器的管理和操作基本都是通过 containerd 完成的,只是相当对于对其进行了封装,提供一些非常简单的命令进行管理容器生命周期。
Containerd 是一个工业级标准的容器运行时,它强调简单性、健壮性和可移植性,主要在宿主机中管理完整的容器生命周期:容器镜像的传输和存储、容器的执行和管理、存储和网络等。
Kubernetes 调用链对比
在Kubenetes 1.19.x 与 Kubernetes 1.20.x 版本针对runtime分别使用docker、Container的调用链关系。1 2 3 4 5 Kubelet -> Dockerd -> Containerd Kubelet -> Containerd
命令对比:containerd不支持docker API和docker CLI, 但是可以通过cri-tool实现类似的功能。
镜像相关功能
docker
containerd
显示本地镜像列表
docker images
crictl images
下载镜像
docker pull
crictl pull
上传镜像
docker push
无
删除本地镜像
docker rmi
crictl rmi
查看镜像详情
docker inspect
crictl inspecti
容器相关功能
docker
containerd
显示容器列表
docker ps
crictl ps
创建容器
docker create
crictl create
启动容器
docker start
crictl start
停止容器
docker stop
crictl stop
删除容器
docker rm
crictl rm
查看容器详情
docker inspect
crictl inspect
attach
docker attach
crictl attach
exec
docker exec
crictl exec
logs
docker logs
crictl logs
stats
docker stats
crictl stats
POD相关功能
docker
containerd
显示POD列表
无
crictl pods
查看POD详情
无
crictl inspectp
运行POD
无
crictl runp
停止POD
无
crictl stopp
配置参数对比 :
对比项
docker
containerd
存储路径
docker作为k8s容器运行时的情况下,容器日志的落盘由docker来完成, 保存在类似/var/lib/docker/containers/CONTAINERID
目录下。kubelet会在/var/log/pods和/var/log/containers
下面建立软链接,指向/var/lib/docker/containers/CONTAINERID
目录下的容器日志文件
containerd作为k8s容器运行时的情况下, 容器日志的落盘由kubelet来完成,保存到/var/log/pods/$CONTAINER_NAME
目录下,同时在/var/log/containers目录下创建软链接,指向日志文件
配置参数
在docker配置文件中指定:"log-driver": "json-file", "log-opts": {"max-size": "100m","max-file": "5"}
方法一:在kubelet参数中指定: --container-log-max-files=5 --container-log-max-size="100Mi"
方法二:在KubeletConfiguration中指定:"containerLogMaxSize": "100Mi","containerLogMaxFiles": 5
容器日志保存到数据盘
把数据盘挂载到"data-root"
(缺省是/var/lib/docker
)即可
创建一个软链接/var/log/pods
指向数据盘挂载点下的某个目录在TKE中选择”将容器和镜像存储在数据盘”会自动创建软链接/var/log/pods
stream server 区别: 执行 kubectl exec/logs 等命令需要在apiserver跟容器运行时之间建立流转发通道。 Docker API本身提供stream服务,kubelet 内部的 docker-shim 会通过 docker API做流转发, containerd 的stream服务需要单独配置。1 2 3 4 [plugins.cri] stream_server_address = "127.0.0.1" stream_server_port = "0" enable_tls_streaming = false
Tips: 从k8s1.11引入了 kubelet stream proxy
(https://github.com/kubernetes/kubernetes/pull/64006)
, 从而使得containerd stream server
只需要监听本地地址即可。
对比项
docker
containerd
谁负责调用CNI
kubelet内部的docker-shim
containerd内置的cri-plugin(containerd 1.1以后)
如何配置CNI
kubelet参数 –cni-bin-dir 和 –cni-conf-dir
containerd配置文件(toml):plugins.cri.cni bin_dir = “/opt/cni/bin” conf_dir = “/etc/cni/net.d”
名称空间(Namespace): Docker没有namespace名称空间,而Containerd是支持namespaces的,对于上层编排系统的支持,主要区分了3个命名空间分别是k8s.io、moby和default,以上我们用crictl操作的均在k8s.io命名空间完成如查看镜像列表就需要加上-n参数 .
weiyigeek.top-Containerd Namespace