前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Containerd深度剖析-runtime篇

Containerd深度剖析-runtime篇

作者头像
zouyee
发布于 2022-11-07 09:48:44
发布于 2022-11-07 09:48:44
1.4K00
代码可运行
举报
文章被收录于专栏:Kubernetes GOKubernetes GO
运行总次数:0
代码可运行

虽然容器领域的创业随着CoreOS、Docker的卖身,而逐渐归于平寂,但随着Rust语言的兴起,Firecracker、youki项目在容器领域泛起涟漪,对于云原生从业者来说,面试等场景中或多或少都会谈论到容器一些的历史与技术背景。

文|ianlewis

编辑|zouyee

技术深度|简单

需求简介

注: Container runtime统称为容器运行时

Docker时代,关于容器运行时术语的定义是非常明确的,其为运行和管理容器的软件。但随着Docker涵盖的内容日益增多,以及多种容器编排工具的引入,该定义变得日益模糊了。

当你运行一个Docker容器时,一般的步骤是:

  • 下载镜像
  • 将镜像解压成一个bundle,即将各层文件平铺到一个单一的文件系统中。
  • 运行容器

最初的规范规定,只有运行容器的部分定义为容器运行时,但一般用户,将上述三个步骤都默认为容器运行时所必须的能力,从而让容器运行时的定义成为一个令人困惑的话题。

当人们想到容器运行时,可能会想到一连串的相关概念;runc、runv、lxc、lmctfy、Docker(containerd)、rkt、cri-o。每一个都是基于不同的场景而实现的,均实现了不同的功能。如containerd和cri-o,实际均可使用runc来运行容器,但其实现了如镜像管理、容器API等功能,可以将这些看作是比runc具备的更高级的功能。

可以发现,容器运行时是相当复杂的。每个运行时都涵盖了从低级到高级的不同部分,如下图所示。

根据功能范围划分,将其分为低级容器运行时 (Low level Container Runtime)和高级容器运行时 (High level Container Runtime),其中只关注容器的本身运行通常称为低级容器运行时(Low level Container Runtime)。支持更多高级功能的运行时,如镜像管理及一些gRPC/Web APIs,通常被称为 高级容器运行时 (High level Container Runtime)。需要注意的是,低级运行时和高级运行时有本质区别,各自解决的问题也不同。

低级容器运行时

低级运行时的功能有限,通常执行运行容器的低级任务。大多数开发者日常工作中不会使用到。其一般指按照 OCI 规范、能够接收可运行roofs文件系统和配置文件并运行隔离进程的实现。这种运行时只负责将进程运行在相对隔离的资源空间里,不提供存储实现和网络实现。但是其他实现可以在系统中预设好相关资源,低级容器运行时可通过 config.json 声明加载对应资源。低级运行时的特点是底层、轻量,限制也很一目了然:

  • 只认识 rootfs 和 config.json,没有其他镜像能力
  • 不提供网络实现
  • 不提供持久实现
  • 无法跨平台等

低级运行时demo

通过以root方式使用Linux cgcreate、cgset、cgexec、chroot和unshare命令来实现简单容器。

首先,以busybox容器镜像作为基础,设置一个根文件系统。然后,创建一个临时目录,并将busybox解压到该目录中。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ CID=$(docker create busybox)
$ ROOTFS=$(mktemp -d)
$ docker export $CID | tar -xf - -C $ROOTFS

紧接着创建uuid,并对内存和CPU设置限制。内存限制是以字节为单位设置的。在这里,将内存限制设置为100MB。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ UUID=$(uuidgen)
$ cgcreate -g cpu,memory:$UUID
$ cgset -r memory.limit_in_bytes=100000000 $UUID
$ cgset -r cpu.shares=512 $UUID

例如,如果我们想把我们的容器限制在两个cpu core上,可以设定一秒钟的周期和两秒钟的配额(1s=1,000,000us),这将允许进程在一秒钟的时间内使用两个cpu core。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ cgset -r cpu.cfs_period_us=1000000 $UUID
$ cgset -r cpu.cfs_quota_us=2000000 $UUID

