1、导语

容器技术火起来了以后,Docker的容器镜像和容器运行时已然成为行业的标准。此后,为了推进容器生态的健康发展。在Linux基金会的主导下,Docker和各大云厂商Google, Amazon, CloudFoundary, Microsoft积极响应于2015年成立了 “Open Container Initiative”,旨在主导容器的生态发展方向,促进容器生态的健康发展。本文主要介绍容器底层的运行标准OCI的背景和主要内容,最后通过用runc构建容器的示例带你了解容器背后不一样的故事。

2、背景

2013年,Docker公司(前身为dotCloud)开源了Docker项目,为我们提供了一种更为轻量、灵活的“计算、网络、存储”资源虚拟化和管理的解决方案。最初的版本主要关注容器的打包、分发和运行,使用了libcontainer作为底层容器引擎。这一创新迅速引起了业界的关注和火热反响,推动了容器技术的迅速发展。 2014年,Docker开始引起广泛的关注和采用,许多开发者和企业开始意识到Docker的潜力,将其应用于应用程序的开发、测试和部署等方面;同时,Docker公司发布了Docker Hub,这是一个用于存储和分享Docker镜像的中央仓库,为用户提供了方便的镜像管理和共享平台。另外,2014年更是容器技术发展的一个爆发点,各种容器编排工具也逐步开始发力。值得一提的是Google发布了Kubernetes的第一个Release版本,现已成长为容器编排领域的领导者。 总之,在2013-2014年,Docker技术开始大火,Docker的容器镜像和容器运行时成为行业的标准。在这期间,Docker作为容器届的大明星架子很大,对各大佬(Linux基金会、谷歌、微软等)提出的合作邀请充耳不闻,态度强硬,力图独自主导容器生态的发展。加上Docker在Runtime的向下兼容性的问题,社区口碑较差。此时,各大佬就纷纷表示要另起炉灶、自已干,其中比较有代表性的就是Google声称要fork一个分支自己干。 不过,Linux基金会最后还是拉着前边提的这些大佬向Docker施压,最终Docker屈服,并于2015年6月在 Docker 大会DockerCon上推出容器标准,随后和亚马逊、谷歌和VMware等公司成立了OCI(Open Container Initiative)。OCI成立之初,Docker 公司为其捐赠了容器镜像格式和运行时的草案及相应的实现代码。原来属于Docker 的 libcontainer 项目被捐赠给OCI,成为独立的容器运行时项目 runc。 注意 1:为什么Docker屈服与Linux基金会? 我们知道Docker的容器运行时解决方案采用的两个核心技术:Namespace(资源隔离)和Cgroup(资源管理),并不是Docker实现的。这两项技术其实在Docker之前早已进入Linux内核。换种说法就是Docker的容器解决方案离不开Linux内核的支持。这就是说行业的各个大佬如果自己想搞,都可以利用这两项技术自己做一套类似于Docker的容器解决方案。说的难听点,Docker屈服于Linux基金会是因为Docker技术依赖于Linux内核;说的好听点,Docker选择屈服于Linux基金会并捐赠OCI的目的是为了推动容器技术的开放标准化、促进合作和共享,并构建一个更广泛参与的容器生态系统。这有助于加速容器技术的发展,推动行业的创新和进步。 注意 2:libcontainer是docker捐给oci之后立马改名为runc的吗? libcontainer并没有立即在被Docker捐赠给OCI(Open Container Initiative)后改名为runc。libcontainer是Docker最初使用的容器引擎,用于实现容器的创建、管理和运行。它是Docker在容器运行时方面的核心组件之一。在2015年时,Docker将libcontainer捐赠给了OCI,成为OCI Runtime Specification的基础。然后,在OCI的基础上,OCI Runtime Specification进一步发展演进,并形成了一个名为”runtime-spec”的项目。后来,为了更好地推进容器运行时的标准化和互操作性,OCI runtime-spec项目与Open Container Initiative的其他相关项目合并,形成了OCI Runtime Bundle规范,并将容器运行时的核心组件命名为”runc”。

3、OCI(Open Container Initiative 开放容器协议)介绍

