使用 OCI 容器注册表

什么是 OCI 容器注册表?

OCI 容器注册表是 Docker 容器注册表的演进,其中构成容器的层可以作为单独的数据文件存储,然后可以检索这些层以重建为正在运行的容器。OCI 是 “开放容器倡议” 的缩写,您可以在 opencontainers.org 了解更多关于 OCI 的信息。

容器注册表正在演进以支持存储其他数据文件,并且在此上下文中,重点是演进以支持将这些额外文件存储在容器注册表中并由其提供服务。

警告

此功能应被视为实验性的。

概述

Ironic 的一个最新添加功能是从 OCI 容器注册表中检索工件。此支持建模为 Ironic 部署可以同时用于磁盘镜像和用于部署的基础工件,例如内核和 ramdisk。适用不同的规则,因此请仔细阅读以下几个部分。

目前,此功能仅适用于直接与 Ironic 交互的用户。Nova 的数据模型与当前使用 Glance 兼容,但这在未来可能会改变。

工作原理

OCI 注册表具有分层数据模型,可以分为三个概念层。

  • 工件索引 - 高级列表,指向清单并包含诸如注释、平台、架构等信息。

  • 清单 - 包含与 blob(大型二进制对象)数据以及查找 blob 数据的位置相关的较低级别细节的中间结构位置。当引用容器镜像时,此清单包含构成容器的多个“层”的信息。

  • Blob 数据 - 实际的工件,只能使用清单中提供的数据进行检索。

Ironic 具有一个单独的镜像服务客户端,该客户端将 OCI 样式的容器 URL 转换为 image_source 值,格式如下 oci://host/user/container:tag。当仅定义了标签时,可以将其视为包含许多工件的特定“标记”视图,Ironic 将搜索这些工件以找到最佳匹配项。

匹配是根据配置的 image_download_source 来执行的,尝试根据文件类型进行加权偏好,例如,如果值为 local,则优先选择 qcow2 磁盘镜像。否则,优先选择 raw。这使用 disktype 注释,其中 qcow2qemu 被认为是 QCOW2 格式的镜像。清单上的 disktype 注释为 rawapplehv,被认为是 raw 磁盘镜像。在适当加权文件类型后,代码尝试将裸机节点的 CPU 架构与远程注册表中列出的平台 architecture 匹配。完成文件识别过程后,Ironic 会自动更新 image_source 值,以匹配远程容器注册表中的工件。

注意

该代码会自动尝试处理观察到的架构命名差异,例如,x86_64 有时被称为 amd64,而 aarch64 有时被称为 arm64

警告

image_download_source 设置为 swift 与此镜像服务不兼容。仅支持 localhttp

当 URL 具体且指向特定的清单时,例如 oci://host/user/container@sha256:f00b...,Ironic 只能从容器注册表中检索该特定文件。由于数据模型,我们还无法了解有关该镜像的其他详细信息,例如注释,因为注释是构成指向清单的结构数据的一部分。

使用容器注册表的另一个优势是,根据容器注册表提供的元数据,已确认传输过程中的校验和。例如,当使用清单 URL 时,URL 的摘要部分用于校验返回的内容,并且该清单随后包含用于标识从哪里下载工件的 URL 的工件摘要值。

身份验证

身份验证是 OCI 镜像注册表用户的重要主题。

虽然一些公共注册表对提供下载访问权限非常友好,但其他注册表可能具有严格的配额,需要用户进行身份验证才能下载工件。此外,私有镜像注册表可能需要对任何访问进行身份验证。

因此,有三种可用的配置路径

  • 节点 instance_info 值设置为 image_pull_secret。此值可用于检索镜像工件,但不适用于提取用作部署过程一部分的内核或 ramdisk 等其他工件。与所有其他 instance_info 字段值一样,此值在节点取消配置后将被删除。使用此字段的方式是提供预共享密钥令牌值。这是您通常在 Docker config.json 文件 auth 字段中用于访问的顶级域的值。

  • 节点 driver_info 值设置为 image_pull_secret。此设置类似于 instance_info 设置,但可由裸机节点的管理员用于定义用于该节点的特定注册表凭据。

  • oci.authentication_config,允许为 Conductor 进程范围内的预共享密钥配置。可以将此配置值设置为解析容器工具用于存储身份验证详细信息的常见身份验证配置格式的文件。仅当未定义要使用的特定密钥时才咨询此值,并且旨在与 Docker config.json 用于存储身份验证详细信息的格式兼容。

