[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) 并提供了两种规范:
- 1) 镜像规范 (https://github.com/opencontainers/image-spec) 镜像规范定义了OCI镜像的标准,high level 运行时将会下载一个OCI 镜像,并把它解压成OCI 运行时文件系统包(filesystem bundle),例如 制定镜像格式、操作等
- 2) 运行时规范 (https://github.com/opencontainers/runtime-spec): 描述了如何从OCI 运行时文件系统包运行容器程序,并且定义它的配置、运行环境和生命周期,如何为新容器设置命名空间(namepsaces)和控制组(cgroups),以及挂载根文件系统等等操作,都是在这里定义的。它的一个参考实现是runC(
低层级运行时- Low-level Runtime)除它以外也有很多其他的运行时遵循OCI标准,例如kata-runtime。
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.不同运行时工具间关系
(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接口客户端工具
ctr: 是containerd本身的 CLI (https://github.com/containerd/containerd/tree/master/cmd/ctr)crictl: 是Kubernetes社区定义的专门 CLI 工具 (https://github.com/kubernetes-sigs/cri-tools)
3.架构简述
CRI 的架构
描述: CRI 插件是 Kubernetes 容器运行时接口 (CRI) 的实现, Containerd 与 Kubelet 在同一节点上运行。
containerd 中的插件处理来自 Kubelet 的所有 CRI 服务请求,并使用容器内部来管理容器和容器映像,该插件使用 containerd 来管理整个容器生命周期和所有容器映像,并通过CNI(另一个CNCF项目)管理pod网络,如下图所示
WeiyiGeek.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.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.架构图简化
containerd-shim 描述: 主要是用于剥离 containerd 守护进程与容器进程。目前已有 shim v1 和 shim v2 两个版本;它是containerd中的一个组件,其通过 shim 调用 runc 的包函数来启动容器。直白的说引入shim是允许runc在创建和运行容器之后退出, 并将shim作为容器的父进程, 而不是containerd作为父进程。
代码语言:javascript复制# - 当我们执行 pstree 命令时可以看到如下的进程关系containerd-shim是独立于containerd进程运行的。
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.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.容器运行时调用层级
- 如下图所示,我们对containerd和cri-o进行了一组性能测试,包括创建、启动、停止和删除容器,得出它们所耗的时间。 Tips : containerd在各个方面都表现良好,除了启动容器这项。从总用时来看 containerd的用时还是要比cri-o要短的。

WeiyiGeek.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.后端存储文件系统性能比较
我们在同一个节点上同时起10,30,50和80的Pod,然后再同时删除,去比较迁移前后创建和删除的用时。从下图可知,containerd用时明显优于Docker。

WeiyiGeek.创建与删除Pod的用时比较
Docker和containerd除了上述常用命令有些区别外,在容器日志及相关参数配置方面也存在一些差异

WeiyiGeek.日志与cni参数配置
0x01 安装配置
1.Ubuntu安装Containerd.io流程
安装环境: 已进行安全基线加固以及系统优化。
代码语言:javascript复制# - OS
$ lsb_release -a
# Description: Ubuntu 20.04.2 LTS focal
$ uname -a
# Linux containerd 5.4.0-78-generic #87-Ubuntu SMP Fri Jun 18 16:29:09 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
# - Container : 1.4.6-1
$ apt-cache madison containerd.io | cut -d '|' -f 2 | sed 's/^[ t]*//g'
# 1.4.6-1
# 1.4.4-1
# 1.4.3-2
# 1.4.3-1
# 1.3.9-1
# 1.3.7-1
# 1.2.13-2安装流程
Step 1.参考Kubernetes容器运行时接口进行安装前的基础环境准备.
代码语言:javascript复制# 安装和配置先决条件:
# 模块加载
cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
# Setup required sysctl params, these persist across reboots.
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
# Apply sysctl params without reboot
sudo sysctl --systemStep 2.在Ubuntu中安装containerd.io,它来自官方Docker存储库的软件包我们可以参考安装Install Docker Engine(https://docs.docker.com/engine/install/ubuntu/)中的安装命令。
# - 卸载原有 Docker 以及 containerd
sudo apt-get remove docker docker-engine docker.io containerd runc
# - 更新apt程序包索引并安装程序包,以允许apt通过HTTPS使用存储库
sudo apt-get update
sudo apt-get install
apt-transport-https
ca-certificates
curl
gnupg
lsb-release
# - 添加Docker的官方GPG密钥:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# - 使用以下命令设置稳定存储库。要添加nightly或test存储库,请在下面的命令中的单词stable后面添加单词nightly或test(或两者)。
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/null
# - 更新apt包索引,安装最新版本的containerd或进入下一步安装特定版本:
sudo apt-get update
# 安装前可查看containerd.io可用的版本: apt-cache madison containerd.io && apt install containerd.io=1.4.6-1
apt install containerd.io=1.4.6-1
# - 查看安装Containerd的版本
$ ctr --version
# ctr containerd.io 1.4.6Step 3.初始化Containerd.io基础配置
代码语言:javascript复制# - 初始化配置
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
# - 替换默认的 sandbox_image = "k8s.gcr.io/pause:3.2" 镜像因为GFW的原因导致无法访问我们可以替换为阿里云镜像源。
sed -ir 's#sandbox_image = Sw .*$#sandbox_image = "registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0"#g' /etc/containerd/config.toml
sed -ir "s#https://registry-1.docker.io#https://xlx9erfu.mirror.aliyuncs.com#g" /etc/containerd/config.toml
# - 应用配置并重新运行containerd服务并查看服务状态
systemctl daemon-reload && systemctl restart containerd
systemctl status containerd.service
# ● containerd.service - containerd container runtime
# Loaded: loaded (/lib/systemd/system/containerd.service; enabled; vendor preset: enabled)
# Active: active (running) since Sun 2021-06-27 19:05:48 CST; 8s ago
# Docs: https://containerd.io
# Process: 3088 ExecStartPre=/sbin/modprobe overlay (code=exited, status=0/SUCCESS)
# Main PID: 3097 (containerd)
# Tasks: 15
# Memory: 22.6M
# CGroup: /system.slice/containerd.service
# └─3097 /usr/bin/containerdTips : 查看默认的配置文件,其中root与state 分别表示 Containerd 有两个不同的存储路径,一个用来保存持久化数据,一个用来保存运行时状态。
代码语言:javascript复制$ 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 本身不存储任何数据,它的所有功能都来自于已加载的插件,真是太机智了。
代码语言:javascript复制$ 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、挂载点、运行时状态以及不需要持久化保存的插件数据。
代码语言:javascript复制