OCI(Open Container Initiative)是一个开放容器倡议组织,成立于 2015 年的 OCI 是Linux基金会旗下的合作项目,旨在推动容器运行时容器镜像格式的开放标准化。该倡议组织的目标是创建一套通用的容器标准,以确保不同容器运行时和工具之间的互操作性和可移植性。 OCI的核心成果是一系列规范和标准,这些规范定义了容器运行时和镜像格式的规范化接口和结构。这些规范包括:
  1. Runtime Specification(运行时规范):定义了容器的生命周期管理,包括创建、启动、停止和销毁容器等操作。最常见的容器运行时实现是OCI Runtime(也称为runc)。
  2. Image Specification(镜像规范):定义了容器镜像的结构和格式,包括镜像的分层结构、元数据和配置等信息。常见的镜像格式是OCI Image(也称为OCI Image Format)。
这些规范和标准确保了不同容器运行时和工具的互操作性,使得用户可以在遵循OCI规范的前提下,使用不同的容器运行时和工具来创建、运行和管理容器。这也促进了容器生态系统的发展,提供了更大的选择和灵活性。目前在行业中遵循OCI标准的容器解决方案比较熟悉的有Docker、Rocket(CoreOS)、warden (Cloud Foundary)等。 除了规范和标准之外,OCI还有一个包含开放源代码项目的工作组,用于实现和推进OCI规范的实际落地。这些项目包括runc(容器运行时实现)、image-spec(镜像规范实现)等。

因此,OCI不仅是一些文档,而是一个综合性的容器标准化倡议组织,通过定义规范和提供开源项目来推动容器生态系统的互操作性和可移植性。

接下来,分别讲解下容器运行时规范和容器镜像规范。

3.1、OCI Runtime 规范

OCI 运行时规范定义了容器配置、运行时和生命周期的标准,主流的容器运行时都遵循OCI运行时的规范,从而提高系统的可移植性和互操作性,用户可根据需要进行选择。 首先,容器启动前需要在文件系统中按一定格式存放所需的文件。OCI运行时规范定义了容器文件系统包(filesystem bundle)的标准,在OCI运行时的实现中通常由高层运行时下载 OCI 镜像,并将OCI镜像解压成OCI运行时文件系统包,然后 OCI 运行时读取配置信息和启动容器里的进程。OCI运行时文件系统包主要包括以下两部分。
  • config.json:这是必需的配置文件,存放于文件系统包的根目录下。OCI运行时规范对Linux、Windows、Solaris和虚拟机4种平台的运行时做了相应的配置规范。
  • 容器的根文件系统:容器启动后进程所使用的根文件系统,由 config.json 中的root.path属性确定该文件系统的路径,通常是“rootfs/”。
然后,在定义文件系统包的基础上,OCI运行时规范制定了运行时和生命周期管理规范。生命周期定义了容器从创建到删除的全过程。总的来说OCI希望通过规范容器的配置、执行环境和生命周期管理,进而达到Docker所提出的“Build, Ship, and Run any app, anwhere”愿景,为了达到这个目的,OCI在制定之初提出了以下5个理念:
  • 操作标准化:对容器整个生命周期内相关的标准化进行标准化,包括:创建、启动、停止、创建快照、暂停、恢复等操作。规范每个操作的具体含义,将容器的具体操作进行原子化规范。
  • 内容无关:内容无关指不管针对的具体容器内容是什么,容器标准操作执行后都能产生同样的效果。如容器可以用同样的方式上传、启动,不管是PostgreSQL还是MySQL数据库服务。
  • 基础设施无关:容器可以运行在任何支持OCI的基础设施上。
  • 为自动化而生:由于容器的标准操作与基础设施无关,这样就为我们更好的进行自动化管理提供了良好的基础。以前那些耗时、耗力需要投入大量人力的工作,现在就可以利用容器进行自支管理。制定容器统一标准,是的操作内容无关化、平台无关化的根本目的之一,就是为了可以使容器操作全平台自动化。
  • 工业级交付:容器标准化能够使软件应用的分发可以达到工业级的交付。标准容器使得我们可以构建自动化的软件交付流水线。不管是内部的DevOps流,还是外部的软件交付机制,容器正在一点点的改变我们对软件打包和交付的认识。