配置文件的示例如下所示。

{
  "auths": {
    "quay.io": {
      "auth": "<pull_secret_here>"
    },
    "private-registry.tld": {
      "auth": "<pull_secret_here>"
    }
  }
}

注意

由于 Ironic 的密钥值清理,image_pull_secret 值在 API 表面上不可见,而是显示为 ‘**’。

注意

如果您需要从 config.json 文件中提取 pull secret,可以使用类似于 jq ‘.auths.”domain.tld”.auth’ config.jsonjq 命令,这将返回您随后可以填充的带引号的字符串。其他基于命令行的方法也存在,供用户在完成对容器平台的登录后检索此类值,这意味着您可以根据需要使用相同的令牌值。

可用 URL 格式

以下 URL 格式可用于下载磁盘镜像工件。当提供非精确的清单 URL 时,Ironic 将尝试识别和匹配工件。非磁盘镜像的工件的 URL 需要具体且指向特定的清单。

注意

如果未定义标签,将尝试标签 latest,但是,如果在容器注册表返回的可用标签列表中未找到该标签,Ironic 将引发 ImageNotFound 错误。

  • oci://host/path/container - 在这种情况下,Ironic 假定 ‘latest’ 是所需的标签。

  • oci://host/path/container:tag - Ironic 根据定义的标签提供的视图发现工件。

  • oci://host/path/container@sha256:f00f - 这是一个定义特定清单的 URL。如果这是一个容器,这将是一个包含许多层以构成容器的清单文件,但对于一个工件,只有单个文件由该清单表示,并且我们检索该特定文件。

警告

使用标签值访问工件,例如 deploy_kerneldeploy_ramdisk,是不可能的。这是一个有意限制,将来可能会在 Ironic 的版本中解决。

已知限制

  • 对于磁盘镜像的使用,仅支持整个磁盘镜像。Ironic 不打算使用此镜像服务支持分区镜像。

  • IPA 不了解远程容器注册表,也不了解对远程注册表的身份验证。预计将在 Ironic 的未来版本中解决此问题。

  • 某些工件可能使用 Zstandard 进行压缩。只有通过 Conductor 传输的磁盘镜像或工件才能正确解压缩。不幸的是,IPA 无法在流式传输内容时动态解压缩此类工件。

  • 对容器镜像注册表的身份验证通过使用预共享令牌密钥可用。

  • 在某些符合 OCI 标准的镜像注册表上,使用标签可能不可行。这可能会导致在尝试解析标签时引发 ImageNotFound 错误。

  • 用户身份验证目前仅限于使用 bearer token,并且仅支持“pull secret” 样式的身份验证。如果需要基本身份验证,请在 Ironic Launchpad 上提交错误报告。

如何将文件上传到我自己的注册表?

虽然有几种不同的方法可以做到这一点,但最简单的方法是利用名为 ORAS 的工具。您可以在 https://oras.land 了解有关 ORAS 的更多信息

ORAS 实用程序能够将任意工件上传到容器注册表,并附带所需的清单然后关联一个标签以便于人工参考。虽然 OCI 数据模型确实乐于支持一个标签位于许多清单前面的模型,但 ORAS 不支持。在 ORAS 模型中,一个标签与一个工件相关联。

在下面的示例中,您可以了解如何实现这一点。请小心,这些示例不是您可以直接剪切和粘贴的命令,而是旨在演示所需的步骤并分享构建工件 URL 的概念。

注意

这些示例命令行可能因您的远程注册表和底层配置而略有不同,因此省略了凭据设置。

作为第一步,我们将演示上传 IPA Ramdisk 内核。

$ export HOST=my-container-host.domain.tld
$ export CONTAINER=my-project/my-container
$ oras push ${HOST}/${CONTAINER}:ipa_kernel tinyipa-master.vmlinuz
✓ Exists    tinyipa-master.vmlinuz                         5.65/5.65 MB 100.00%     0s
  └─ sha256:15ed5220a397e6960a9ac6f770a07e3cc209c6870c42cbf8f388aa409d11ea71
✓ Exists    application/vnd.oci.empty.v1+json                    2/2  B 100.00%     0s
  └─ sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
