containerd 的主要目标是创建一个系统,在这里 content 可以被用来执行 containers。为了执行在流上执行,containerd 需要 content 并且管理它。
这篇文章描述了 content 在 containerd 中是如何流动的,如果被管理的,它在哪里存在。我们使用镜像 docker.io/library/redis:5.0.9 作为例子来探索 content flow。
Content Areas
Content 存在于 containerd 生命周期的很多区域:
- OCI registry,例如 hub.docker.com 和 quay.io
- Containerd content 存储,在 containerd 的本地存储区域,例如标准的 linux 安装情况下,它会在
/var/lib/containerd/io.containerd.content.v1.content
- snapshots,在 containerd 的本地存储区域,例如标准的 Linux 安装情况下
/var/lib/containerd/io.containerd.snapshotter.v1.<type>
,对于一个 overlayfs snapshotter,它将会在/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs
创建一个容器的过程中,会发生下面这些:
- 镜像和他的 content 必须被载入到 content 存储中,这通常在从 OCI registry 下载镜像时发生,但是也可指直接载入 content
- 被提交的 snapshots 必须从镜像中的每一层 content 创建
- 一个活跃的 snapshot 必须在镜像的最后一层 content 的顶部被创建
一个容器现在被创建,它的 root 文件系统作为活跃的 snapshot
这篇文档剩余的部分会详细介绍在每一个区域中的 content和他们如何与其他的关联上的。
Image Format
在 registry 中的镜像通常以下面的格式存在,一个 “image” 包含一个作为 descriptor 的 JSON 文档。这个 descriptor 中有一个参数 mediaType
,这个参数告诉我们它是那个类型,这里有两种选项:
- manifest:列出这个运行将这个镜像运行为 container 的配置文件和为镜像创建文件系统的二进制数据的 hash 值
- index:列出各个平台的 manifests 的哈希值,每一个平台都包含架构(e.g. amd64 和 arm64)和操作系统(e.g. linux)
index 的目的是允许选择匹配目标平台的 manifest
为了将一个镜像从 registry 转移到实际的硬盘存储:
- 取到镜像的 descriptor (JSON 文件)
- 根据
mediaType
判断这个 descriptor 是 manifest 或者是 index- 如果 descriptor 是 index,找到想要运行 container 的对应平台,使用 hash 来找到 manifest
- 如果是 manifest,继续下一步
- 对于 manifest 中的每一个项目(配置和数据层),使用 hash 列表来获取组件并且保存他们
我们使用我们的例子 redis:5.0.9
,来说明一下这个流程
当我们第一次解析 redis:5.0.9
,会获取到下面的 JSON 文档
{
"manifests": [
{
"digest": "sha256:a5aae2581826d13e906ff5c961d4c2817a9b96c334fd97b072d976990384156a",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "amd64",
"os": "linux"
},
"size": 1572
},
{
"digest": "sha256:4ff8940144391ecd5e1632d0c427d95f4a8d2bb4a72b7e3898733352350d9ab3",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v5"
},
"size": 1573
},
{
"digest": "sha256:ce541c3e2570b5a05d40e7fc01f87fc1222a701c81f95e7e6f2ef6df1c6e25e7",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v7"
},
"size": 1573
},
{
"digest": "sha256:535ee258100feeeb525d4793c16c7e58147c105231d7d05ffc9c84b56750f233",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm64",
"os": "linux",
"variant": "v8"
},
"size": 1573
},
{
"digest": "sha256:0f3b047f2789547c58634ce88d71c7856999b2afc8b859b7adb5657043984b26",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "386",
"os": "linux"
},
"size": 1572
},
{
"digest": "sha256:bfc45f499a9393aef091057f3d067ff7129ae9fb30d9f31054bafe96ca30b8d6",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "mips64le",
"os": "linux"
},
"size": 1572
},
{
"digest": "sha256:3198e1f1707d977939154a57918d360a172c575bddeac875cb26ca6f4d30dc1c",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "ppc64le",
"os": "linux"
},
"size": 1573
},
{
"digest": "sha256:24a15cc9366e1557db079a987e63b98a5abf4dee4356a096442f53ddc8b9c7e9",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "s390x",
"os": "linux"
},
"size": 1573
}
],
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"schemaVersion": 2
}
在 descriptor 的最下面,显示了 mediaType
为 “manifest.list”,用 OCI 术语就是 index。它有一个 数组 field 叫做 manifests
,列表中的每一项都是一个 platform 和这个平台所对应的 manifest 的 hash。”platform” 是 “architecture” 和 “os” 的结合。因为我们会运行在 linux 和 amd64 生,所以需要找到一个 platform
像下面这样的 manifests
。
"platform": {
"architecture": "amd64",
"os": "linux"
}
这是列表中的第一个, 它的 hash 是 sha256:a5aae2581826d13e906ff5c961d4c2817a9b96c334fd97b072d976990384156a
然后我们使用 hash 获取这个项目, docker.io/library/redis@sha256:a5aae2581826d13e906ff5c961d4c2817a9b96c334fd97b072d976990384156a
,这会给我们这个 image 在 linux/amd64 上的 manifest.
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"size": 6836,
"digest": "sha256:df57482065789980ee9445b1dd79ab1b7b3d1dc26b6867d94470af969a64c8e6"
},
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 27098147,
"digest": "sha256:123275d6e508d282237a22fefa5aef822b719a06496444ea89efa65da523fc4b"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 1730,
"digest": "sha256:f2edbd6a658e04d559c1bec36d838006bbdcb39d8fb9033ed43d2014ac497774"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 1417708,
"digest": "sha256:66960bede47c1a193710cf8bfa7bf5f50bc46374260923df1db1c423b52153ac"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 7345094,
"digest": "sha256:79dc0b596c9027416a627a6237bd080ac9d87f92b60f1ce145c566632839bce7"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 99,
"digest": "sha256:de36df38e0b6c0e7f29913c68884a0323207c07cd7c1eba71d5618f525ac2ba6"
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 410,
"digest": "sha256:602cd484ff92015489f7b9cf9cbd77ac392997374b1cc42937773f5bac1ff43b"
}
]
}
mediaType
告诉我们这是一个 manifest
, 它符合下面的格式
config
, hash 是sha256:df57482065789980ee9445b1dd79ab1b7b3d1dc26b6867d94470af969a64c8e6
- 一个或者多个
layers
, 在这个示例中有liuge
这些项目中的每一个 index, manifests, 配置文件和每一层, 都分散的存储在 registry 中, 也被分别下载
Content Store
当 content 被载入到 containerd 的 content 存储中,它存储的方式与 registry 非常相似。每一个组件都存储在以 hash 命名的文件中。
继续我们的 redis 示例,如果使用 client.Pull()
或者 ctr pull
,在 content store 中会得到下面的
sha256:1d0b903e3770c2c3c79961b73a53e963f4fd4b2674c2c4911472e8a054cb5728
- the indexsha256:a5aae2581826d13e906ff5c961d4c2817a9b96c334fd97b072d976990384156a
- the manifest for linux/amd64sha256:df57482065789980ee9445b1dd79ab1b7b3d1dc26b6867d94470af969a64c8e6
- the configsha256:123275d6e508d282237a22fefa5aef822b719a06496444ea89efa65da523fc4b
- layer 0sha256:f2edbd6a658e04d559c1bec36d838006bbdcb39d8fb9033ed43d2014ac497774
- layer 1sha256:66960bede47c1a193710cf8bfa7bf5f50bc46374260923df1db1c423b52153ac
- layer 2sha256:79dc0b596c9027416a627a6237bd080ac9d87f92b60f1ce145c566632839bce7
- layer 3sha256:de36df38e0b6c0e7f29913c68884a0323207c07cd7c1eba71d5618f525ac2ba6
- layer 4sha256:602cd484ff92015489f7b9cf9cbd77ac392997374b1cc42937773f5bac1ff43b
- layer 5
如果查看 content 存储,会看到下面的这些(为了容易阅读经过了过滤和排序)
$ tree /var/lib/containerd/io.containerd.content.v1.content/blobs
/var/lib/containerd/io.containerd.content.v1.content/blobs
└── sha256
├── 1d0b903e3770c2c3c79961b73a53e963f4fd4b2674c2c4911472e8a054cb5728
├── a5aae2581826d13e906ff5c961d4c2817a9b96c334fd97b072d976990384156a
├── df57482065789980ee9445b1dd79ab1b7b3d1dc26b6867d94470af969a64c8e6
├── 123275d6e508d282237a22fefa5aef822b719a06496444ea89efa65da523fc4b
├── f2edbd6a658e04d559c1bec36d838006bbdcb39d8fb9033ed43d2014ac497774
├── 66960bede47c1a193710cf8bfa7bf5f50bc46374260923df1db1c423b52153ac
├── 79dc0b596c9027416a627a6237bd080ac9d87f92b60f1ce145c566632839bce7
├── de36df38e0b6c0e7f29913c68884a0323207c07cd7c1eba71d5618f525ac2ba6
└── 602cd484ff92015489f7b9cf9cbd77ac392997374b1cc42937773f5bac1ff43b
如果我们使用 containerd 接口也能够看到同样的东西(同样是经过处理的)
ctr content ls
DIGEST SIZE AGE LABELS
sha256:1d0b903e3770c2c3c79961b73a53e963f4fd4b2674c2c4911472e8a054cb5728 1.862 kB 6 minutes containerd.io/gc.ref.content.0=sha256:a5aae2581826d13e906ff5c961d4c2817a9b96c334fd97b072d976990384156a,containerd.io/gc.ref.content.1=sha256:4ff8940144391ecd5e1632d0c427d95f4a8d2bb4a72b7e3898733352350d9ab3,containerd.io/gc.ref.content.2=sha256:ce541c3e2570b5a05d40e7fc01f87fc1222a701c81f95e7e6f2ef6df1c6e25e7,containerd.io/gc.ref.content.3=sha256:535ee258100feeeb525d4793c16c7e58147c105231d7d05ffc9c84b56750f233,containerd.io/gc.ref.content.4=sha256:0f3b047f2789547c58634ce88d71c7856999b2afc8b859b7adb5657043984b26,containerd.io/gc.ref.content.5=sha256:bfc45f499a9393aef091057f3d067ff7129ae9fb30d9f31054bafe96ca30b8d6,containerd.io/gc.ref.content.6=sha256:3198e1f1707d977939154a57918d360a172c575bddeac875cb26ca6f4d30dc1c,containerd.io/gc.ref.content.7=sha256:24a15cc9366e1557db079a987e63b98a5abf4dee4356a096442f53ddc8b9c7e9
sha256:a5aae2581826d13e906ff5c961d4c2817a9b96c334fd97b072d976990384156a 1.572 kB 6 minutes containerd.io/gc.ref.content.2=sha256:f2edbd6a658e04d559c1bec36d838006bbdcb39d8fb9033ed43d2014ac497774,containerd.io/gc.ref.content.3=sha256:66960bede47c1a193710cf8bfa7bf5f50bc46374260923df1db1c423b52153ac,containerd.io/gc.ref.content.4=sha256:79dc0b596c9027416a627a6237bd080ac9d87f92b60f1ce145c566632839bce7,containerd.io/gc.ref.content.5=sha256:de36df38e0b6c0e7f29913c68884a0323207c07cd7c1eba71d5618f525ac2ba6,containerd.io/gc.ref.content.6=sha256:602cd484ff92015489f7b9cf9cbd77ac392997374b1cc42937773f5bac1ff43b,containerd.io/gc.ref.content.0=sha256:df57482065789980ee9445b1dd79ab1b7b3d1dc26b6867d94470af969a64c8e6,containerd.io/gc.ref.content.1=sha256:123275d6e508d282237a22fefa5aef822b719a06496444ea89efa65da523fc4b
sha256:df57482065789980ee9445b1dd79ab1b7b3d1dc26b6867d94470af969a64c8e6 6.836 kB 6 minutes containerd.io/gc.ref.snapshot.overlayfs=sha256:87806a591ce894ff5c699c28fe02093d6cdadd6b1ad86819acea05ccb212ff3d
sha256:123275d6e508d282237a22fefa5aef822b719a06496444ea89efa65da523fc4b 27.1 MB 6 minutes containerd.io/uncompressed=sha256:b60e5c3bcef2f42ec42648b3acf7baf6de1fa780ca16d9180f3b4a3f266fe7bc
sha256:f2edbd6a658e04d559c1bec36d838006bbdcb39d8fb9033ed43d2014ac497774 1.73 kB 6 minutes containerd.io/uncompressed=sha256:b5a8df342567aa93d568b263b25c1eaf52655f0952e1911742ffb4f7a521e044
sha256:66960bede47c1a193710cf8bfa7bf5f50bc46374260923df1db1c423b52153ac 1.418 MB 6 minutes containerd.io/uncompressed=sha256:c03c7e9701eb61f1e2232f6d19faa699cd9d346207aaf4f50d84b1e37bbad3e2
sha256:79dc0b596c9027416a627a6237bd080ac9d87f92b60f1ce145c566632839bce7 7.345 MB 6 minutes containerd.io/uncompressed=sha256:367024e4e00618a9ada3203b5922d3186a0aa6136a1c4cbf5ed380171e1afe48
sha256:de36df38e0b6c0e7f29913c68884a0323207c07cd7c1eba71d5618f525ac2ba6 99 B 6 minutes containerd.io/uncompressed=sha256:60ef3ee42de712ef7748cc8e92192e926180b1be6fec9580933f1347fb6b2747
sha256:602cd484ff92015489f7b9cf9cbd77ac392997374b1cc42937773f5bac1ff43b 410 B 6 minutes containerd.io/uncompressed=sha256:bab68e5155b7010010964bf3aadc30e4a9c625701314ff6fa3c143c72f0aeb9c
Labels
content 的每一块都有数个 labels,这个分块是用来介绍 labels 的,但这不是一个对于 labels 的全部概览
Layer Labels
我们以 layers 自身开始,这些都只含有一个 label: containerd.io/uncompressed
,这些文件是 gzipped tar 文件,label 在解压的时候提供 hash 值
你可以这样做获取同样的值
$ cat <file> | gunzip - | sha256sum -
例如:
$ cat /var/lib/containerd/io.containerd.content.v1.content/blobs/sha256/602cd484ff92015489f7b9cf9cbd77ac392997374b1cc42937773f5bac1ff43b | gunzip - | sha256sum -
bab68e5155b7010010964bf3aadc30e4a9c625701314ff6fa3c143c72f0aeb9c
Config Labels
我们有一个单独的 config layer,sha256:df57482065789980ee9445b1dd79ab1b7b3d1dc26b6867d94470af969a64c8e6
, 它有一个以 cpntainerd.io/gc.ref
为前缀的 label,这个标签标示它影响垃圾回收
示例,label 为 containerd.io/gc.ref.snapshot.overlayfs
并且有值 sha256:87806a591ce894ff5c699c28fe02093d6cdadd6b1ad86819acea05ccb212ff3d
这被用来将这个 config 连接到 snapshot,我们一会讨论 snapshot 时,就能看到它。
Manifest Labels
Index Labels
Snapshots
在 content 存储中的 content 时不可变的,但是也有一些格式是不可用的,例如大多数 container layers 是 tar-gzip 格式,不能简单的挂在 tar-gzip 文件,即使可以,我们也希望 content 是不可变的。
为了使用它,我们为 content 创建了 snapshot
流程就像下面这样:
- snapshotter 从 parent 创建 snapshot。在第一层的时候,是空白的,现在这是一个 “active” 的 snapshot