注意 1:OCI(Open Container Initiative)主要关注容器的低层运行时(runtime)规范,而不包含高层运行时的规范。而高层运行时(例如Docker、containerd、CRI-O等)则是在OCI规范的基础上构建的,它们提供了更高级别的容器管理和操作功能,例如镜像管理、容器编排、网络管理等。这些高层运行时工具根据OCI规范实现了自己的容器生态系统,为用户提供更便捷的容器使用体验。低层运行时和高层运行时详细介绍请参见《浅析容器运行时》这篇博文。 注意 2:本文出现的OCI运行时都是指容器的底层运行时。

3.1.1 OCI Runtime 规范——基本属性

OCI规范规定容器的基本状态包含以下几种属性:
  • - ociVersion:oci规范的版本信息
  • - id: 容器的ID, 在同一主机上必须唯一,对于不同主机的容器ID,不做强制性要求。
  • - status: 容器的运行状态,包含以下几种: - creating: 正在被创建 - created: 容器进程未退出,而用户的应用进程还未执行的状态。 - running: 容器进程已经退出,而且用户的应用进程已经开始正常运行。 - stopped: 容器进程已经退出。
  • - pid: 容器进程ID。
  • - bundle: 容器标准包的绝对路径。包含了容器的具体运行时配置信息和root文件系统。
  • - annotations: 容器的自定义属性信息。
示例如下:
1
2
3
4
5
6
7
8
9
10
<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">{</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">"ociVersion": "0.2.0",</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">"id": "oci-container1",</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">"status": "running",</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">"pid": 4422,</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">"bundle": "/containers/redis",</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">"annotations": {</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">"myKey": "myValue"</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">}</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">} </font>

3.1.2 OCI Runtime 规范——生命周期

OCI定义了容器的生命周期中四个基本的状态: creating, created, running, stopped。各状态的转换如下图所示:

OCI标准 (open container initiative) - 图1

需要注意的是OCI在start操作中预置了3个勾子函数prestart, poststart, poststop。用于在容器进程,用户进程启动前后进行一些定制化的操作。
  • prestart: 只能在运行时进行调用,如果调用失败需要清除容器进程。prestart会在start命令执行后,但还未启动用户进程之前进行调用。对Linux来讲,prestart会在容器命名空间创建完成后调用。
  • poststart:该hook会在启动完用户进程,但start操作还未返回前进行调用。比如,我们可以通过poststart hook通知用户容器的进程已经启动。
  • poststop: 会在容器被删除但是删除命令还未返回之前被调用。

3.1.3 OCI Runtime 规范——运行时配置(Linux)

由于容器Runtime的配置文件config.json在各平台下的配置略有不同,本文主要介绍常见的Linux平台下的配置。容器Runtime配置主要围绕元数据、资源隔离、资源管理、用户进程几个维度展开:元数据主要包括:
  • - oci的版本信息: ociVersion
  • - 容器运行的根文件系统(root filesystem)路径和读写权限。
  • - hostname配置。
  • - 用户配置

3.1.4 OCI Runtime 规范——资源隔离(namespace)

对于Linux来讲,OCI支持Linux内核支持的7种类型,具体来讲如下:
  • - pid: 保证用户进程只能看到所在容器内的其它进程。
  • - network:使容器拥有自已的网络栈。
  • - mount: 使容器拥有隔离的mount表。
  • - ipc: 使容器内的进程拥有系统级的IPC资源隔离。
  • - uts: 容器可以使用自已的hostname和domainname。
  • - user: 使得容器可以对主机和容器内的用户和用户组进行映射。
  • - cgroup:使得容器拥有独立的cgroup视图。

3.1.5 OCI Runtime 规范——资源管理

  • - mount:根据用户的需求,顺序对用户的挂载配置项进行挂载操作。每个挂载项包含基本的source, destination配置项。
  • - rlimit: cpu,mem等的限制。

3.1.6 OCI Runtime 规范——用户进程

用户进程即process配置项,主要包括环境变量、安全、权限控制、OOM管理等内容。当然还有最重要的用户进程的配置。对Linux来讲,还要求容器内的proc, sysfs, devpts, tmpfs这四个文件系统必须可用。

3.1.7 OCI Runtime 规范——容器标准包(Bundle)

