前言
在涉及到容器时你经常听到一个术语:容器运行时。每个人对这个术语都有不同的理解,即使在容器社区也是这种情况。这篇文章是这个系列文章的第一篇,它们分别是:
- 容器运行时介绍:为什么它们令人如此困惑?
- 深入
Low-Level
运行时 - 深入
High-Level
运行时 Kubernetes运行时和CRI
这篇文章将会介绍容器运行时是什么和为什么会让人如此困惑,然后深入介容器运行时不同的类型,它们是如何工作的以及它们之间的不同点。
通常来说,一个计算机程序员可能将一个程序的运行周期理解为"运行时",或者是支持其运行的语言的特定实现,其中的一个例子是`Java HotSpot
运行时,后者更接近"容器运行时"的概念。容器运行时负责一个容器运行的所有部分,而容器实际上并未在运行程序本身。正如我们将从本系列文章中看到的那样,运行时实现了各个级别的功能特性,但实际上运行一个容器就是调用容器运行时所需的全部。
为什么容器让人如此困惑?
Docker于2013年发布,它端到端地解决了开发者运行容器的诸多问题:
- 容器镜像格式
- 构建镜像的方法(
Dcokerfile/docker build
) - 管理容器镜像的方法(
Docker images, docker rm, etc
) - 管理容器实例的方法(
docker ps, docker rm, etc
) - 分享容器镜像的方法(
docker push/pull
) - 运行容器的方法(
docker run
)
在那时,Docker是一个一体化的系统,然而事实上这些功能特性彼此之间并没有相互依赖,它们中的每一个都可以在一个更小更专注的工具实现,每个工具都可以在一个通用的标准(容器标准)下一起使用。因为这个缘故,Docker,Google,CoreOS
以及其他的供应商一起创立了开放容器规范(OCI
),同时他们拆分了容器运行这部分的代码将其作为一个工具或lib库称之为run c
,然后捐赠给OCI
作为OCI
运行标准的参考范例。
最初,这使Docker对OCI
的捐赠感到困惑,他们捐赠的是一个标准而不是运行容器的方法,其中并没有包含镜像格式或者镜像仓库拉取推送规则,当你运行一个Docker容器的时候,它们实际上经过了以下的步骤:
- 下载镜像
- 将镜像文件解开为
bundle
文件,将一个文件系统拆分成多层 - 从
bundle
文件运行容器
Docker标准化的仅仅是第三步。在此之前,每个人都认为容器运行时支持Docker支持的所有功能。最终,Docker方面澄清:原始OCI规范指出,只有“运行容器”的部分组成了runtime
。这种“概念失联”一直持续到今天,并使“容器运行时”成为一个令人困惑的话题。希望我能证明双方都不是完全错误的,并且在本博文中将广泛使用该术语。
Low-Level和High-Level容器运行时
当人们想到容器运行时时,可能会想到许多示例。runc,lxc,lmctfy,Docker(containerd),rkt,cri-o
。这些中的每一个都是针对不同的情况而构建的,并实现了不同的功能。有些容器(例如containerd
和cri-o
容器)实际上使用runc来运行容器,在runc
之上实现了镜像管理和API接口。与runc
的Low-Level
特性相比,您可以将这些功能(包括图像传输,图像管理,图像解压缩和API)视为High-Level
特性。考虑到这点,可以看到容器运行时空间相当复杂,每个运行时涵盖了从Low-Level
到High-Level
的不同部分,这里有一张非常直观的图:
因此,从实际出发,通常只专注于正在运行的容器的runtime通常称为“Low-Level
容器运行时”,支持更多高级功能(如镜像管理和gRPC / Web API
)的运行时通常称为“High-Level
容器运行时”,“High-Level
容器运行时”或通常仅称为“容器运行时”,我将它们称为“高级容器运行时”。值得注意的是,Low-Level
容器运行时和High-Level
容器运行时是解决不同问题的、从根本上不同的事物。容器是通过Linux nanespace
和Cgroups
实现的,Namespace
能让你为每个容器提供虚拟化系统资源,像是文件系统和网络,Cgroups
提供了限制每个容器所能使用的资源的如内存和CPU使用量的方法。在最低级别的运行时中,容器运行时负责为容器建立namespaces
和cgroups
,然后在其中运行命令,Low-Level
容器运行时支持在容器中使用这些操作系统特性。
通常情况下,开发人员想要运行一个容器不仅仅需要Low-Level
容器运行时提供的这些特性,同时也需要与镜像格式、镜像管理和共享镜像相关的API接口和特性,而这些特性一般由High-Level
容器运行时提供。就日常使用来说,Low-Level
容器运行时提供的这些特性可能满足不了日常所需,因为这个缘故,唯一会使用Low-Level
容器运行时的人是那些实现High-Level
容器运行时以及容器工具的开发人员。那些实现Low-Level
容器运行时的开发者会说High-Level
容器运行时比如containerd
和cri-o
不像真正的容器运行时,因为从他们的角度来看,他们将容器运行的实现外包给了runc
。但是从用户的角度来看,它们只是提供容器功能的单个组件,可以被另一个的实现替换,因此从这个角度将其称为runtime
仍然是有意义的。即使containerd
和cri-o
都使用runc
,但是它们是截然不同的项目,支持的特性也是非常不同的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。