OCI Image Format Spec 解读
在 OCI
标准中,为描述 OCI Image
规范包含了 image manifest
,image index
(optional),filesystem layers
集合以及 configuration
配置。通过如上描述使得对于 image
在异构场景下能够被构建、传输且可执行变的通用化。
站在上层应用的视角, image manifest
包含内容可寻址且解压可运行的filesystem layer
。而 image configuration
包含例如应用参数、环境变量等。image index
则是对系列 manifest
和 descriptors
的描述指向,用于对不同镜像的补充,一般来说是对不同架构或者不同属性的描述。
Specification
解读
OCI Image Media Types
OCI Image Media Types
定义了如下的格式:
application/vnd.oci.descriptor.v1+json
:Content Descriptor
application/vnd.oci.layout.header.v1+json
:OCI Layout
application/vnd.oci.image.index.v1+json
:Image Index
application/vnd.oci.image.manifest.v1+json
:Image manifest
application/vnd.oci.image.config.v1+json
:Image config
application/vnd.oci.image.layer.v1.tar
:"Layer"
, 以tar
方式压缩application/vnd.oci.image.layer.v1.tar+gzip
:"Layer"
, 以gzip
方式压缩application/vnd.oci.image.layer.v1.tar+zstd
:"Layer"
, 以zstd
方式压缩application/vnd.oci.scratch.v1+json
:Scratch blob
application/vnd.oci.artifact.manifest.v1+json
:Artifact manifest
而如下 media types
将被剔除且不建议在未来的版本中使用:
application/vnd.oci.image.layer.nondistributable.v1.tar
application/vnd.oci.image.layer.nondistributable.v1.tar+gzip
application/vnd.oci.image.layer.nondistributable.v1.tar+zstd
对于 media type
的明细配置矩阵可以参考 Compatibility Matrix
。
各 Media Type
关联关系
通过 Descriptors
描述其中关联关系。其中 image-index
可以理解为是 **fat-manifest
**,是对目标架构平台的 image manifests
的关联描述入口。其中 image manifest
则是对具体 image configuration
和众多 layers
的描述关联入口。
spec
中上层组件描述
Image Manifest
构建容器镜像的一种文档描述,其作用有如下三点:
- 用于构建可内容寻址的镜像,镜像模型中包含可哈希获取镜像配置及其组件
- 通过
fat-manifest
来维护各异构平台支持的镜像 - 可转换位
OCI
运行时规范
属性介绍
image index
主要是一系列架构平台的信息描述,而 image manifest
则是维护具体镜像配置,以及在具体 operating system
的镜像的系列 layers
的信息。
shcemaVersion
| int | REQUIREDmediaType
| string | REQUIREDartifactType
| string | OPTIONALconfig
|descriptor
| REQUIREDmediaType
| string
layers
| array of objects
其中元素必须是descriptor
,且必须拥有一个入口 entry。subject
|descriptor
| OPTIONALannotation
| string-string-map | OPTIONAL
Image Manifest
示例
1 | { |
**Note: mediaType
必须与所包含的 digest
相对应,比如当 digest
是通过 ScratchDigestSHA256
生成,那么 media type “必须” 配置为 application/vnd/oci.scratch.v1+json
**。
1 | { |
OCI Image Index
解读
image index 的配置设置是作为 image manifests 的上层引用。
具体属性解读
schemaVersion
| int | REQUIREDmediaType
| stringmanifests
| array of objects | REQUIREDmanifests
包含系列 带有如下属性的descriptor properties
mediaType
| stringplatform
| obeject | OPTIONAL
当具体platform
被提供时,需要进行如下运行时资源声明architecture
| string | REQUIREDos
| string | REQUIREDos.version
| string | OPTIONALos.features
| array of strings | OPTIONALvariant
| string | OPTIONALfeatures
| array of strings
annotations
| string-string-map | OPTIONAL
Platform Variants
ISA/ABI | architecture | variant |
---|---|---|
ARM | 32-bit, v6 | arm |
ARM | 32-bit, v7 | arm |
ARM | 32-bit, v8 | arm |
ARM | 64-bit, v8 | arm64 |
OCI Image Index
示例
- simple image index with two platforms
1 | { |
- image index with multiple media types
1 | { |
OCI Image Layout Specification 解读
通过 image layout 及 ref 即可构建一个 OCI Runtime Specification bundle:
- 通过 image index 查询 manifest
- 通过定义的 layers 使用 filesystem layers
- 通过
image-confing.json
转换为 OCI Runtime Specification
内容解读
image layout 包含如下内容:
blogs
目录:- content-addressable blobs
- blog has no schema
- 目录必须存在但可能为空
oci-layout
文件- 必须存在
- 必须为 JSON 对象
- 必须包含
imageLayoutVersion
字段 - 可能还包含其他字段
index.json
文件- 必须存在
- 必须为 image index 对象
示例
1 | $ cd example.com/app/ |
Blogs 解读
blogs
的目录中包含由SHA算法生成的各个包含实际内容的子目录组织而成。blog/<alg>/<encoded>
必须与digest <alg>:<encoded>
相匹配,比如 contentblobs/sha256/da39a3ee5e6b4b0d3255bfef95601890afd80709
必须与 digestsha256:da39a3ee5e6b4b0d3255bfef95601890afd80709
相匹配.
示例
1 | $ cat ./blobs/sha256/9b97579de92b1c195b85bb42a11011378ee549b02d7fe9c17bf2a6b35d5cb079 | jq |
1 | $ cat ./blobs/sha256/afff3924849e458c5ef237db5f89539274d5e609db5db935ed3959c90f1f2d51 | jq |
1 | $ cat ./blobs/sha256/5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270 | jq |
oci-layout file
示例
1 | { |
index.json file
示例
1 | { |
Image Layer Filesystem Changeset
+gzip Media Types
application/vnd.oci.image.layer.v1.tar+gzip
application/vnd.oci.image.layer.nondistributable.v1.tar+gzip
+zstd Media Types
application/vnd.oci.image.layer.v1.tar+zstd
application/vnd.oci.image.layer.nondistributable.v1.tar
Change Types
changes 类型:
- Additions
- Modifications
- Removals
其中 Additions 和 Modifications 在改变集中是相同的表现,而 Removals 则通过 “whiteout” 标识。
File Types
- regular files
- directories
- sockets
- symbolic links
- block devices
- character devices
- FIFOs
File Attributes
Additions 和 Modifications 必须包含额属性:
- Modification Time(
mtime
) - User ID(
uid
)- User Name(
uname
)
- User Name(
- Group ID(
gid
)- Group Name(
gname
)
- Group Name(
- Mode(
mode
) - Extended Attributes(
xattrs
) - Symlink reference(
linkname
+ symbolic link type) - Hardlink reference(
linkname
)
Creating
Initial Root Filesystem
initial root 作为基础或者父 layer。如下示例只是为了配合演示,其中的 root filesystem 只是一个初始化状态,为一个空目录。rootfs_c9d_v1/
Populate Initial Filesystem
目录和文件创建:
1 | rootfs-c9d-v1/ |
rootfs-c9d_v1/
归档为 tar 包,包含如下内容:
1 | ./ |
Populate a Comparison Filesystem
创建一个新的目录,其中初始化内容为 rootfs-c9d_v1/
。能够保留文件属性的示例命令如下:
- cp(1): cp -a rootfs-c9d-v1/ rootfs-c9d-v1.s1/
- rsync(1): rsync -aHAX rootfs-c9d-v1/ rootfs-c9d-v1.s1/
- tar(1): mkdir rootfs-c9d-v1.s1 && tar –acls –xattrs -C rootfs-c9d-v1/ -c . | tar -C rootfs-c9d-v1.s1/ –acls –xattrs -x (including –selinux where supported)
对 snapshot 的任何改变都不能改变或影响它所复制的目录。
如上 rootfs-c9d-v1.s1
作为 rootfs-c9d-v1
一个相同的快照,其将作为更新和改变做好准备。
Note: 写时复制或者联合文件系统可以高效地处理目录 snapshots
1 | rootfs-c9d-v1.s1/ |
在此演示中,向 /etc/my-app.d
中添加一个默认的配置文件,同时移除现有的配置文件。另外对 ./bin/my-app-tools
的二进制文件做出改变(文件属性或者文件内容)来演示 layout
变化,变化后内容如下:
1 | rootfs-c9d-v1.s1/ |
Determining Changes
当两目录进行比对时,相对路径根目录是顶层目录,查找哪些内容被添加、修改或者删除。
上述演示比对结果如下:
1 | Added: /etc/my-app.d/ |
Representing Changes
tar 归档文件将被创建且其中只会包含更改的文件集
其中 rootfs-c9d-v1.s1
显示如下:
1 | ./etc/my-app.d/ |
需要注意的是当更改集生效的时候需要保证 ./etc/my-app-config
已经被删除了,其文件前缀为 .wh.
。
OCI Image Configuration
OCI image 是对 root filesystem 变化的有序集合且其中包含相应执行参数,可供容器运行使用。
关键术语
Layer
- 镜像文件系统由 layers 构成
- 每个 layer 对代表一组文件系统的变化,其格式为 tar 包,记录着文件的添加、修改或者删除
- layers 中并不包含配置的元数据,比如环境变量或者参数,这些将作为一个完整镜像时的属性,而不是在每个layer中有体现
- 使用基于层的或联合的文件系统,如AUFS,或通过计算文件系统快照的差异,文件系统的变化集可以被用来呈现一系列的image layers,就像它们是一个完整的文件系统
Image JSON
- 每个镜像将有一个关联的 JSON 结构体用来描述一些关于镜像的基础信息,如创建日期、作者,同时也可以包含一些运行时的配置例如执行入口、默认参数、网络和volume卷
- JSON 描述包含对每个 layer 加密哈希后的引用,用于对该镜像的历史信息的维护
- JSON 应该是不可变的
- 改变的话应该意味着一个新的派生镜像,而不是在原有的镜像上做出改变
Layer DiffID
layer 的 DiffID 是该层未压缩的 tar 归档的摘要,并以描述符摘要的格式进行序列化。
Layer ChainID
为方便起见,有时用一个标识符来指代一叠加的 layer 是很有用的。
ImageID
每个镜像的的ID是由其配置JSON的SHA256哈希值给出的。
属性
- created | string | OPTIONAL
- author | string | OPTIONAL
- architecture | string | REQUIRED
- os | string | REQUIRED
- os.version | string | OPTIONAL
- os.features | *array of strings | OPTIONAL
- variant | string | OPTIONAL
- config | object | OPTIONAL
- User | string | OPTIONAL
- ExposedPorts | object | OPTIONAL
- Env | array of strings | OPTIONAL
- Entrypoint | array of strings | OPTIONAL
- Cmd | array of strings | OPTIONAL
- Volume | object | OPTIONAL
- WorkingDir | string | OPTIONAL
- Labels | object | OPTIONAL
- StopSignal | string | OPTIONAL
- Memory | integer | OPTIONAL
- CpuShares | integer | OPTIONAL
- Healthcheck | object | OPTIONAL
- rootfs | object | REQUIRED
- created | string | OPTIONAL
- author | string | OPTIONAL
- created_by | string | OPTIONAL
- comment | string | OPTIONAL
- empty_layer | boolean | OPTIONAL
示例
1 | { |
Conversion to OCI Runtime Configuration
- 从 filesystem layers 提取 root filesystem
- 将 image configuration blob 转换为 OCI Runtime configuration blob
属性比对表
Image Field | Runtime Field | Notes |
---|---|---|
Config.WorkingDir | process.cwd | |
Config.Env | process.env | 1 |
Config.Entrypoint | process.args | 2 |
Config.Cmd | process.args | 2 |
Annotations Fields
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 |
Parsed Fields
Image Field | Runtime Field |
---|---|
Config.User | process.user.* |
Optional Fields
Image Field | Runtime Field | Notes |
---|---|---|
Config.ExposedPorts | annotations | 1 |
Config.Volumes | mounts | 2 |
Annotations
关于注解的三种模式
Config.Labels
-> configurationannotations
-> manifestannotations
-> image index
Descriptor
属性
- mediaType | string | REQUIRED
- digest | string | REQUIRED
- size | int64 | REQUIRED
- urls | array of strings | OPTIONAL
- annotations | string-string-map | OPTIONAL
- data | string | OPTIONAL
- artifactType | string | OPTIONAL
Digests 摘要
摘要命名规范
1 | digest ::= algorithm ":" encoded |
Digest 计算规则
1 | let ID(C) = Descriptor.digest |
其中 H 为具体 哈希算法
已注册的算法
algorithm identifier | algorithm |
---|---|
sha256 | sha-256 |
SHA512 | sha-512 |
示例
- 包含基础信息的 Manifest
1 | { |
- 带有指定 url 的 Manifest
1 | { |
- 带有 artifact 的 manifest
1 | { |
最后的话
其实个人把此作为 containerd 源码分析的番外篇,但是对于 OCI Image 部分的理解还是很有必要的,除了 containerd 相关的 OCI Interface 的定义,下沉到更加底层的操作,其实就是对此标准的封装。