容器标准包包含了容器运行的所有环境依赖,它是保证容器运行一致性的基础。一个标准的容器标准包包含所需要加载和启动容器的所有信息。包含两部分内容:
  • - config.json: 即前文所述的容器运行时配置内容。
  • - root filesystem: 即前文所述的root.path所代表的位置。
注意 1: OCI Bundle用于描述容器运行时所需的文件和目录的集合。OCI Bundle 是一个文件夹(或目录),其中包含了容器的根文件系统、配置文件、执行上下文等相关内容。它提供了容器运行时所需的一切组件,使得容器的创建、启动和管理变得更加简单和可靠。 在 OCI 规范中,OCI Bundle 必须包含以下几个重要的元素:
  1. 根文件系统(Root Filesystem):包含了容器的根文件系统镜像,用于提供容器的文件系统环境。
  2. 配置文件(Configuration File):描述容器的配置和运行参数,例如容器的资源限制、环境变量、网络设置等。
  3. 执行上下文(Execution Context):包含了容器运行时所需的执行上下文信息,如容器的进程、文件描述符等。
通过定义和组织这些元素,OCI Bundle 提供了一种标准的容器描述方式,使得容器可以在不同的 OCI 兼容的运行时中进行无缝迁移和运行。 总结来说,OCI Bundle 是 Open Container Initiative 规范中定义的一个文件夹,用于描述容器运行时所需的文件和目录的集合。它包含了根文件系统、配置文件和执行上下文等组件,为容器的创建和管理提供了一致的标准化方式。

3.2、OCI Image规范

OCI的Image格式规范是容器ship anywhere的基础, 最终落地时体现为Runtime中的bundle,以此为基础为用户提供一致的运行时依赖环境。该规范由Docker贡献,并由社区维护。该规范包含manifest, image index 和 filesystem layers三部分内容。
  • - anifest: 对于指定架构和OS的容器镜像, manifest定义了它所依赖的相关配置信息和对应的layer镜像层信息。
  • - image index: 比manifest更高层的抽象,包含了额外的配置信息。
  • - filesystem layer: 给出了如何将容器的文件系统进行序列化,如何创建和使用这些layer。我们知道容器的启动速度可达秒级。主要的原因是我们常见的aufs, devicemapper等均采用了COW(copy on write)的技术,使得相同镜像的不同容器实例可以共享bundle,write(修改)的数据也是在layter中。

4、OCI一种实现方式——runc介绍

OCI(Open Container Initiative)规范定义了容器的底层运行时接口和容器镜像格式,为容器生态系统的互操作性提供了标准。现在有了规范,还需要一个落地的实体。由此runc诞生了(runc是官方提供的容器运行时参考实现)。 runc最初属于Docker公司的libcontainer项目,用于提供容器的底层隔离和资源管理功能。随着Docker公司将libcontainer开源并捐赠给Linux基金会,runc成为OCI的一部分。作为基于libcontainer的重构版本,runc负责容器的生命周期管理,遵循OCI容器运行时规范。 runc在容器生态系统中得到了广泛的应用和采用。作为容器运行时的参考实现,它符合OCI规范,可以与其他兼容OCI规范的工具和生态系统无缝协同工作。runc为开发者和企业提供了一致的容器运行时接口,使得容器的创建、启动、停止和销毁等操作变得更加标准化和可移植。因此,runc在推动容器技术的发展和推广中发挥了重要的作用。 注意 1:Docker也在其v1.11版本以后开始将runc作为自身服务的一个组件。

4.1 runc功能简介