✓ Uploaded  application/vnd.oci.image.manifest.v1+json       606/606  B 100.00%     0s
  └─ sha256:2d408348dd6ff2e26efc1de03616ca91d76936a27028061bc314289cecdc895f
Pushed [registry] my-container-host.domain.tld/my-project/my-container:ipa_kernel
ArtifactType: application/vnd.unknown.artifact.v1
Digest: sha256:2d408348dd6ff2e26efc1de03616ca91d76936a27028061bc314289cecdc895f
$
$ export MY_IPA_KERNEL=oci://${HOST}/${CONTAINER}:@sha256:2d408348dd6ff2e26efc1de03616ca91d76936a27028061bc314289cecdc895f

如您从本示例中看到,我们执行了该命令并上传了该文件。需要强调的重要方面是在最后报告的摘要。这是您可以用来生成 URL 的清单摘要。

警告

在构建您自己的环境变量时,特别是使用摘要值时,请记住您需要使用您自己的上传的摘要值,而不是来自示例的摘要值。

$ oras push ${HOST}/${CONTAINER}:ipa_ramdisk tinyipa-master.gz
✓ Exists    tinyipa-master.gz                              91.9/91.9 MB 100.00%     0s
  └─ sha256:0d92eeb98483f06111a352b673d588b1aab3efc03690c1553ef8fd8acdde15fc
✓ Exists    application/vnd.oci.empty.v1+json                    2/2  B 100.00%     0s
  └─ sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
✓ Uploaded  application/vnd.oci.image.manifest.v1+json       602/602  B 100.00%     0s
  └─ sha256:b17e53ff83539dd6d49e714b09eeb3bd0a9bb7eee2ba8716f6819f2f6ceaad13
Pushed [registry] my-container-host.domain.tld/my-project/my-container:ipa_ramdisk
ArtifactType: application/vnd.unknown.artifact.v1
Digest: sha256:b17e53ff83539dd6d49e714b09eeb3bd0a9bb7eee2ba8716f6819f2f6ceaad13
$
$ export MY_IPA_RAMDISK=oci://${HOST}/${CONTAINER}:@sha256:b17e53ff83539dd6d49e714b09eeb3bd0a9bb7eee2ba8716f6819f2f6ceaad13

提醒您,请记住使用不同的标签与 ORAS。

例如,您可以通过执行以下命令查看远程注册表中的当前标签。

$ oras repo tags --insecure $HOST/project/container
ipa_kernel
ipa_ramdisk
unrelated_item
$

现在您已成功上传 IPA 内核和 ramdisk,剩下的项目是磁盘镜像。在下面的示例中,我们正在生成基于容器标签的 URL 以及直接清单摘要 URL。

注意

下面的示例设置了清单注释 disktype 和工件平台。虽然不是必需的,但如果您允许 Ironic 使用容器标签而不是摘要 URL 来解析磁盘镜像,则建议这样做。

$ oras push -a disktype=qcow2 --artifact-platform linux/x86_64 $HOST/$CONTAINER:cirros-0.6.3 ./cirros-0.6.3-x86_64-disk.img
✓ Exists    cirros-0.6.3-x86_64-disk.img                   20.7/20.7 MB 100.00%     0s
  └─ sha256:7d6355852aeb6dbcd191bcda7cd74f1536cfe5cbf8a10495a7283a8396e4b75b
✓ Uploaded  application/vnd.oci.image.config.v1+json           38/38  B 100.00%   43ms
  └─ sha256:369358945e345b86304b802b704a7809f98ccbda56b0a459a269077169a0ac5a
✓ Uploaded  application/vnd.oci.image.manifest.v1+json       626/626  B 100.00%     0s
  └─ sha256:0a175cf13c651f44750d6a5cf0cf2f75d933bd591315d77e19105e5446b73a86
Pushed [registry] my-container-host.domain.tld/my-project/my-container:cirros-0.6.3
ArtifactType: application/vnd.unknown.artifact.v1
Digest: sha256:0a175cf13c651f44750d6a5cf0cf2f75d933bd591315d77e19105e5446b73a86
$ export MY_DISK_IMAGE_TAG_URL=oci://${HOST}/${CONTAINER}:cirros-0.6.3
$ export MY_DISK_IMAGE_DIGEST_URL=oci://${HOST}/${CONTAINER}@sha256:0a175cf13c651f44750d6a5cf0cf2f75d933bd591315d77e19105e5446b73a86