接下来在容器中执行命令。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ cgexec -g cpu,memory:$UUID \
>     unshare -uinpUrf --mount-proc \
>     sh -c "/bin/hostname $UUID && chroot $ROOTFS /bin/sh"
/ # echo "Hello from in a container"
Hello from in a container
/ # exit

最后,删除前面创建的cgroup和临时目录。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ cgdelete -r -g cpu,memory:$UUID
$ rm -r $ROOTFS

低级运行时demo

为了更好地理解低级容器运行时,以下列举了几个低级运行时代表,各自实现了不同的功能。

runC

runC是目前使用最广泛的容器运行时。它最初是集成在Docker的内部,后来作为一个单独的工具,并以公共库的方式提取出来。

在2015 年,在 Linux 基金会的支持下有了 Open Container Initiative (OCI)(就是负责制定容器标准的组织),Docker 将自己容器格式和运行时 runC 捐给了 OCI。OCI 在此基础上制定了 2 个标准:运行时标准 Runtime Specification (runtime-spec) 和 镜像标准 Image Specification (image-spec) ,下面通过示例,简要介绍一下 runC。

首先创建根文件系统。这里我们将再次使用busybox。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ mkdir rootfs
$ docker export $(docker create busybox) | tar -xf - -C rootfs

接下来创建一个config.json文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ runc spec