我们先看下runc都提供那些功能:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@node1 ~]# runc -h</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">NAME:</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">runc - Open Container Initiative runtime</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">runc is a command line client for running applications packaged according to</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">the Open Container Initiative (OCI) format and is a compliant implementation of the</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">Open Container Initiative specification.</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">runc integrates well with existing process supervisors to provide a production</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">container runtime environment for applications. It can be used with your</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">existing process monitoring tools and the container will be spawned as a</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">direct child of the process supervisor.</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">Containers are configured using bundles. A bundle for a container is a directory</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">that includes a specification file named "config.json" and a root filesystem.</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">The root filesystem contains the contents of the container.</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">To start a new instance of a container:</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;"># runc run [ -b bundle ] <</font>``<font style="color:rgb(0, 0, 255) !important;background-color:rgb(255, 255, 255) !important;">container-id</font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">></font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">Where "<</font>``<font style="color:rgb(0, 0, 255) !important;background-color:rgb(255, 255, 255) !important;">container-id</font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">>" is your name for the instance of the container that you</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">are starting. The name you provide for the container instance must be unique on</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">your host. Providing the bundle directory using "-b" is optional. The default</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">value for "bundle" is the current directory.</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">USAGE:</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">runc [global options] command [command options] [arguments...]</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">VERSION:</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">1.0.3</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">commit: v1.0.3-0-gf46b6ba</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">spec: 1.0.2-dev</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">go: go1.16.15</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">libseccomp: 2.5.1</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">COMMANDS:</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">checkpoint checkpoint a running container</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">create create a container</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">delete delete any resources held by the container often used with detached container</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">events display container events such as OOM notifications, cpu, memory, and IO usage statistics</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">exec execute new process inside the container</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">init initialize the namespaces and launch the process (do not call it outside of runc)</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">kill kill sends the specified signal (default: SIGTERM) to the container's init process</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">list lists containers started by runc with the given root</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">pause pause suspends all processes inside the container</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">ps ps displays the processes running inside a container</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">restore restore a container from a previous checkpoint</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">resume resumes all processes that have been previously paused</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">run create and run a container</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">spec create a new specification file</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">start executes the user defined process in a created container</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">state output the state of a container</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">update update container resource constraints</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">help, h Shows a list of commands or help for one command</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">GLOBAL OPTIONS:</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">--debug enable debug output for logging</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">--log value set the log file path where internal debug information is written</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">--log-format value set the format used by logs ('text' (default), or 'json') (default: "text")</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">--root value root directory for storage of container state (this should be located in tmpfs) (default: "/run/runc")</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">--criu value path to the criu binary used for checkpoint and restore (default: "criu")</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">--systemd-cgroup enable systemd cgroup support, expects cgroupsPath to be of form "slice:prefix:name" for e.g. "system.slice:runc:434234"</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">--rootless value ignore cgroup permission errors ('true', 'false', or 'auto') (default: "auto")</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">--help, -h show help</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">--version, -v print the version</font>

如前文的Runtime介绍中所述,runC提供了生命周期管理、暂停、恢复、热迁移、状态查询等操作。具体的细节在此不再赘述。下面我们通过运行一个容器来演示OCI是如何进行容器管理,提供基础的原子操作,与上层的管理系统进行解耦的。

4.2 runc功能示例

我们通过运行一个容器监控工具cadvisor的容器来展示整个容器管理过程。
由上文可知,在OCI下我们要运行一个容器,需要做两个准备:config.json、bundle(filesystem) 。 (1)、config.json 准备
config.json定义了容器运行时的具体配置信息,首先我们利用runC生成一个模板,然后在模板上再进行相关的修改。
1
2
3
4
5
6
<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 runc-demo]# pwd && ls</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">/opt/runc-demo</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 runc-demo]# runc spec</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 runc-demo]# ls</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">config.json</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 runc-demo]#</font>

为了方便演示,我们简单修改cadvisor的启动参数,将args修改为:/usr/bin/cadvisor -logtostderr ,修改后的config.json为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">{</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">"ociVersion": "1.0.2-dev",</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">"process": {</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">"terminal": true,</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">"user": {</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">"uid": 0,</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">"gid": 0</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">},</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">"args": [</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">"/usr/bin/cadvisor",</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">"-logtostderr"</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">],</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">"env": [</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">"TERM=xterm"</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">],</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">"cwd": "/",</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">......</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">}</font>

(2)、bundle 准备
runC可以使用符合OCI规范的bundle,前边提到这个规范是Docker贡献的,所以为了简化过程,我们可以直接利用Docker生成这样一个bundle。我们在另外一台部署有Docker的主机上执行以下命令创建cadvisor bundle。
1
2
3
4
<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 runc-demo]# mdkir rootfs</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 runc-demo]# docker export $(docker create cadvisor:latest)|tar -C rootfs -xvf-</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 runc-demo]# ls rootfs/</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">bin dev etc glibc-2.28-r0.apk glibc-bin-2.28-r0.apk home lib lib64 media mnt proc root run sbin srv sys tmp usr var</font>

