OCI相关译文,包括image规范以及Runtime规范。
一、OCI(Open Container Initiative)
OCI(Open Container Initiative)是指开放容器倡议,是一个开放的治理结构;目的是建立容器格式和运行时的行业开放标准。
OCI 由 Docker 和其他容器行业的领导者于 2015 年 6 月创立,目前包含两个规范:运行时规范(runtime-spec)和镜像规范(image-spec)。运行时规范概述了如何运行在磁盘上解压的“文件系统包”。在高层级的表现是,OCI 实现会下载一个 OCI 映像,然后将该映像解压缩到一个 OCI 运行时文件系统包中。此时,OCI Runtime Bundle 将由 OCI Runtime 运行。
二、Image Spec
实现镜像规范(image-spec)的工具,可用于构建、传输和准备要运行的容器镜像。
本规范定义了如何创建 OCI 镜像,由以下几部分组成:
- image manifest:镜像清单,包含镜像内容和依赖项的元数据
- filesystem (layer) serialization:文件系统的序列化,包含一个或多个文件系统
层的压缩档案,可以解压成可执行的文件系统 - image configuration:镜像配置包括诸如应用参数、环境等信息
OCI镜像一经构建,可以通过名称发现、下载、哈希校验、签名认证以及解压为OCI的运行时Bundle。
OCI 镜像的媒体类型
相关术语
- Layer 图层:文件系统由layers 层组成,每一层表示一组基于 tar 的文件系统更改;记录要相对于其父层添加、更改或删除的文件;图层没有配置元数据
- Image JSON:描述了有关镜像的一些基本信息
- Layer DiffID:表示该层未压缩文件的sha25
- Layer ChainID:用于标识
镜像层级栈,取值由DiffID计算得来;存储了父层以及对应镜像数据的存储目录的cache_id - ImageID:每个镜像ID 由其配置 JSON的 SHA256 散列给出,表示为 256 位的十六进制编码
镜像的媒体类型如下:
application/vnd.oci.descriptor.v1+json:内容描述符- 作用
- 描述了目标内容的配置,表示组件之间的引用
- 内容描述符包括内容的类型、内容标识符(摘要)和原始内容的字节大小。
- 属性
mediaType:媒体类型digest:摘要size:大小urls:下载此对象的 URI 列表annotations:包含此描述符的任意元数据
- 作用
application/vnd.oci.layout.header.v1+json:OCI 布局- blobs 包含内容可寻址的 blob
- oci-layout
- index:表示镜像索引的JSON对象
application/vnd.oci.image.index.v1+json:镜像索引- schemaVersion:docker架构的版本,为向后兼容需要为2;未来的版本中可能被删除
- mediaType 需要为application/vnd.oci.image.index.v1+json
- manifests(必须):该属性包含特定平台的清单列表,数组大小可为0
- mediaType:必须支持application/vnd.oci.image.manifest.v1+json与application/vnd.oci.image.index.v1+json
- platform:定义特定的平台
- architecture:CPU 体系结构
- os:操作系统
- os.version:操作系统的版本
- os.features:指定一个强制性的操作系统功能
- variant:指定 CPU 的变体
- features:保留字段
- annotations:任意元数据
application/vnd.oci.image.manifest.v1+json:镜像清单- schemaVersion:docker架构的版本,为向后兼容需要为2;未来的版本中可能被删除
- mediaType 需要为application/vnd.oci.image.index.v1+json
- config:容器的配置对象
layers(对象数组):每一项都是描述符,索引为0时表示基础层,然后按照堆栈顺序设置索引;文件系统的布局最终可以匹配空目录,初始空目录的所有权、模式和其他属性未指定。mediaType:至少支持以下的媒体类型- application/vnd.oci.image.layer.v1.tar
- application/vnd.oci.image.layer.v1.tar+gzip
- application/vnd.oci.image.layer.nondistributable.v1.tar
- application/vnd.oci.image.layer.nondistributable.v1.tar+gzip
annotations:任意元数据
- application/vnd.oci.image.config.v1+json:镜像配置
- created:创建镜像的日期和时间
- author:提供创建并负责维护图像的个人或实体的姓名、电子邮件地址
- architecture(必选): CPU 架构
- os(必选):操作系统
- os.version:操作系统版本
- os.features 字符串数组,系统所需组件
- variant: CPU 架构的变体
- config:镜像运行容器的基础执行参数
User:镜像的默认用户;如果是Linux 的系统,参数可以是user、uid、user:group、uid:gid、uid:group、user:gidExposedPorts:容器暴露的一组端口Env:字符串数组;格式为VARNAME=VARVALUE,表示环境变量Entrypoint:字符串数组;用作容器启动时待执行命令的参数列表Cmd:字符串数组;如果Entrypoint未指定值,则Cmd数组的第一个条目应该被解释为要运行的可执行文件Volumes:描述进程可能将特定容器实例的数据写入何处WorkingDir:设置当前工作目录Labels:该字段包含容器的任意元数据StopSignal:发送到容器后停止容器的默认信号;该信号可以是与内核 syscall 表中的位置匹配的有效无符号数字(例如9),也可以是 SIGNAME 格式的信号名称(例如 SIGKILL或SIGRTMIN)- Memory 保留使用
- MemorySwap 保留使用
- CpuShares 保留使用
- Healthcheck 保留使用
- rootfs(必选):引用镜像相关层的内容地址
- type(必选):必须设置为layers
- diff_ids(必选):DiffIDs数组
- history:描述每一图层的历史
- created
- author
- created_by
- comment:自定义消息
- empty_layer:如果历史记录项与 rootfs 部分中的实际层不对应,则将其设置为 true
- application/vnd.oci.image.layer.v1.tar:“Layer”,作为 tar 存档
- application/vnd.oci.image.layer.v1.tar+gzip: "Layer",通过gzip压缩的 tar 存档
- application/vnd.oci.image.layer.v1.tar+zstd: "Layer",通过zstd压缩的 tar 存档
- application/vnd.oci.image.layer.nondistributable.v1.tar:“Layer”,具有分发限制的 tar 存档
- application/vnd.oci.image.layer.nondistributable.v1.tar+gzip: "Layer",具有分发限制的 tar 存档,使用gzip压缩
- application/vnd.oci.image.layer.nondistributable.v1.tar+zstd: "Layer",具有分发限制的 tar 存档,用zstd压缩
媒体类型按以下方式相互引用:
【上图来自media-types.md】
而描述符用于所有引用;镜像清单只与一个目标配置文件有关,且可能有多层。
annotations规则
- 注释必须是键值映射,其中键和值都必须是字符串。
- 虽然该值必须存在,但它可能是一个空字符串
- key必须是唯一的
- key在映射中需要使用方向域名表示法,例如
com.example.myKey - OCI规范预留了前缀为
org.opencontainers的KEY,不得被其他规范和扩展使用,包括其他 OCI 规范 - 如果没有anntation,则此属性不存在或为空
- 查询未知的KEY时,不得产生错误
镜像层中文件系统的更改(Image Layer Filesystem Changeset)
本章描述了如何序列化一个文件系统以及文件系统的修改,例如删除文件时将信息写入到称之为层的blob中。
修改类型
- Additions 附加
- Modifications 修改
- Removals 移动
文件类型
- 普通文件(-)
- 目录(d)
- 套接字(sockets):实现双向管道的进程间通信功能
- 软链接(字符链接L)
- 块设备(B):硬件设备,通过随机访问固定大小的数据块(chunk)来区分
- 字符设备:通过连续的流数据访问,典型的字符设备是终端和键盘
- FIFOs(命令管道):与套接字相比,FIFO是更底层的进程间通信方式,允许一端写入数据,另外一端读取数据;且作为一个文件存在于目录中
文件属性
- 修改时间(
mtime) - 用户编号(
uid)- 用户名称(
uname)
- 用户名称(
- 组别编号(
gid)- 组名(
gname)
- 组名(
- 模式(
mode) - 扩展属性(
xattrs) - 符号链接引用(
linkname+symbolic link type) - 硬链接引用(linkname)
硬链接(Hardlinks )
- 硬链接是一个POSIX概念;表示在同一个设备上,同一文件拥有一个或多个目录;
- 不是所有的文件系统都支持硬链接;
- 如果非目录文件的链接数大于1,则认为它们是“硬链接”文件;
- 硬链接文件位于同一设备上具有相同的 inode;
- 硬链接文件可能位于产生变更集(changeset)的目录之外,没有被记录在
changeset中 - 硬链接可存储在
tar文件中
Windows 上的实现必须支持这些附加属性:
- 文件属性 (MSWINDOWS.fileattr)
- 安全描述符 (MSWINDOWS.rawsd)
- 挂载点(MSWINDOWS.mountpoint)
- 创作时间(LIBARCHIVE.creationtime)
转换为 OCI 运行时配置
将OCI镜像提取到 OCI Runtime 中时,有以下两个步骤:
- 从文件系统层的集合中提取为
bundle,表示根文件系统; - 将媒体类型为
application/vnd.oci.image.config.v1+json的镜像配置转换为OCI运行时配置
兼容性好的配置转换器必须在生成的运行时配置中,将下列字段逐字提取到相应的字段:
对应字段 某些镜像配置字段在运行时配置中具有相同的对应关系
| Image Field | Runtime Field | Notes |
|---|---|---|
Config.WorkingDir | process.cwd | |
Config.Env | process.env | 1 |
Config.Entrypoint | process.args | 2 |
Config.Cmd | process.args | 2 |
注释字段
| Image Field | Runtime Field | Notes |
|---|---|---|
os | annotations | 1,2 |
architecture | annotations | 1,3 |
variant | annotations | 1,4 |
os.version | annotations | 1,5 |
os.features | annotations | 1,6 |
author | annotations | 1,7 |
created | annotations | 1,8 |
Config.Labels | annotations | |
Config.StopSignal | annotations | 1,9 |
解析字段
某些镜像配置字段有一个对应的字段,必须先进行翻译;符合要求的配置转换器应该解析相关字段并在生成的运行时配置中设置相应的字段。
| Image Field | Runtime Field |
|---|---|
Config.User | process.user.* |
可选字段 某些镜像配置字段不适用于所有转换,因此在配置转换器中的实现是可选的。
| Image Field | Runtime Field | Notes |
|---|---|---|
Config.ExposedPorts | annotations | 1 |
Config.Volumes | mounts | 2 |
注释 在镜像规范中,有以下几种方式定义注释:
- 镜像配置中的 Config.Labels属性;
- 镜像manifest中的
annotations属性; - 镜像索引的
annotations属性。
镜像规格
可拓展性
为保持可拓展性,必须忽略未知属性。
规范化
- OCI镜像是的内容可寻址,容易删除重复数据。
- 许多镜像可能依赖于一个特定的层,但在存储中只有一个blob。
- 为避免二义,需要使用规范的序列化方式。
JSON
JSON 内容应该序列化为规范的 JSON;在镜像规范的媒体类型中,所有以 +JSON 结尾的类型都包含 JSON 内容。
EBNF
对于规范中描述的字段格式,使用 Extended Backus-Naur 的有限子集,类似于 XML 规范使用的子集
实例:
# 相等
literal ::= "matchthis"
# 正则
set := [abc]
range := [A-Z]
zeroormore ::= expression*
oneormore ::= expression+
# 与
symbol ::= A B
# 中缀运算符
oneof ::= A | B
运算符的优先级顺序:
- 文字和字符类
- 分组
() - 一元操作符
+* - 连接
- 中缀运算符|
三、Runtime Spec
OCI运行时规范旨在指定容器的配置、执行环境和生命周期。
标准容器原则
为了方便容器的交付,定义一个叫做标准容器的软件交付单元。
标准容器的目标是将一个软件组件及其所有的依赖关系,用一种自我描述和可移植的格式进行封装;任何符合运行时规范的程序都可以在没有额外依赖的情况下运行它,而不管底层机器和容器的内容是什么。
标准容器的规范定义了:
- 配置文件格式
- 一套标准操作
- 一个执行环境
标准容器可以类比为运输业中使用集装箱。
集装箱作为交付的基本单元;可以被提起(lifted)、堆叠(stacked)、锁定、加载、卸载和添加标签。忽略集装箱的内容,通过制定一套一致的、精简的和有效的流程实现标准化。
对于软件来说,标准容器作为软件包的基本、标准化的交付单位,提供了类似的功能。
标准容器的 5 个原则
- 标准操作
- 标准容器定义了一组标准操作。可以使用标准容器工具创建、启动和停止它们;使用标准文件系统工具复制和快照;并使用标准网络工具下载和上传。
- 内容无关
- 启动方式一致
- 与基础设施无关
- 标准容器可以在任何 OCI 支持的基础设施中运行
- 专为自动化设计
- 标准容器专为自动化而设计:因为无论内容和基础设施如何,它们都提供相同的标准操作,因此标准容器非常适合自动化。
- 工业级交付
捆绑包(Filesystem Bundle)
容器格式
本节定义了一种将容器编码为文件系统包的格式——一组以某种方式组织的文件,并包含所有必要的数据和元数据,以便任何兼容的运行时程序对其执行所有标准操作。
捆绑包的定义只涉及容器及其配置数据如何存储在本地文件系统上,以便兼容的运行时程序使用。标准容器包含加载和运行容器所需的所有信息:
config.json包含配置数据,位于捆绑包的根目录中- 容器的根目录系统
捆绑 (Bundle macOS):捆绑通常包含一个可执行代码文件和一些资源文件,例如NIB文件,图像,声音,本地化字符串,配置文件(通常是属性列表文件)和其它媒体。在其它系统上,例如Microsoft Windows,这些资源通常在编译时就被直接包含在了可执行文件中。Mac OS X从NeXTSTEP中引入了捆绑的概念,用以代替早期Mac OS中以资源分支存储附加元数据的技术。多数类型的捆绑在使用时与普通文件类似,从而减少了其内部文件意外更改或丢失的风险。同时,捆绑的另一个意义在于可以使用文件夹简化组织资源的方式,避免使用资源分支导致的额外的复杂性。
运行时和生命周期
使用运行时创建容器的实体必须能够对同一容器使用本规范中定义的操作,而其它运行时实例是否可以看到该容器则不做约束。
State
容器的状态包括以下属性:
- ociVersion: (string,REQUIRED)是开放容器计划运行时规范(Open Container Initiative Runtime Specification)的版本,是状态遵守的规范版本。
- id:(string,REQUIRED)表示
容器ID。在单主机上的所有容器都必须是唯一的;没有要求它在主机间是唯一的。 - status:(string,REQUIRED)是容器的运行时状态。取值如下
creating表示正在创建容器created表示运行时已完成running表示容器进程已执行用户指定的程序,但尚未退出stopped容器进程已退出
- pid:(int,REQUIRED)是
容器进程ID。对于在运行时命名空间中执行的挂钩,它是运行时所看到的 pid。对于在容器命名空间中执行的挂钩,它是容器所看到的 pid。 - Bundle:(string,REQUIRED)是容器 bundle 目录的绝对路径。提供这些信息是为了让使用者能够在主机上找到容器的配置和根文件系统。
- annotations:包含与容器相关的注释列表;如果没有提供任何注释,那么这个属性可能不存在或者为空
hook:表示挂钩,是一种编程范例;在可以正常运作的程序中额外添加流程控制
生命周期(Lifecycle)
生命周期描述了事件的时间线,从容器被创建到销毁。
-
OCI兼容通过对捆绑包位置的引用以及唯一标识符调用运行时的
create命令; -
容器的运行时环境会根据
config.json中的配置来创建;如果无法根据该文件创建容器环境,必须向用户汇报错误。虽然config.json中要求的资源必须被创建,但用户指定的程序(来自进程)此时不可运行。此后config.json文件的任意修改都不会影响到容器。 -
prestart hook、createRuntime hooks、createContainer hooks必须被runtime程序调用。如果任意hook调用失败,runtime程序必须产生一个错误并停止容器,然后在第10步继续生命周期。 -
通过唯一标识符调用运行时程序的
start命令。 -
poststart hooks必须被runtime程序调用。如果任意hook调用失败,runtime程序必须产生一个错误并停止容器,然后在第10步继续生命周期。 -
runtime程序必须运行用户指定的程序,如 process 所指定的。
-
poststart hooks必须被runtime程序调用。如果调用失败会记录一个警告,而其余的hook和生命周期会继续运行。 -
容器进程退出,可能是由于fail、exit、崩溃或调用了runtime程序的 kill 操作而发生的。
-
通过唯一标识符调用运行时程序的
delete命令。 -
通过撤消创建阶段(步骤 2)执行的步骤来销毁容器。
-
poststop hooks必须由运行时程序调用。如果任何poststop hooks失败,运行时必须记录一个警告,但是其余的钩子和生命周期继续进行,就像钩子成功了一样。
Error
在指定命令反馈错误提示的情况下,本规范不强制规定如何将该错误返回或暴露给实现的用户。除非另有说明,否则生成错误后必须离开环境状态,就好像从未执行过命令一样。
Warnings
在指定的操作记录了一个警告的情况下,本规范并没有规定是否将该警告返回或暴露给实现的用户。除非另有说明,记录警告并不改变操作的流程;它会继续执行,就像Warnings没有被记录一样。
Operations
除非另有说明,运行时必须支持以下操作:
Query State(状态查询)
- 语法为
state <container-id>,如果没有提供容器的 ID,则此操作必须生成错误提示; - 试图查询不存在的容器必须生成错误提示;
- 此命令必须返回指定的容器状态。
Create
语法为:create <container-id> <path-to-bundle>
- 如果没有提供包的路径以及与容器关联的容器ID,则此操作必须生成错误。如果所提供的 ID 在运行时范围内的所有容器中都不是唯一的,或者在其他任何方面都是无效的,那么实现必须生成一个错误,并且不能创建新的容器。
- 此操作必须创建一个新容器。
- 除了进程必须应用之外,所有在 config.json 中配置的属性都必须应用。 在创建容器之前,运行时程序根据规范对 config.json 进行校验
- 在此操作之后对 config.json 文件所做的任何更改都不会对容器产生影响。
Start
语法为:start <container-id>
- 如果没有提供容器 ID,则此操作必须生成错误提示。
- 试图启动未创建的容器必须对容器没有影响并且必须生成错误提示。
- 此操作必须按照进程的指定运行用户指定的程序。
- 如果没有设置进程,此操作必须生成错误提示。
Kill
语法为:kill <container-id> <signal>
- 如果没有提供容器 ID,则此操作必须生成错误提示。
- 试图向既未创建也未运行的容器发送信号时,必须对容器没有影响并且必须生成错误提示。
- 此操作必须向容器进程发送指定的信号。
Delete
语法为:delete <container-id>
- 如果没有提供容器 ID,则此命令必须返回错误提示。尝试删除未停止的容器必须对容器没有影响并且必须返回错误提示;删除容器必须删除在
create命令中创建的资源。 - 请注意,不能删除与容器关联但不是由此容器创建的资源。一旦一个容器被删除,它的 ID 可能会被后续的容器使用。
Hooks
本规范中指定的许多操作都有“Hooks”,允许在每个操作之前或之后执行附加操作。
配置(config.json)
此配置文件包含实现容器标准操作所必需的元数据;包括运行进程、环境变量、沙箱特性等等。这些数据定义在Schema/config-Schema中,与Spec-Go/config.Go 绑定。
ociVersion
ociVersion:表示支持的OCI规范版本
例如:
"ociVersion": "0.1.0"
Root
root:指定容器的根文件系统。在 Windows 上,对于 Windows 服务器的容器,此字段为必须的;对于 Hyper-V 容器,该字段无法设置。- path:指定容器的根文件系统的路径
- 在 window 平台上,路径为
volume guid; - 在 POSIX 平台上, 可以是包的绝对路径(/to/bundle/rootfs)或相对路径(rootfs),需要包含
rootfs目录
- 在 window 平台上,路径为
readonly:取值为 true,那么根文件系统在容器内为只读的;默认值为 false
- path:指定容器的根文件系统的路径
例如:
# POSIX
"root": {
"path": "rootfs",
"readonly": true
}
# window
"root": {
"path": "\\\\?\\Volume{ec84d99e-3f02-11e7-ac6c-00155d7682cf}\\"
}
mounts
mounts(OPTIONAL,对象数组):表示对root目录进行挂载;runtime必须安装列出的顺序安装条目,该值必须是绝对路径。destination(必需,字符串):表示定义的挂载点,即容器内的路径,为绝对路径- window:一个挂载目录不能嵌套在另一个挂载目录中
- Solaris:对应于FS资源的“DIR”
source(OPTIONAL,字符串):设备名称,可以是绑定装载的文件或目录名称或虚拟名称。挂载的路径值相对于绑定包可以是绝对值,也可以是相对值。- Windows:容器主机文件系统上的本地目录,不支持 UNC 路径和映射驱动器
- Solaris:对应于FS资源的“DIR”
options(OPTIONAL,字符串数组):表示文件系统的挂载选项- Linux:支持mount(8)手册页中列出了支持的选项
- Solaris:对应于zonecfg(1M)中FS资源的“选项”
- Windows:runtime必须支持
ro,当设置为ro时挂载的文件系统是只读的
例如
# Windows
"mounts": [
{
"destination": "C:\\folder-inside-container",
"source": "C:\\folder-on-host",
"options": ["ro"]
}
]
mounts(POSIX)
对于 POSIX 平台,mount有以下字段:
- type(OPTIONAL,String):
- Linux:内核支持文件系统类型
/proc/filesystems中列出的内核,例如:"minix", "ext2", "ext3", "jfs", "xfs", "reiserfs", "msdos", "proc", "nfs","iso9660",bind,rbind,"none" - Solaris:对应于zonecfg(1M)中FS资源的“选项”
- Linux:内核支持文件系统类型
例如:
# linux
"mounts": [
{
"destination": "/tmp",
"type": "tmpfs",
"source": "tmpfs",
"options": ["nosuid","strictatime","mode=755","size=65536k"]
},
{
"destination": "/data",
"type": "none",
"source": "/volumes/testing",
"options": ["rbind","rw"]
}
]
# Solaris
"mounts": [
- {
"destination": "/opt/local",
"type": "lofs",
"source": "/usr/local",
"options": ["ro","nodevices"]
},
{
"destination": "/opt/sfw",
"type": "lofs",
"source": "/opt/sfw"
}
]
Process(程序)
Process (object,OPTIONAL)指定容器进程。在调用 start 时,此属性为 REQUIRED。
terminal(bool, OPTIONAL):指定一个终端是否被附加到进程上,默认为false。作为一个例子,如果在Linux上设置为 "true",就会为进程分配一个假终端对,并且假终端pty在进程的标准流中被重复使用。consoleSize(object, OPTIONAL):指定终端的控制台大小(以字符为单位)。如果终端为false或未设置,运行时必须忽略consoleSize。height(uint, REQUIRED)width(uint, REQUIRED)
cwd(string, REQUIRED):为可执行文件设置的工作目录。这个值必须是一个绝对路径env(字符串数组, OPTIONAL):设置环境变量,具有与IEEE标准1003.1-2008相同的语义。args(字符串数组, OPTIONAL):具有与IEEE标准1003.1-2008 execvp的argv类似的语义。本规范扩展了IEEE标准,至少有一个条目是REQUIRED(非Windows),该条目的使用语义与execvp的文件相同。这个字段在Windows上是OPTIONAL,如果省略了这个字段,commandLine是REQUIRED。commandLine(string, OPTIONAL):指定要在Windows上执行的完整命令行。这是在Windows上提供命令行的首选方法。如果省略,在向Windows进行系统调用之前,runtiem将回滚args的字段。
POSIX process
对于支持 POSIX rlimits 的系统(例如 Linux 和 Solaris) ,进程对象支持以下特定于进程的属性:
rlimits(对象数组, OPTIONAL):允许为进程设置资源限制。每个条目都有以下结构type(string, REQUIRED):用于限制平台资源- Linux:有效值定义在
getrlimit(2)手册页中,例如RLIMIT_MSGQUEUE - Solaris:有效值定义在
getrlimit(3)手册页中,例如RLIMIT_MSGQUEUE
- Linux:有效值定义在
soft(uint64, REQUIRED):对应资源执行的限制值;rlim.rlim_cur必须与配置的值相符。hard(uint64, REQUIRED):是指非特权进程可以设置的软限制的上限。rlim.rlim_max必须与配置的值相匹配。只有特权进程(例如,具有CAP_SYS_RESOURCE能力的进程)可以提高硬限制。
如果 rlimits 包含具有相同类型的重复项,则运行库必须生成错误。
Linux Process
对于基于 linux 的系统,进程对象支持以下特定于进程的属性:
apparmorProfile(string, OPTIONAL):指定进程AppArmor profile的名称capabilities(object, OPTIONAL):该数组指定流程的capabilities集合,有效值在capabilities(7)手册页中定义,例如CAP_CHOWN。任何不能被映射到相关的内核接口的值,或者不能被授予的值,都必须被Runtime记录为Warning。如果容器请求的功能不能被授权,例如Runtime在具有有限功能集的限制性环境中运行,那么不应该失败。capabilities包含以下属性:effective(string数组, OPTIONAL):内核进行线程capabilities检查时实际使用到的集合bounding(string数组, OPTIONAL):包含了所有 capabilities,为进程保留inheritable(string数组, OPTIONAL):当程序对应的可执行文件设置了inheritable bit位时,调用execve执行该程序会继承调用者的Inheritable集合,并将其加入到permitted集合。但在非root用户下执行execve时,通常不会保留inheritable 集合,可以考虑使用ambient 集合,当一个程序drop掉一个capabilities时,只能通过execve执行SUID置位的程序或者程序的文件带有该capabilities的方式来获得该capabilitiespermitted(string数组, OPTIONAL):effective集合和inheritable集合的超集,限制了它们的范围,因此如果一个capabilities不存在permitted中,是不可以通过cap_set_proc来获取的。当一个线程从permitted集合中丢弃一个capabilities时,只能通过获取程序可执行文件的capabilities或execve一个set-user-ID-root(以root用户权限运行的)程序来获得ambient(string数组, OPTIONAL):是在内核4.3之后引入的,用于补充Inheritable使用上的缺陷,ambien集合可以使用函数prctl修改。当程序由于SUID(SGID)bit位而转变UID(GID),或执行带有文件capabilities的程序时会导致该集合被清空
noNewPrivileges(bool, OPTIONAL):将no_new_privs设置为 "true "可以防止进程获得额外的权限。oomScoreAdj(int, OPTIONAL):设置伪文件系统中进程的[pid]/oom_score_adj的分数。如果设置了oomScoreAdj,Runtime必须将oom_score_adj设置为给定值;如果oomScoreAdj没有被设置,Runtime不会改变oom_score_adj的值。selinuxLabel(string, OPTIONAL):指定进程的SELinux标签。
User
Process的User属性是一个特定于平台的结构,允许特定的控制流程作为哪个用户运行。对于 POSIX 平台,User属性有以下字段:
uid(int, REQUIRED):用户IDgid(int, REQUIRED):用户组IDumask(int, OPTIONAL)additionalGids(整型数组, OPTIONAL):指定容器命名空间中附加的 组ID,
例如【Linux】:
"process": {
"terminal": true,
"consoleSize": {
"height": 25,
"width": 80
},
"user": {
"uid": 1,
"gid": 1,
"umask": 63,
"additionalGids": [5, 6]
},
"env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"TERM=xterm"
],
"cwd": "/root",
"args": [
"sh"
],
"apparmorProfile": "acme_secure_profile",
"selinuxLabel": "system_u:system_r:svirt_lxc_net_t:s0:c124,c675",
"noNewPrivileges": true,
"capabilities": {
"bounding": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"permitted": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"inheritable": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"effective": [
"CAP_AUDIT_WRITE",
"CAP_KILL"
],
"ambient": [
"CAP_NET_BIND_SERVICE"
]
},
"rlimits": [
{
"type": "RLIMIT_NOFILE",
"hard": 1024,
"soft": 1024
}
]
}
例如【Solaris】:
"process": {
"terminal": true,
"consoleSize": {
"height": 25,
"width": 80
},
"user": {
"uid": 1,
"gid": 1,
"umask": 7,
"additionalGids": [2, 8]
},
"env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"TERM=xterm"
],
"cwd": "/root",
"args": [
"/usr/bin/bash"
]
}
例如【Windows】:
"process": {
"terminal": true,
"user": {
"username": "containeradministrator"
},
"env": [
"VARIABLE=1"
],
"cwd": "c:\\foo",
"args": [
"someapp.exe",
]
}
Hostname
hostname (string, OPTIONAL):指定容器的主机名,如在容器内运行的进程所见
例如:
"hostname": "mrsdalloway"
特定于平台的配置(Platform-specific configuration)
linux(object, OPTIONAL)windows(object, OPTIONAL)solaris(object, OPTIONAL)vm(object, OPTIONAL)zos(object, OPTIONAL)
例如:
{
"linux": {
"namespaces": [
{
"type": "pid"
}
]
}
}
POSIX-platform Hooks
对于 POSIX 平台,配置的结构支持用于配置与容器生命周期相关的自定义操作的hook。
hooks(object, OPTIONAL)可以包含以下任何属性:prestart(对象数组, OPTIONAL):包含一组prestarthooks- 数组中的条目包含以下属性:
- path (string, REQUIRED)
- args(字符串数组, OPTIONAL)
- env(字符串数组, OPTIONAL)
- timeout (int, OPTIONAL):终止hook前的等待秒数,必须大于零
- path的值必须在运行时命名空间中解析。
prestart钩子必须在运行时命名空间中执行。
- 数组中的条目包含以下属性:
- createRuntime(array of objects, OPTIONAL):包含一组
createRuntimehooks- 数组中的条目包含以下属性:
- path (string, REQUIRED)
- args(字符串数组, OPTIONAL)
- env(字符串数组, OPTIONAL)
- timeout (int, OPTIONAL):终止hook前的等待秒数,必须大于零
- path的值必须在运行时命名空间中解析。
createRuntime钩子必须在运行时命名空间中执行。
- 数组中的条目包含以下属性:
createContainer(对象数组, OPTIONAL),包含了一组createContainer类型的hook- 数组中的条目与createRuntime条目具有相同的属性。
- path的值必须在运行时命名空间中解析。
-
createContainer钩子必须在容器的命名空间中执行。
- startContainer(对象数组, OPTIONAL),包含了一组startContainer类型的hook
- 数组中的条目与createRuntime条目具有相同的模式。
- path的值必须在容器命名空间中解析。
-
startContainer钩子必须在容器的命名空间中执行。
- poststart(对象数组, OPTIONAL),包含了一组poststart类型的hook
- 数组中的条目与createRuntime条目具有相同的模式。
- path的值必须在运行时命名空间中解析。
poststart钩子必须在运行时命名空间中执行。
- poststop(对象数组, OPTIONAL),包含了一组poststop类型的hook
- 数组中的条目与createRuntime条目具有相同的模式。
- path的值必须在运行时命名空间中解析。
poststop钩子必须在运行时命名空间中执行。
钩子允许用户指定在各种生命周期事件之前或之后运行的程序。钩子必须按照列出的顺序来调用。容器的状态必须通过 stdin 传递给钩子,以便它们可以做适合于容器当前状态的工作。
| hook名称 | 命名空间 | 生效期 |
|---|---|---|
prestart (Deprecated) | runtime | 在调用启动操作之后但在执行用户指定的程序命令之前 |
createRuntime | runtime | 运行在创建操作期间,在创建运行时环境之后并且在pivot_root或任何等效操作之前 |
createContainer | container | 运行在创建操作期间,在创建运行时环境之后并且在pivot_root或任何等效操作之前 |
startContainer | container | 在调用启动操作之后但在执行用户指定的程序命令之前 |
poststart | runtime | 在执行用户指定的进程之后但在开始操作返回之前 |
poststop | runtime | 在容器被删除之后但在删除操作返回之前 |
例如:
"hooks": {
"prestart": [
{
"path": "/usr/bin/fix-mounts",
"args": ["fix-mounts", "arg1", "arg2"],
"env": [ "key1=value1"]
},
{
"path": "/usr/bin/setup-network"
}
],
"createRuntime": [
{
"path": "/usr/bin/fix-mounts",
"args": ["fix-mounts", "arg1", "arg2"],
"env": [ "key1=value1"]
},
{
"path": "/usr/bin/setup-network"
}
],
"createContainer": [
{
"path": "/usr/bin/mount-hook",
"args": ["-mount", "arg1", "arg2"],
"env": [ "key1=value1"]
}
],
"startContainer": [
{
"path": "/usr/bin/refresh-ldcache"
}
],
"poststart": [
{
"path": "/usr/bin/notify-start",
"timeout": 5
}
],
"poststop": [
{
"path": "/usr/sbin/cleanup.sh",
"args": ["cleanup.sh", "-f"]
}
]
}
Annotations
Annotation (object,OPTIONAL)包含容器的任意元数据。此信息可以是结构化的,也可以是非结构化的。注释必须是键值映射。如果没有注释,那么这个属性可能不存在或者是一个空映射。
- 键必须是字符串,但不能是空字符串;
- 值必须是字符串,可以是空字符串。
"annotations": {
"com.example.gpu-cores": "2"
}
配置模式示例(config.json)
{
"ociVersion": "1.0.1",
"process": {
"terminal": true,
"user": {
"uid": 1,
"gid": 1,
"additionalGids": [
5,
6
]
},
"args": [
"sh"
],
"env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"TERM=xterm"
],
"cwd": "/",
"capabilities": {
"bounding": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"permitted": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"inheritable": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"effective": [
"CAP_AUDIT_WRITE",
"CAP_KILL"
],
"ambient": [
"CAP_NET_BIND_SERVICE"
]
},
"rlimits": [
{
"type": "RLIMIT_CORE",
"hard": 1024,
"soft": 1024
},
{
"type": "RLIMIT_NOFILE",
"hard": 1024,
"soft": 1024
}
],
"apparmorProfile": "acme_secure_profile",
"oomScoreAdj": 100,
"selinuxLabel": "system_u:system_r:svirt_lxc_net_t:s0:c124,c675",
"noNewPrivileges": true
},
"root": {
"path": "rootfs",
"readonly": true
},
"hostname": "slartibartfast",
"mounts": [
{
"destination": "/proc",
"type": "proc",
"source": "proc"
},
{
"destination": "/dev",
"type": "tmpfs",
"source": "tmpfs",
"options": [
"nosuid",
"strictatime",
"mode=755",
"size=65536k"
]
},
{
"destination": "/dev/pts",
"type": "devpts",
"source": "devpts",
"options": [
"nosuid",
"noexec",
"newinstance",
"ptmxmode=0666",
"mode=0620",
"gid=5"
]
},
{
"destination": "/dev/shm",
"type": "tmpfs",
"source": "shm",
"options": [
"nosuid",
"noexec",
"nodev",
"mode=1777",
"size=65536k"
]
},
{
"destination": "/dev/mqueue",
"type": "mqueue",
"source": "mqueue",
"options": [
"nosuid",
"noexec",
"nodev"
]
},
{
"destination": "/sys",
"type": "sysfs",
"source": "sysfs",
"options": [
"nosuid",
"noexec",
"nodev"
]
},
{
"destination": "/sys/fs/cgroup",
"type": "cgroup",
"source": "cgroup",
"options": [
"nosuid",
"noexec",
"nodev",
"relatime",
"ro"
]
}
],
"hooks": {
"prestart": [
{
"path": "/usr/bin/fix-mounts",
"args": [
"fix-mounts",
"arg1",
"arg2"
],
"env": [
"key1=value1"
]
},
{
"path": "/usr/bin/setup-network"
}
],
"poststart": [
{
"path": "/usr/bin/notify-start",
"timeout": 5
}
],
"poststop": [
{
"path": "/usr/sbin/cleanup.sh",
"args": [
"cleanup.sh",
"-f"
]
}
]
},
"linux": {
"devices": [
{
"path": "/dev/fuse",
"type": "c",
"major": 10,
"minor": 229,
"fileMode": 438,
"uid": 0,
"gid": 0
},
{
"path": "/dev/sda",
"type": "b",
"major": 8,
"minor": 0,
"fileMode": 432,
"uid": 0,
"gid": 0
}
],
"uidMappings": [
{
"containerID": 0,
"hostID": 1000,
"size": 32000
}
],
"gidMappings": [
{
"containerID": 0,
"hostID": 1000,
"size": 32000
}
],
"sysctl": {
"net.ipv4.ip_forward": "1",
"net.core.somaxconn": "256"
},
"cgroupsPath": "/myRuntime/myContainer",
"resources": {
"network": {
"classID": 1048577,
"priorities": [
{
"name": "eth0",
"priority": 500
},
{
"name": "eth1",
"priority": 1000
}
]
},
"pids": {
"limit": 32771
},
"hugepageLimits": [
{
"pageSize": "2MB",
"limit": 9223372036854772000
},
{
"pageSize": "64KB",
"limit": 1000000
}
],
"memory": {
"limit": 536870912,
"reservation": 536870912,
"swap": 536870912,
"kernel": -1,
"kernelTCP": -1,
"swappiness": 0,
"disableOOMKiller": false
},
"cpu": {
"shares": 1024,
"quota": 1000000,
"period": 500000,
"realtimeRuntime": 950000,
"realtimePeriod": 1000000,
"cpus": "2-3",
"mems": "0-7"
},
"devices": [
{
"allow": false,
"access": "rwm"
},
{
"allow": true,
"type": "c",
"major": 10,
"minor": 229,
"access": "rw"
},
{
"allow": true,
"type": "b",
"major": 8,
"minor": 0,
"access": "r"
}
],
"blockIO": {
"weight": 10,
"leafWeight": 10,
"weightDevice": [
{
"major": 8,
"minor": 0,
"weight": 500,
"leafWeight": 300
},
{
"major": 8,
"minor": 16,
"weight": 500
}
],
"throttleReadBpsDevice": [
{
"major": 8,
"minor": 0,
"rate": 600
}
],
"throttleWriteIOPSDevice": [
{
"major": 8,
"minor": 16,
"rate": 300
}
]
}
},
"rootfsPropagation": "slave",
"seccomp": {
"defaultAction": "SCMP_ACT_ALLOW",
"architectures": [
"SCMP_ARCH_X86",
"SCMP_ARCH_X32"
],
"syscalls": [
{
"names": [
"getcwd",
"chmod"
],
"action": "SCMP_ACT_ERRNO"
}
]
},
"namespaces": [
{
"type": "pid"
},
{
"type": "network"
},
{
"type": "ipc"
},
{
"type": "uts"
},
{
"type": "mount"
},
{
"type": "user"
},
{
"type": "cgroup"
}
],
"maskedPaths": [
"/proc/kcore",
"/proc/latency_stats",
"/proc/timer_stats",
"/proc/sched_debug"
],
"readonlyPaths": [
"/proc/asound",
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
],
"mountLabel": "system_u:object_r:svirt_sandbox_file_t:s0:c715,c811"
},
"annotations": {
"com.example.key1": "value1",
"com.example.key2": "value2"
}
}