Open Container Initiative (OCI) 是 Docker 在 2015 年不情愿发起的一个组织,旨在制定开放的标准化容器技术。在 OCI 中,包含了 OCI 运行时规范、容器镜像格式规范和容器注册规范,符合 OCI 标准的运行时也可以称为 OCI 运行时。 OCI 运行时接收 json 文件的容器配置,在命令行启动容器的创建、启动和停止。
kubernetes 底层生态系统现状
事实上 Kubernete 本身不具备创建容器的能力,是通过 kubelet 组件调用容器运行时 API 接口和命令来创建容器,Kubernete 与 容器运行时的关系是历史性的,也很复杂。但是随着 Kubernete 弃用 Docker ,目前主流的运行时主要是 containerd 和 CRI-O。
containerd 从 Docker 项目中分离出来,使 Docker 更加模块化。当你安装 Docker 时,它也会安装 containerd,因为 Docker 本身也是使用 containerd 的。containerd 通过内部的 cri 插件实现了 Kubernetes 容器运行时接口 (CRI) 。
CRI-O 是在 CRI 标准提出后,RedHat 构建的一个更简单、仅为 Kubernetes 所用的运行时,它实现了一个最小的 CRI 接口。
目前广泛使用的 OCI 运行时是 runC,它是 Docker 公司基于 libcontainer 的基础上封装捐献出来的,作为 OCI 的参考实现。有了 OCI 标准,Docker 无法再垄断容器技术,也涌出了一些 runC 替代方案,比如:
还有用 C 编写的轻量级 OCI 运行时 crun。(runC 是用 Go 编写的)
在上图中 OCI 运行时看起来像是容器的父进程,但实际上 OCI 运行时在容器启动后就退出了,容器的父进程由一个独立的 containerd-shim 或 conmon 进程接管。
runC VS Crun
Go 对计算的 fork/exec 模型没有很好的支持,Go 的线程模型期望程序分叉第二个进程,然后立即执行。但是,OCI 容器运行时预计会分叉容器中的第一个进程。所以,Go 可能不是完成这项任务的最佳编程语言。C 默认情况下不是多线程的,而是围绕 fork/exec 模型构建和设计的。它可以以更简洁的方式处理 OCI 运行时的这一部分。C 也是一种低得多的语言,可以很好地与 Linux 内核交互。最后,C 是轻量级的,因此与 Go 相比,它可以编译成更小的尺寸并使用更少的内存。
根据社区的测试结果,不管是用时还是内存,Crun 都比 runC 好,感兴趣的读者可以去看看。 https://github.com/containers/crun
bucketbench 是一个符合 CRI 标准的容器运行时基准测试工具。 这里作者使用 bucketbench 来测试实际的差距。
测试环境
服务器配置 | 4核8G |
操作系统 | Ubuntu 20.04.3 LTS |
容器运行时 | crio 1.22.0 |
OCI 运行时 | runc 1.0.2 、runc 0.20.1 |
安装 bucketbench
# 需要安装 golang
git clone https://github.com/estesp/bucketbench
make binary
这里测试参数很简单,5 个线程 ,循环 2 次,执行 run/stop/delete 操作
cat crio.yaml
---
name: CRICrio
image: docker.io/polinux/stress:latest
command: stress --cpu 1 --io 1 --vm 1 --vm-bytes 128M --timeout 10s
detached: true
drivers:
- type: CRI
threads: 5
iterations: 2
clientpath: /var/run/crio/crio.sock
commands:
- run
- stop
- delete
测试
# 确认 OCI 运行时
crio-status config | egrep "default_runtime|runtime_path"
time ./bucketbench --log-level=debug run -b crio.yaml
runc 的结果
切换 crun,重启 cri-o
cat /etc/crio/crio.conf.d/10-crun.conf
[crio.runtime]
default_runtime = "crun"
[crio.runtime.runtimes.crun]
allowed_annotations = [
"io.containers.trace-syscall",
]
执行
time ./bucketbench --log-level=debug run -b crio.yaml
crun 结果
从对比结果看,crun 操作时 ,crun 平均值明显好于 runC,stop/delete 其实总体也相差不大。
测试的方式方法仅供参考,有环境的读者的可以去测试验证下。crun 的详细介绍文档见
https://www.redhat.com/sysadmin/introduction-crun