(3)、create
完成了准备工作,我们就可以创建容器了。现在我们看下当前的目录结构:
1
2
3
4
<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 runc-demo]# ls</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">config.json rootfs</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 runc-demo]# runc create oci-cadvisor</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">ERRO[0000] runc create failed: cannot allocate tty if runc will detach without setting console socket</font>

执行过程中会出现如下错误,将config.json里面的terminal字段修改微false,即可。
1
2
3
4
5
6
<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 runc-demo]# runc create oci-cadvisor</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 runc-demo]# runc list</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">ID PID STATUS BUNDLE CREATED OWNER</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">oci-cadvisor 75690 created /opt/runc-demo 2023-06-19T03:28:34.390683035Z root</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 runc-demo]# ps -ef|grep cadvisor</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">root 78338 29627 0 11:29 pts/0 00:00:00 grep --color=auto cadvisor</font>

从执行输出可以看到oci-cadvisor容器已经create成功,但cadvisor进程还未被拉起。等等,那这个pid(75690)是谁的进程ID?我们来看一下,其实这是runc的init进程。具体我们会在后续的文章里解释。
1
2
3
4
5
`[root@master1 runc-demo]# ps -ef grep 75690`

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">root 75690 1 0 11:28 ? 00:00:00 runc init</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">root 85074 29627 0 11:33 pts/0 00:00:00 grep --color=auto 75690</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 runc-demo]# ps -ef|grep 75690|grep -v grep</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">root 75690 1 0 11:28 ? 00:00:00 runc init</font>

(4) 、start
oci-cadvisor容器的容器进程已经被拉起,接下来需要做的就是把真正的业务进程拉起来。结合前边的生命周期管理图,所以可看我们现在需要执行start操作。
1
2
3
4
5
6
<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 runc-demo]# runc start oci-cadvisor</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 ~]# runc list</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">ID PID STATUS BUNDLE CREATED OWNER</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">oci-cadvisor 75690 running /opt/runc-demo 2023-06-19T03:39:43.998415069Z root</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 ~]# ps -ef|grep cadvisor|grep -v grep</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">root 75690 1 0 11:39 ? 00:00:00 /usr/bin/cadvisor -logtostderr</font>

现在可以看到oci-cadvisor容器已经run起来了。仔细再观察一下,what? cadvisor进程的pid和前边runc init的进程pid居然是一样的? 这是因为runc通过执行syscall.Exec(Linux 中的exec)让用户进程接管了init进程。 (5)、exec
现在容器进程也跑起来了,让我们进到oci-cadvisor去看一看。说进容器里看一看,其实是我们再新起了一个进程,而这个进程的命名空间和容器拥有的命名空间,这样就可以通过这个进程去查看容器内的信息。让我们sh进去简单看一下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 ~]# runc exec -t oci-cadvisor /bin/sh</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">/ # ps aux</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">PID USER TIME COMMAND</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">1 root 0:00 /usr/bin/cadvisor -logtostderr</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">24 root 0:00 /bin/sh</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">30 root 0:00 ps aux</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">/ # ifconfig</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">lo Link encap:Local Loopback </font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">inet addr:127.0.0.1 Mask:255.0.0.0</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">inet6 addr: ::1/128 Scope:Host</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">UP LOOPBACK RUNNING MTU:65536 Metric:1</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">RX packets:24 errors:0 dropped:0 overruns:0 frame:0</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">TX packets:24 errors:0 dropped:0 overruns:0 carrier:0</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">collisions:0 txqueuelen:1000</font>

<font style="color:rgb(51, 51, 51);background-color:rgb(255, 255, 255) !important;"> </font>``<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">RX bytes:1744 (1.7 KiB) TX bytes:1744 (1.7 KiB)</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">/ # netstat -ntlp|grep 8080</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">tcp 0 0 :::8080 :::* LISTEN 1/cadvisor</font>

从容器内我们可以看到oci-cadvisor拥有的cadvisor进程,我们刚才exec的sh进程。同样该容器也拥有独立的网络栈。当然,这些只是容器的一部分特性。 (6)、kill 接下来,我们kill掉oci-cadvisor容器,使其进行stopped状态。
1
2
3
4
<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 ~]# runc kill oci-cadvisor</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 ~]# runc list</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">ID PID STATUS BUNDLE CREATED OWNER</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">oci-cadvisor 0 stopped /opt/runc-demo 2023-06-19T03:39:43.998415069Z root</font>

