建议收藏!史上最全的容器运行时介绍,没有之一!

建议收藏!史上最全的容器运行时介绍,没有之一!

建议收藏!史上最全的容器运行时介绍,没有之一!

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

01 需求简介

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

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

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

  • 下载镜像

  • 将镜像解压成一个bundle,即将各层文件平铺到一个单一的文件系统中

  • 运行容器

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

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

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

建议收藏!史上最全的容器运行时介绍,没有之一!

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

02 低级容器运行时

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

低级运行时 demo

通过以 root 方式使用 Linux cgcreate、cgset、cgexec、chroot 和 unshare 命令来实现简单容器。
首先,以 busybox 容器镜像作为基础,设置一个根文件系统。然后,创建一个临时目录,并将 busybox 解压到该目录中。
$ CID=$(docker create busybox)$ ROOTFS=$(mktemp -d)$ docker export $CID | tar -xf - -C $ROOTFS
紧接着创建 uuid,并对内存和 CPU 设置限制。内存限制是以字节为单位设置的。在这里,将内存限制设置为100MB。
$ 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。
$ cgset -r cpu.cfs_period_us=1000000 $UUID$ cgset -r cpu.cfs_quota_us=2000000 $UUID

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

$ 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 和临时目录。

低级运行时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。

$ mkdir rootfs$ docker export $(docker create busybox) | tar -xf - -C rootfs

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

$ runc spec

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

$ 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的目录下运行命令。

$ 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。

03 高级容器运行时

高级运行时负责容器镜像的传输和管理,解压镜像,并传递给低级运行时来运行容器。通常情况下,高级运行时提供一个守护程序和一个API,远程应用程序可以使用它来运行容器并监控它们,它们位于低层运行时或其他高级运行时之上。
高层运行时也会提供一些看似很低级的功能。例如,管理网络命名空间,并允许容器加入另一个容器的网络命名空间。
这里有一个类似逻辑分层图,可以帮助理解这些组件是如何结合在一起工作的。

建议收藏!史上最全的容器运行时介绍,没有之一!

高级运行时代表

  • Docker

Docker 是最早的开源容器运行时之一。它是由平台即服务的公司 dotCloud 开发的,用于在容器中运行用户的应用。
Docker 是一个容器运行时,包含了构建、打包、共享和运行容器。Docker 基于 C/S 架构实现,最初是由一个守护程序 dockerd 和 docker 客户端应用程序组成。守护程序提供了构建容器、管理镜像和运行容器的大部分逻辑,以及一些 API。命令行客户端可以用来发送命令和从守护进程中获取信息。
它是第一个流行开来的运行时间,毫不过分的说,Docker 对容器的推广做出了巨大的贡献。
Docker 最初实现了高级和低级的运行时功能,但这些功能后来被分解成单独的项目,如runc 和 containerd,以前 Docker 的架构如下图所示,现有架构中,docker-containerd 变成了 containerddocker-runc 变成了 runc

建议收藏!史上最全的容器运行时介绍,没有之一!

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

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

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

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

$ sudo ctr images pull docker.io/library/redis:latest

列出所有的镜像:

$ sudo ctr images list

运行容器:

$ sudo ctr container create docker.io/library/redis:latest redis

列出运行容器:

$ sudo ctr container list

停止容器:

$ sudo ctr container delete redis

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

  • rkt(已废弃)

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

04 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(篇幅有限,进行了一些简化工作)。RunPodSandboxCreateContainer RPCs 在其响应中返回 ID,在后续请求中使用。

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 的相关实现。

cat <<EOF | sudo tee /etc/crictl.yamlruntime-endpoint: unix:///run/containerd/containerd.sockEOF

或者通过命令行指定:

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

来源:本文经作者授权转载,点击查看原文。

还不过瘾?GOPS 全球运维大会 2022 · 深圳站,想了解云原生时代下最新运维趋势/运维转型/自我提升,GOPS全球运维大会,你不可错过!

披荆斩棘的容器云演进之路/Serverless 在国信证券的探索实践/百亿日志秒级分析,PB级日志服务平台架构设计~精彩转不停,猛戳下方二维码了解详情!

建议收藏!史上最全的容器运行时介绍,没有之一!
建议收藏!史上最全的容器运行时介绍,没有之一!
建议收藏!史上最全的容器运行时介绍,没有之一!

<<  滑动查看下一张图片  >>

近期好文:

无监控,不运维!Prometheus 在线服务的监控实操指南

“高效运维”公众号诚邀广大技术人员投稿

投稿邮箱:jiachen@greatops.net,或添加联系人微信:greatops1118。
建议收藏!史上最全的容器运行时介绍,没有之一!
点个“在看”,一年不宕机

文章来源于网络,投诉文章请联系:zhangyunfei@anqiangkj.com

主题测试文章,只做测试使用。发布者:2915,转转请注明出处:https://community.anqiangkj.com/archives/16930

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022年5月30日 下午7:56
下一篇 2022年5月31日 下午9:08

相关推荐