这个命令为容器创建一个模板config.json。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ cat config.json
{
        "ociVersion": "1.0.2",
        "process": {
                "terminal": true,
                "user": {
                        "uid": 0,
                        "gid": 0
                },
                "args": [
                        "sh"
                ],
                "env": [
                        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                        "TERM=xterm"
                ],
                "cwd": "/",
                "capabilities": {
...

默认情况下,它在根文件系统位于./rootfs的目录下运行命令。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ sudo runc run mycontainerid
/ # echo "Hello from in a container"
Hello from in a container

rkt(已废弃)

rkt是一个同时具有低级和高级功能的运行时。例如,很像Docker,rkt允许你构建容器镜像,获取和管理本地存储库中的容器镜像,并通过一个命令运行它们。

runV

runv 是 OCF 基于管理程序的(Hypervisor-based )运行时 Runtime.runV 兼容 OCF。作为虚拟容器运行时引擎的runV已被淘汰。 runV团队与英特尔一起在OpenInfra Foundation中创建了Kata Containers项目

youki

Rust是时下最流行的编程语言,而容器开发也是一个时兴的应用领域。将两者结合使用Rust来做容器开发是一个值得尝鲜的体验。youki是使用Rust的实现OCI运行时规范,类似于runc。

高级容器运行时

高级运行时负责容器镜像的传输和管理,解压镜像,并传递给低级运行时来运行容器。通常情况下,高级运行时提供一个守护程序和一个API,远程应用程序可以使用它来运行容器并监控它们,它们位于低层运行时或其他高级运行时之上。

高层运行时也会提供一些看似很低级的功能。例如,管理网络命名空间,并允许容器加入另一个容器的网络命名空间。

这里有一个类似逻辑分层图,可以帮助理解这些组件是如何结合在一起工作的。

高级运行时代表

Docker

Docker是最早的开源容器运行时之一。它是由平台即服务的公司dotCloud开发的,用于在容器中运行用户的应用。

Docker是一个容器运行时,包含了构建、打包、共享和运行容器。Docker基于C/S架构实现,最初是由一个守护程序dockerd和docker客户端应用程序组成。守护程序提供了构建容器、管理镜像和运行容器的大部分逻辑,以及一些API。命令行客户端可以用来发送命令和从守护进程中获取信息。

它是第一个流行开来的运行时间,毫不过分的说,Docker对容器的推广做出了巨大的贡献。

Docker最初实现了高级和低级的运行时功能,但这些功能后来被分解成单独的项目,如runc和containerd,以前Docker的架构如下图所示,现有架构中,docker-containerd变成了containerd,docker-runc变成了runc。

dockerd提供了诸如构建镜像的功能,而dockerd使用containerd来提供诸如镜像管理和运行容器的功能。例如,Docker的构建步骤实际上只是一些逻辑,它解释Docker文件,使用containerd在容器中运行必要的命令,并将产生的容器文件系统保存为一个镜像。

Containerd

containerd是从Docker中分离出来的高级运行时。containerd实现了下载镜像、管理镜像和运行镜像中的容器。当需要运行一个容器时,它会将镜像解压到一个OCI运行时bundle中,并向runc发送init以运行它。

Containerd还提供了API,可以用来与它交互。containerd的命令行客户端是ctr和nerdctl。

可以通过ctr拉取一个容器镜像。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ sudo ctr images pull docker.io/library/redis:latest

列出所有的镜像:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ sudo ctr images list

运行容器:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ sudo ctr container create docker.io/library/redis:latest redis

列出运行容器:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ sudo ctr container list

停止容器:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ sudo ctr container delete redis

这些命令类似于用户与Docker的互动方式。

rkt(已废弃)

rkt是一个同时具有低级和高级功能的运行时。例如,很像Docker,rkt允许你构建容器镜像,获取和管理本地存储库中的容器镜像,并通过一个命令运行它们。

Kubernetes CRI

CRI在Kubernetes 1.5中引入,作为kubelet和容器运行时之间的桥梁。社区希望Kubernetes集成的高级容器运行时实现CRI。该运行时处理镜像的管理,支持Kubernetes pods,并管理容器,因此根据高级运行时的定义,支持CRI的运行时必须是一个高级运行时。低级别的运行时并不具备上述功能。

为了进一步了解CRI,可以看看整个Kubernetes架构。kubelet代表工作节点,位于Kubernetes集群的每个节点上,kubelet负责管理其节点的工作负载。当需要运行工作负载时,kubelet通过CRI与运行时进行通信。由此可以看出,CRI只是一个抽象层,允许切换不同的容器运行时。

CRI规范

CRI定义了gRPC API,该规范定义在Kubernetes仓库中cri-api目录中。CRI定义了几个远程程序调用(RPC)和消息类型。这些RPC用于管理工作负载等内容,如 "拉取镜像"(ImageService.PullImage)、"创建pod"(RuntimeService.RunPodSandbox)、"创建容器"(RuntimeService.CreateContainer)、"启动容器"(RuntimeService.StartContainer)、"停止容器"(RuntimeService.StopContainer)等操作。

例如,通过CRI启动一个新的Pod(篇幅有限,进行了一些简化工作)。RunPodSandbox和CreateContainer RPCs在其响应中返回ID,在后续请求中使用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ImageService.PullImage({image: "image1"})
ImageService.PullImage({image: "image2"})
podID = RuntimeService.RunPodSandbox({name: "mypod"})
id1 = RuntimeService.CreateContainer({
    pod: podID,
    name: "container1",
    image: "image1",
})
id2 = RuntimeService.CreateContainer({
    pod: podID,
    name: "container2",
    image: "image2",
})
RuntimeService.StartContainer({id: id1})
RuntimeService.StartContainer({id: id2})

可以直接使用crictl工具与CRI运行时交互,可以用它来调试和测试CRI的相关实现。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cat <<EOF | sudo tee /etc/crictl.yaml
runtime-endpoint: unix:///run/containerd/containerd.sock
EOF

或者通过命令行指定:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
crictl --runtime-endpoint unix:///run/containerd/containerd.sock …

关于crictl的使用参见官网。

支持CRI的运行时

Containerd

containerd应该是目前最流行的CRI运行时。它以插件的方式实现CRI,默认是启用的。它默认在unix套接字上监听消息。

从1.2版本开始,它通过 runtime handler来支持多种低级运行时。运行时处理程序是通过CRI中的字段传递,根据该运行时处理程序,containerd运行shim的应用程序来启动容器。这可以用来运行 runc及其他的低级运行时的容器,如 gVisor、Kata Containers等。在Kubernetes API中通过RuntimeClass进行运行时配置。

下图是Containerd的发展史。

Docker

docker-shim是K8s社区第一个被开发的,作为kubelet和Docker之间的shim。随着Docker将其许多功能分解到containerd中,现在通过containerd支持CRI。当现代版本的Docker被安装时,containerd也一起被安装,CRI直接与containerd对话,随着docker-shim正式废弃,是时候考虑相关迁移的工作了,K8s在这方面做了大量的工作,具体可参看官方文档。

CRI-O

cri-o是一个轻量级的CRI运行时,它支持OCI,并提供镜像的管理、容器进程管理、监控日志及资源隔离等工作。

cri-o的通信地址默认是在/var/run/crio/crio.sock。

下图为CRI插件的演变史。

由于笔者时间、视野、认知有限,本文难免出现错误、疏漏等问题,期待各位读者朋友、业界专家指正交流。

参考文献

1.https://blog.mobyproject.org/where-are-containerds-graph-drivers-145fc9b7255

2.https://insujang.github.io/2019-10-31/container-runtime/

3.https://github.com/cri-o/cri-o

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-05-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DCOS 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Rc-form: 消失的“Ta”
那是一个艳阳高照的早上,临近中休时间,小 H 正准备动身去吃午餐,突然,钉钉弹出了一条新消息:(登登登~)“您有一个新的 bug:表单点击提交按钮没反应”。自信的小 H 心想:这期的需求我不就给表单多加了几个字段嘛,怎么会影响到表单的提交功能呢?应该是提错 bug 了吧。于是,小 H 按照 bug 的描述复现起了场景:
政采云前端团队
2023/09/01
2440
Rc-form: 消失的“Ta”
Error: must set key for <rc-animate> children
在使用antd-design中的select的组件时候,报了这样的一个错误: Error: must set key for <rc-animate> children <FormItem {...formItemLayout} label={item.label} key={index}> { getFieldDecorator(item.paramName, { initialValue: [], })( <Select size={confi
杭州前端工程师
2018/06/15
1.6K0
Form 表单在数栈的应用(下):深入篇
后文中所提到的 Form 表单均为 Antd 3.x 中的 Form 组件,以下简称为 Form 表单。在 Form 表单在数栈的应用(上): 校验篇 中提到,我们生在一个最好的时代,其实是别人造好轮子帮我们做了一些事情,那我们今天看一看,别人的轮子是怎么造的,我们自己能不能实现。 留心过 Antd 的同学可能有印象,Antd 是基于 react-component 组件进行了 UI 封装,文章会以 react-component/form 的代码为主。
袋鼠云数栈
2022/01/19
9070
antd-design Form,Select联合使用 placeholder 不起作用问题
Contents 1 antd-design Form,Select联合使用 placeholder 不起作用问题 1.1 起因 1.2 排查 1.3 补充: antd-design Form,Sel
w候人兮猗
2020/07/01
2.1K0
数栈技术文章分享:你居然是这样的initialValue
先说一下写着篇文章的契机,是因为回显,复杂表单的回显,让我觉得我对initialValue这个属性是有误解的。
袋鼠云数栈
2021/05/14
1K0
数栈技术文章分享:你居然是这样的initialValue
React后台管理前端系统(基于开源框架开发)起步式
这个系统的搭建背景是这样的,有一个朋友想看到现有系统中的一些,用户数据,新闻数据,只需要看到,短期不需要增删改功能,让我搭建一个简单的后台系统给他看.接到任务作为一个有四年开发经验的人来说这也太简单了吧,开始干吧,最简单的办法是直接使用开源项目https://github.com/PanJiaChen/vue-element-admin 下载下来,把不要的路由去掉,新建几个表格路由,页面直接复制过来就用了.
拿我格子衫来
2022/01/24
2K0
React后台管理前端系统(基于开源框架开发)起步式
疫情期间,写的两个场景
嗯~这种实现的方式还是和舒服的,不用自己布局,不用自己再次思考逻辑;如果你想自己捣鼓一个,那你是真的闲,还不如花点时间捣鼓其他非编程的东西。
Jimmy_is_jimmy
2020/04/01
1.1K0
疫情期间,写的两个场景
最熟悉的陌生人 rc-form
我们也许会经常使用例如 Ant Design、Element UI、Vant 等第三方组件库来快速在项目中完成页面的布局效果和简单的交互功能。
政采云前端团队
2021/07/19
1.1K0
Form 表单在数栈的应用(上): 校验篇
本文的重点为 Form 表单的校验及在数栈中的应用,偏向于应用总结与心得分享。众所周知,我们生在一个最好的时代,antd 已经帮我们把绝大多数功能封装好了,即开即用, API 详尽,但即便如此,antd 开发人员依然在当前基础上一遍又一遍地做优化和探索,所以,笔者希望通过本文不仅能带给大家业务上的小技巧,还希望能带给大家一些思路上的启发。
袋鼠云数栈
2022/01/12
2.2K1
React 16.x折腾记 - (6) 基于React 16.x+ Antd 3.x封装的一个声明式的查询组件(实用强大)
根据ctype渲染的控件有Input,Button,Select,DatePicker,Cascader,Radio
CRPER
2024/02/06
2080
React 16.x折腾记 - (6)  基于React 16.x+ Antd 3.x封装的一个声明式的查询组件(实用强大)
React 折腾记 - (6) 基于React 16.6 + Antd 3.10.7封装的一个声明式的查询组件
根据ctype渲染的控件有Input,Button,Select,DatePicker,Cascader,Radio
CRPER
2018/12/12
2.7K0
猿实战09——实现你设计的类目系统
上两个章节,猿人君教会了你如何通过设计落地实现了属性库,今天我们继续来实现系统的另一个基石地位的模块——后台类目。
山旮旯的胖子
2020/09/07
7770
猿实战09——实现你设计的类目系统
详细剖析|袋鼠云数栈前端框架Antd 3.x 升级 4.x 的踩坑之路
袋鼠云数栈从 2016 年发布第⼀个版本开始,就始终坚持着以技术为核⼼、安全为底线、提效为⽬标、中台为战略的思想,坚定不移地⾛国产化信创路线,不断推进产品功能迭代、技术创新、服务细化和性能升级。
袋鼠云数栈
2023/03/06
4.2K0
React 实现一个markdown[2]
theme: channing-cyan highlight: a11y-light
用户4793865
2023/02/03
1.2K0
Python 数据可视化,常用看这一篇就够了
如果你想要用 Python 进行数据分析,就需要在项目初期开始进行探索性的数据分析,这样方便你对数据有一定的了解。其中最直观的就是采用数据可视化技术,这样,数据不仅一目了然,而且更容易被解读。
全栈程序员站长
2022/09/07
2.1K0
React 16.8.6 升级指南(react-hooks篇)
从官方的态度可以很容易看出是十分重视hooks这个特性的,并且官方直言我们期望 Hook 能够成为人们编写 React 组件的主要方式。并且从笔者的实践过程来看hooks不仅仅是一种新玩法,更重要的意义是可以帮助开发者做减法,减少代码量,减少维护成本,甚至减少理解成本。
腾讯IVWEB团队
2020/06/28
2.8K0
React源码解析之HostComponent的更新(下)
在上篇 React源码解析之HostComponent的更新(上) 中,我们讲到了多次渲染阶段的更新,本篇我们讲第一次渲染阶段的更新
进击的小进进
2020/03/18
2.8K0
React源码解析之HostComponent的更新(下)
精读《怎么用 React Hooks 造轮子》
上周的 精读《React Hooks》 已经实现了对 React Hooks 的基本认知,也许你也看了 React Hooks 基本实现剖析(就是数组),但理解实现原理就可以用好了吗?学的是知识,而用的是技能,看别人的用法就像刷抖音一样(哇,饭还可以这样吃?),你总会有新的收获。
黄子毅
2022/03/14
2.5K0
EngineerCMS核心代码
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hotqin888/article/details/53367020
hotqin888
2018/09/11
1.4K0
一篇看懂 React Hooks
React Hooks 是 React 16.7.0-alpha 版本推出的新特性,想尝试的同学安装此版本即可。
前端迷
2019/08/05
3.8K0
推荐阅读
相关推荐
Rc-form: 消失的“Ta”
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档