(7)、delete
最后,我们将oci-cadvisor从我们的环境里清理掉,删除运行时的数据(注意bundle仍在)。
1
2
3
4
5
6
<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 ~]# runc delete oci-cadvisor</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 ~]# runc list</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">ID PID STATUS BUNDLE CREATED OWNER</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 runc-demo]# cd /opt/runc-demo/</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">[root@master1 runc-demo]# ls rootfs/</font>

<font style="color:rgb(0, 0, 0) !important;background-color:rgb(255, 255, 255) !important;">bin dev etc glibc-2.28-r0.apk glibc-bin-2.28-r0.apk home lib lib64 media mnt proc root run sbin srv sys tmp usr var</font>

至此完成了runc对容器的整个生命周期管理过程展示。

4.3 runc功总结

现在看起来利用runc创建容器,并对其进行管理还是比较简单,解决了容器最核心、最底层、最基础的问题。而这离我们的实际需求还差的很远,想要成为云计算的基础设施还有很长的路要走。具体来说主要面临以下几个问题。 对大规模管理的支持较弱。runc只是个命令行工具,不是常驻进程,对于大规模的编排需求,无法通过网络调用实现。同样,也无法实现整个容器生命周期的自动化管理。 bundle的管理。OCI包含了OCF规范,但是像我们这样直接利用原生的bundle来构建容器运行时的环境依赖直观上来看有以下几个缺陷:
  • - 每个容器都要有自己的bundle,无法复用(应用都有写数据需求),同时带来的是存储资源的浪费和启动速度的下降。
  • - 容器的bundle没有统一的管理,“ship anywhere”的愿望看起来可望不可及。
  • - bundle的生命周期管理现在还没解决。runC的delete操作并不是清理bundle。
  • 网络能力弱。容器拥有独立的网络栈,但是还没有解决容器内的业务进程的通信需求,“世界那么大,还是要出去看看的”。
虽然总有不足的地方,但庆幸的是已经迈出了第一步。OCI(Open Container Initiative)组织一成立便得到了包括谷歌、微软、亚马逊、华为等一系列云计算厂商的支持。制定的容器运行时和镜像规范现已经成为一个可靠的基础标准。OCI通过开源的方式以runc落地,逐步脱离Docker的控制范围。在runc的基础上,允许和鼓励多样化的容器解决方案,这为广大的云厂商和我们这些开发者提供了更广阔的发挥空间,不断促进容器生态的持续创新,服务各行各业。

5、总结

OCI(Open Container Initiative)是一个开放容器倡议组织,旨在推动容器运行时和镜像格式的开放标准化。OCI由多家技术公司和个人共同组成,包括Docker、CoreOS、Google、Red Hat等。该倡议组织的目标是创建一套通用的容器标准,以确保不同容器运行时和工具之间的互操作性和可移植性。 OCI的核心成果是一系列规范和标准,这些规范定义了容器运行时和镜像格式的规范化接口和结构。这些规范包括:
  1. Runtime Specification(运行时规范):定义了容器的生命周期管理,包括创建、启动、停止和销毁容器等操作。最常见的容器运行时实现是OCI Runtime(也称为runc)。
  2. Image Specification(镜像规范):定义了容器镜像的结构和格式,包括镜像的分层结构、元数据和配置等信息。常见的镜像格式是OCI Image(也称为OCI Image Format)。
这些规范和标准确保了不同容器运行时和工具的互操作性,使得用户可以在遵循OCI规范的前提下,使用不同的容器运行时和工具来创建、运行和管理容器。这也促进了容器生态系统的发展,提供了更大的选择和灵活性。

除了规范和标准之外,OCI还有一个包含开放源代码项目的工作组,用于实现和推进OCI规范的实际落地。这些项目包括runc(底层容器运行时实现)、image-spec(镜像规范实现)等。

因此,OCI不仅是一些文档,而是一个综合性的容器标准化倡议组织,通过定义规范和提供开源项目来推动容器生态系统的互操作性和可移植性。