ORAS で OCI アーティファクトを扱う
ORAS とは
The OCI Registry as Storage (ORAS) は OCI Artifact を扱うためのクライアントツールで、 CNCF の Sandbox プロジェクトとなっています。
Docker CLI を使ってローカル環境とリモートのコンテナレジストリ間でコンテナイメージを push/pull できるように、ORAS はローカル環境とリモートのレジストリ間で OCI アーティファクト を push/pull できるように設計されたクライアントツール (CLI) となっています。
ドキュメントに OCI アーティファクトの説明や ORAS コマンドの使用例がいくつか載っていますが、OCI アーティファクトに馴染みがないと一読しただけではどのように使うか分かりづらい部分もあります。そのため、ここでは OCI アーティファクトの説明を混ぜつつ ORAS でどのようなことがことができるか見てみます。
また、私自身 OCI アーティファクトに慣れていない部分もあるので備忘録も兼ねています。
OCI Artifact と OCI Artifact Registry
ORAS Introduction に書いてあることの簡単なまとめ
Introduction では OCI アーティファクトの必要性や経緯について簡単に書いてあります。
まだ OCI アーティファクト仕様が決まってなかった頃は、 Dockerfile でイメージ内に pdf やテキスト、バイナリなどを配置してそのイメージをコンテナレジストリに push することで、イメージ自体に関係ある/なしに関わらず任意のファイルを含めることができました(今もできますが)。
しかしこの方法は正当な方法とは言い難くファイル自身の管理に難がある、イメージに不要なファイルを含むためサイズが肥大するなどのいろいろな問題があったため、より正当な方法で特定のガイドラインに沿った方法でファイル等を共有できるような仕様を定めました。Open Container Initiative (OCI) によって定められたこの仕様を OCI Image Manifest Specification、OCI イメージマニフェストの仕様に準拠して管理されるファイルやメタデータのまとまりを OCI Artifact と呼びます。また、OCI マニフェストを管理できるように設定されたコンテナレジストリのことを OCI Artifact Registry と呼びます。OCI アーティファクトを扱うことができる他のツールも既にありますが、ORAS は OCI アーティファクトに焦点を当ててより容易に管理することを目的に開発されています。
What ORAS does differently is shift the focus from container images to other types of artifacts.
Registries supporting OCI Artifacts に一例がある通り主要なクラウドプロバイダーや OSS のコンテナレジストリでは現時点で OCI アーティファクトをサポートしているため、「OCI Artifact Registry は OCI アーティファクトを管理できるように拡張されたコンテナレジストリを指す」という認識でひとまず問題ないかと思います。
Docker イメージの OCI レイアウトの調査
ORAS で OCI アーティファクトを扱う前に OCI アーティファクトの構造について簡単に見ておきます。Understanding OCI artifacts に OCI アーティファクトの構造を図示したものがあるので下図に引用します。
OCI アーティファクトの構造 (Understanding OCI artifacts より引用)。index, manifest, blob の入れ子構造で構成される。
OCI アーティファクトは上記のように Index, Manifest, Blob の入れ子構成になっています、index と manifest はどのようなアーティファクトが入っているかを表す json 形式のメタデータであり、Blob が実際にアーティファクト内に保存されるファイル等となっています。
とはいえこれだけだと具体的なイメージが湧きづらいので、ここでは dockerhub 上に存在する nginx のイメージを例にとって実際に OCI アーティファクトの構造を見ていきます。
nginx イメージは amd64, arm, s390 など複数のアーキテクチャに対応しているため、docker manifest inspect nginx
でマニフェストを参照するとまず複数のアーキテクチャの manifest list が記載された情報が取得できます。これが OCI アーティファクトにおける Index に対応しており、manifests[]
以下に各アーキテクチャに対応する個々のマニフェストが記載されています。
- mediaType: マニフェストの形式。マニフェスト自体は json 形式となので
+json
となっている。OCI Image Media Types に使用可能な値の一覧がある。 - digest: マニフェストの digest
- size: マニフェストのサイズ (byte)
- platform: os やアーキテクチャの情報が記載されている。
`docker manifest inspect nginx` の結果
schemaVersion: 2
mediaType: application/vnd.oci.image.index.v1+json
manifests:
- mediaType: application/vnd.oci.image.manifest.v1+json
size: 2295
digest: sha256:5f0574409b3add89581b96c68afe9e9c7b284651c3a974b6e8bac46bf95e6b7f
platform:
architecture: amd64
os: linux
- mediaType: application/vnd.oci.image.manifest.v1+json
size: 841
digest: sha256:2b7d6ce2be05018291aaa1979ccf1e744fa7082eea89142faac3dd067607875e
platform:
architecture: unknown
os: unknown
- mediaType: application/vnd.oci.image.manifest.v1+json
size: 2297
digest: sha256:6289301f3b918b98577fe0f80c13761aae5791b0059ed9e1d554edeae7c54b82
platform:
architecture: arm
os: linux
variant: v5
- mediaType: application/vnd.oci.image.manifest.v1+json
size: 841
digest: sha256:3e576b43defa219d279961a042477c8df1b2e46d15ec8cd98827eae750e33fa6
platform:
architecture: unknown
os: unknown
- mediaType: application/vnd.oci.image.manifest.v1+json
size: 2297
digest: sha256:4bcb64ff3494e929c732a97eacef3fddc8ab27347f23e12d7e6ffc2e2cecb0a2
platform:
architecture: arm
os: linux
variant: v7
- mediaType: application/vnd.oci.image.manifest.v1+json
size: 841
digest: sha256:1fb32bf81384775941104a4d95d6303586ff0699b31182de738c2157b211ef1b
platform:
architecture: unknown
os: unknown
- mediaType: application/vnd.oci.image.manifest.v1+json
size: 2297
digest: sha256:bab0713884fed8a137ba5bd2d67d218c6192bd79b5a3526d3eb15567e035eb55
platform:
architecture: arm64
os: linux
variant: v8
- mediaType: application/vnd.oci.image.manifest.v1+json
size: 841
digest: sha256:9b1e294be5c53a9edbb775540981332a842602564ab184cf6c775f9a2b0f7688
platform:
architecture: unknown
os: unknown
- mediaType: application/vnd.oci.image.manifest.v1+json
size: 2294
digest: sha256:09279664808b1f74e3698a901d94d9640f11af5986b85c3b7ca7944a7dded1be
platform:
architecture: "386"
os: linux
- mediaType: application/vnd.oci.image.manifest.v1+json
size: 841
digest: sha256:a46d7c4bd192f2e0ee360619ea657b141a5667d9daee98d6ff5accc193a868e2
platform:
architecture: unknown
os: unknown
- mediaType: application/vnd.oci.image.manifest.v1+json
size: 2298
digest: sha256:41852102bec70d119236a95437f8a98e11d8404e50d3b2eaf6942d725f037df7
platform:
architecture: mips64le
os: linux
- mediaType: application/vnd.oci.image.manifest.v1+json
size: 567
digest: sha256:f2917769e890c8c9faf9c22ef264fec8d5a0daf27b4777fb7f2fba7e35d0173c
platform:
architecture: unknown
os: unknown
- mediaType: application/vnd.oci.image.manifest.v1+json
size: 2297
digest: sha256:822ae767e1045bf2818edf6929c8b1fb89687b9a8d33e809287842ed443b77d7
platform:
architecture: ppc64le
os: linux
- mediaType: application/vnd.oci.image.manifest.v1+json
size: 841
digest: sha256:0ccc36a3515f0d50aa41079d38c75ead02799c48caa58a4117968c9a74a46e69
platform:
architecture: unknown
os: unknown
- mediaType: application/vnd.oci.image.manifest.v1+json
size: 2295
digest: sha256:612962b8456a7aff0fe6b18d7313bb90e44b7b34aced6ce432543884886cc3a4
platform:
architecture: s390x
os: linux
- mediaType: application/vnd.oci.image.manifest.v1+json
size: 841
digest: sha256:cff2127d747263db959a262b1fe407cee4b1881946cb6dd0287e35739e36ead4
platform:
architecture: unknown
os: unknown
ちなみにコマンド実行時に -v
をつけることで各アーキテクチャのマニフェストの内容を合わせて取得できます。
例えば以下の内容では OCIManifest
以下が amd64 のアーキテクチャのマニフェストとなっており、図中の Manifest
に対応しています。
`docker manifest inspect nginx -v` の結果の一部
- Ref: docker.io/library/nginx:latest@sha256:5f0574409b3add89581b96c68afe9e9c7b284651c3a974b6e8bac46bf95e6b7f
Descriptor:
mediaType: application/vnd.oci.image.manifest.v1+json
digest: sha256:5f0574409b3add89581b96c68afe9e9c7b284651c3a974b6e8bac46bf95e6b7f
size: 2295
platform:
architecture: amd64
os: linux
Raw: ewogICJzY2hlbWFWZXJzaW9uIjogMiwKICAibWVkaWFUeXBlIjogImFwcGxpY2F0aW9uL3ZuZC5vY2kuaW1hZ2UubWFuaWZlc3QudjEranNvbiIsCiAgImNvbmZpZyI6IHsKICAgICJtZWRpYVR5cGUiOiAiYXBwbGljYXRpb24vdm5kLm9jaS5pbWFnZS5jb25maWcudjEranNvbiIsCiAgICAiZGlnZXN0IjogInNoYTI1Njo1ZWY3OTE0OWUwZWM4NGE3YTlmOTI4NGMzZjkxYWEzYzIwNjA4ZjgzOTFmNTQ0NWVhYmU5MmVmMDdkYmRhMDNjIiwKICAgICJzaXplIjogNzQ4NgogIH0sCiAgImxheWVycyI6IFsKICAgIHsKICAgICAgIm1lZGlhVHlwZSI6ICJhcHBsaWNhdGlvbi92bmQub2NpLmltYWdlLmxheWVyLnYxLnRhcitnemlwIiwKICAgICAgImRpZ2VzdCI6ICJzaGEyNTY6ZTRmZmYwNzc5ZTZkZGQyMjM2NjQ2OWYwODYyNmMzYWIxODg0YjVjYmUxNzE5YjI2ZGEyMzhjOTVmMjQ3YjMwNSIsCiAgICAgICJzaXplIjogMjkxMjYyMzIKICAgIH0sCiAgICB7CiAgICAgICJtZWRpYVR5cGUiOiAiYXBwbGljYXRpb24vdm5kLm9jaS5pbWFnZS5sYXllci52MS50YXIrZ3ppcCIsCiAgICAgICJkaWdlc3QiOiAic2hhMjU2OjJhMGNiMjc4ZmQ5Zjc3MzdlZjVkZGM1MmI0MTk4ODIxZGQwMmU4N2VkMjA0Zjc0ZDdlNDkxMDE2Yjk2ZWJlN2YiLAogICAgICAic2l6ZSI6IDQxODc1NzgyCiAgICB9LAogICAgewogICAgICAibWVkaWFUeXBlIjogImFwcGxpY2F0aW9uL3ZuZC5vY2kuaW1hZ2UubGF5ZXIudjEudGFyK2d6aXAiLAogICAgICAiZGlnZXN0IjogInNoYTI1Njo3MDQ1ZDZjMzJhZTJkM2RjMDAyZjMzYmViMGMxY2RkN2Y2OWIyNjYzYTk3MjAxMTdhYzliODJlYzI4ODY1ZTMwIiwKICAgICAgInNpemUiOiA2MjcKICAgIH0sCiAgICB7CiAgICAgICJtZWRpYVR5cGUiOiAiYXBwbGljYXRpb24vdm5kLm9jaS5pbWFnZS5sYXllci52MS50YXIrZ3ppcCIsCiAgICAgICJkaWdlc3QiOiAic2hhMjU2OjAzZGUzMWFmYjAzNTczZTBmYTY3OWQ2Nzc3YmEzMjY3YzJiOGVjMDg3Y2JjMGVmYTQ2NTI0YzFkZTA4ZjQzZWMiLAogICAgICAic2l6ZSI6IDk1NgogICAgfSwKICAgIHsKICAgICAgIm1lZGlhVHlwZSI6ICJhcHBsaWNhdGlvbi92bmQub2NpLmltYWdlLmxheWVyLnYxLnRhcitnemlwIiwKICAgICAgImRpZ2VzdCI6ICJzaGEyNTY6MGYxN2JlOGRjZmYyZTJjMjdlZTZhMzNjMWJhY2M1ODJlNzFmNzZmODU1YzJkNjlkNTEwZjJhOTNkZjg5NzMwMyIsCiAgICAgICJzaXplIjogMzk0CiAgICB9LAogICAgewogICAgICAibWVkaWFUeXBlIjogImFwcGxpY2F0aW9uL3ZuZC5vY2kuaW1hZ2UubGF5ZXIudjEudGFyK2d6aXAiLAogICAgICAiZGlnZXN0IjogInNoYTI1NjoxNGI3ZTVlOGYzOTQ2ZGEwZjkxMjBkYWIzYjBlMDVlZjI0YTVjYTg3NGJhNDg0MzI3ZGI4YjMzMDhhOTJiNTMyIiwKICAgICAgInNpemUiOiAxMjEwCiAgICB9LAogICAgewogICAgICAibWVkaWFUeXBlIjogImFwcGxpY2F0aW9uL3ZuZC5vY2kuaW1hZ2UubGF5ZXIudjEudGFyK2d6aXAiLAogICAgICAiZGlnZXN0IjogInNoYTI1NjoyM2ZhNWE3Yjk5YTY4NTI1ODg4NTkxOGM0NjhkZWQwNDJiOTViNWE3YzU2Y2VlNzU4YTY4OWY0ZjdlNTk3MWUwIiwKICAgICAgInNpemUiOiAxMzk4CiAgICB9CiAgXSwKICAiYW5ub3RhdGlvbnMiOiB7CiAgICAiY29tLmRvY2tlci5vZmZpY2lhbC1pbWFnZXMuYmFzaGJyZXcuYXJjaCI6ICJhbWQ2NCIsCiAgICAib3JnLm9wZW5jb250YWluZXJzLmltYWdlLmJhc2UuZGlnZXN0IjogInNoYTI1Njo3MGQ0YzA0MzAyYmRjZDcxYzRmYTIxYjZjMTJlOTk4ODgzODBhMDdmMDRlM2Q0NDQ1MmI5NjFiY2EwNDY0ODlkIiwKICAgICJvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYmFzZS5uYW1lIjogImRlYmlhbjpib29rd29ybS1zbGltIiwKICAgICJvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuY3JlYXRlZCI6ICIyMDI0LTA4LTE0VDIxOjMxOjEyWiIsCiAgICAib3JnLm9wZW5jb250YWluZXJzLmltYWdlLnJldmlzaW9uIjogImU3OGNmNzBjZTdiNzNhMGM5ZWE3MzRjOWNmOGFhYWEyODNjMWNjNWEiLAogICAgIm9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5zb3VyY2UiOiAiaHR0cHM6Ly9naXRodWIuY29tL25naW54aW5jL2RvY2tlci1uZ2lueC5naXQjZTc4Y2Y3MGNlN2I3M2EwYzllYTczNGM5Y2Y4YWFhYTI4M2MxY2M1YTptYWlubGluZS9kZWJpYW4iLAogICAgIm9yZy5vcGVuY29udGFpbmVycy5pbWFnZS51cmwiOiAiaHR0cHM6Ly9odWIuZG9ja2VyLmNvbS9fL25naW54IiwKICAgICJvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVyc2lvbiI6ICIxLjI3LjEiCiAgfQp9
OCIManifest:
schemaVersion: 2
mediaType: application/vnd.oci.image.manifest.v1+json
config:
mediaType: application/vnd.oci.image.config.v1+json
digest: sha256:5ef79149e0ec84a7a9f9284c3f91aa3c20608f8391f5445eabe92ef07dbda03c
size: 7486
layers:
- mediaType: application/vnd.oci.image.layer.v1.tar+gzip
digest: sha256:e4fff0779e6ddd22366469f08626c3ab1884b5cbe1719b26da238c95f247b305
size: 29126232
- mediaType: application/vnd.oci.image.layer.v1.tar+gzip
digest: sha256:2a0cb278fd9f7737ef5ddc52b4198821dd02e87ed204f74d7e491016b96ebe7f
size: 41875782
- mediaType: application/vnd.oci.image.layer.v1.tar+gzip
digest: sha256:7045d6c32ae2d3dc002f33beb0c1cdd7f69b2663a9720117ac9b82ec28865e30
size: 627
- mediaType: application/vnd.oci.image.layer.v1.tar+gzip
digest: sha256:03de31afb03573e0fa679d6777ba3267c2b8ec087cbc0efa46524c1de08f43ec
size: 956
- mediaType: application/vnd.oci.image.layer.v1.tar+gzip
digest: sha256:0f17be8dcff2e2c27ee6a33c1bacc582e71f76f855c2d69d510f2a93df897303
size: 394
- mediaType: application/vnd.oci.image.layer.v1.tar+gzip
digest: sha256:14b7e5e8f3946da0f9120dab3b0e05ef24a5ca874ba484327db8b3308a92b532
size: 1210
- mediaType: application/vnd.oci.image.layer.v1.tar+gzip
digest: sha256:23fa5a7b99a685258885918c468ded042b95b5a7c56cee758a689f4f7e5971e0
size: 1398
annotations:
com.docker.official-images.bashbrew.arch: amd64
org.opencontainers.image.base.digest: sha256:70d4c04302bdcd71c4fa21b6c12e99888380a07f04e3d44452b961bca046489d
org.opencontainers.image.base.name: debian:bookworm-slim
org.opencontainers.image.created: "2024-08-14T21:31:12Z"
org.opencontainers.image.revision: e78cf70ce7b73a0c9ea734c9cf8aaaa283c1cc5a
org.opencontainers.image.source: https://github.com/nginxinc/docker-nginx.git#e78cf70ce7b73a0c9ea734c9cf8aaaa283c1cc5a:mainline/debian
org.opencontainers.image.url: https://hub.docker.com/_/nginx
org.opencontainers.image.version: 1.27.1
...
次に manifest より下の構造を見ていくために amd64 の PC 上でイメージを pull してローカルに展開します。
# イメージをダウンロード
$ docker pull nginx@sha256:5f0574409b3add89581b96c68afe9e9c7b284651c3a974b6e8bac46bf95e6b7f
docker.io/library/nginx@sha256:5f0574409b3add89581b96c68afe9e9c7b284651c3a974b6e8bac46bf95e6b7f: Pulling from library/nginx
e4fff0779e6d: Already exists
2a0cb278fd9f: Pull complete
7045d6c32ae2: Pull complete
03de31afb035: Pull complete
0f17be8dcff2: Pull complete
14b7e5e8f394: Pull complete
23fa5a7b99a6: Pull complete
Digest: sha256:5f0574409b3add89581b96c68afe9e9c7b284651c3a974b6e8bac46bf95e6b7f
Status: Downloaded newer image for nginx@sha256:5f0574409b3add89581b96c68afe9e9c7b284651c3a974b6e8bac46bf95e6b7f
docker.io/library/nginx@sha256:5f0574409b3add89581b96c68afe9e9c7b284651c3a974b6e8bac46bf95e6b7f
# イメージを nginx.tar に出力
$ docker save nginx@sha256:5f0574409b3add89581b96c68afe9e9c7b284651c3a974b6e8bac46bf95e6b7f > nginx.tar
# 展開
$ mkdir test
$ tar xvf nginx.tar -C test
test ディレクトリに展開したファイルを見てみると blobs ディレクトリや oci-layout
などのファイルが存在していることがわかります。
このように複数の blobs と各 json 形式を含むディレクトリ構成を OCI Layout または OCI Image Layout とよびます。oci-layout が満たすべき基準や各ファイルの説明は OCI Image Layout Specification で決められています。
.
├── blobs
│ └── sha256
│ ├── 228a13938b2b1dd85f37d629df6e0cff088dd83b31bc9e9b7f19bdc2a8bc8e2d
│ ├── 23f842574fa2f03da3f7ac8d8f3c6d94c66d76cc00fc982dd9470c83f0049fa2
│ ├── 55e54df86207fa772302a6fc1e78eb60bd7e3ebd4f913ef7f5ad668ad69ab64d
│ ├── 5ba9eb7201206bb0a7bef734333205a60a5466b0badf49d2a8841448c44d939b
│ ├── 5ef79149e0ec84a7a9f9284c3f91aa3c20608f8391f5445eabe92ef07dbda03c
│ ├── 5f0272c6e96d5cd8ea1c6507cfce81980d4b99322bd037d99250a79d4c0b9f1a
│ ├── 72db5db515fdd9ae82b759fc207fdfbcc31567c28bb87950abc94ce1d60b2d40
│ ├── 7d2ee22b1699d37a35181e47d42a275803f3acf547c22a41bb8a53891c7b4f2c
│ ├── 8b87c0c6652495401acfbe029ede84a5f327664770561ba5f8b7fe9149f52dd0
│ ├── 97cfa79367882fde35f0089339609799c3e98fb79d7a4b241d1c2b6ed58b7d56
│ ├── 9853575bc4f955c5892dd64187538a6cd02dba6968eba9201854876a7a257034
│ ├── c20af9497622696cbac2919bd19bcf67672d012b74cc0408acb178b688ac8f0d
│ ├── dd8a2508f00668508e4fe4bd8030fec6e4262103a239404daa8386f1c6537797
│ ├── ec1a2ca4ac8784def146544fc7068db06a188d2da4fd7c4e134a76415b8bc1a8
│ ├── f4f00eaedec7933a48b09f3948c685c41d55f0bf5906295dd022c05b65082344
│ └── f5e50abe9b0f6d2fe7532dc473c0639d9f1729455dd2c9b3bce575f705d20e80
├── index.json
├── manifest.json
└── oci-layout
index.json
は index の内容になっています。OCI Image Index Specification で定義されています。
schemaVersion: 2
mediaType: application/vnd.oci.image.index.v1+json
manifests:
- mediaType: application/vnd.oci.image.manifest.v1+json
digest: sha256:7d2ee22b1699d37a35181e47d42a275803f3acf547c22a41bb8a53891c7b4f2c
size: 1307
manifest.json
はこの amd64 のマニフェストにどのような情報が保存されているかが記載されており、LayerSources
に各 blobs のサイズ等の情報が含まれています。Dockerfile の文脈ではよく レイヤー の話が出てきますが、それが以下の Layers に対応しています。
- Config: blobs/sha256/5ef79149e0ec84a7a9f9284c3f91aa3c20608f8391f5445eabe92ef07dbda03c
RepoTags: null
Layers:
- blobs/sha256/9853575bc4f955c5892dd64187538a6cd02dba6968eba9201854876a7a257034
- blobs/sha256/72db5db515fdd9ae82b759fc207fdfbcc31567c28bb87950abc94ce1d60b2d40
- blobs/sha256/8b87c0c6652495401acfbe029ede84a5f327664770561ba5f8b7fe9149f52dd0
- blobs/sha256/ec1a2ca4ac8784def146544fc7068db06a188d2da4fd7c4e134a76415b8bc1a8
- blobs/sha256/55e54df86207fa772302a6fc1e78eb60bd7e3ebd4f913ef7f5ad668ad69ab64d
- blobs/sha256/f4f00eaedec7933a48b09f3948c685c41d55f0bf5906295dd022c05b65082344
- blobs/sha256/5f0272c6e96d5cd8ea1c6507cfce81980d4b99322bd037d99250a79d4c0b9f1a
LayerSources:
sha256:55e54df86207fa772302a6fc1e78eb60bd7e3ebd4f913ef7f5ad668ad69ab64d:
mediaType: application/vnd.oci.image.layer.v1.tar
size: 2560
digest: sha256:55e54df86207fa772302a6fc1e78eb60bd7e3ebd4f913ef7f5ad668ad69ab64d
sha256:5f0272c6e96d5cd8ea1c6507cfce81980d4b99322bd037d99250a79d4c0b9f1a:
mediaType: application/vnd.oci.image.layer.v1.tar
size: 7168
digest: sha256:5f0272c6e96d5cd8ea1c6507cfce81980d4b99322bd037d99250a79d4c0b9f1a
sha256:72db5db515fdd9ae82b759fc207fdfbcc31567c28bb87950abc94ce1d60b2d40:
mediaType: application/vnd.oci.image.layer.v1.tar
size: 113963520
digest: sha256:72db5db515fdd9ae82b759fc207fdfbcc31567c28bb87950abc94ce1d60b2d40
sha256:8b87c0c6652495401acfbe029ede84a5f327664770561ba5f8b7fe9149f52dd0:
mediaType: application/vnd.oci.image.layer.v1.tar
size: 3584
digest: sha256:8b87c0c6652495401acfbe029ede84a5f327664770561ba5f8b7fe9149f52dd0
sha256:9853575bc4f955c5892dd64187538a6cd02dba6968eba9201854876a7a257034:
mediaType: application/vnd.oci.image.layer.v1.tar
size: 77832704
digest: sha256:9853575bc4f955c5892dd64187538a6cd02dba6968eba9201854876a7a257034
sha256:ec1a2ca4ac8784def146544fc7068db06a188d2da4fd7c4e134a76415b8bc1a8:
mediaType: application/vnd.oci.image.layer.v1.tar
size: 4608
digest: sha256:ec1a2ca4ac8784def146544fc7068db06a188d2da4fd7c4e134a76415b8bc1a8
sha256:f4f00eaedec7933a48b09f3948c685c41d55f0bf5906295dd022c05b65082344:
mediaType: application/vnd.oci.image.layer.v1.tar
size: 5120
digest: sha256:f4f00eaedec7933a48b09f3948c685c41d55f0bf5906295dd022c05b65082344
Blob の中身
blobs
ディレクトリ内の各 digest は layer に対応する実際のファイルやディレクトリとなっています。今見ている nginx の debian ベースの Dockerfile は以下にソースコードがあります。
manifest.json
内では Layers と LayerSources が 7 つありますが、Dockerfile では RUN, COPY が合わせて 6 つあるのでそれぞれに対応していることがわかります(残り 1 つはベースイメージの debian:bookworm-slim に対応)。
例えば、Dockerfile の以下の部分ではそれぞれのシェルスクリプトを /docker-entrypoint.d
以下にコピーしています。
COPY 20-envsubst-on-templates.sh /docker-entrypoint.d
COPY 30-tune-worker-processes.sh /docker-entrypoint.d
この処理で 5, 6 つめのレイヤーが作成されるので中身を見てみます。
docker image inspect nginx@[digest]
でレイヤーの digest を確認。
$ docker image inspect nginx@sha256:5f0574409b3add89581b96c68afe9e9c7b284651c3a974b6e8bac46bf95e6b7f | yq -P
- Id: sha256:5ef79149e0ec84a7a9f9284c3f91aa3c20608f8391f5445eabe92ef07dbda03c
...
RootFS:
Type: layers
Layers:
- sha256:9853575bc4f955c5892dd64187538a6cd02dba6968eba9201854876a7a257034
- sha256:72db5db515fdd9ae82b759fc207fdfbcc31567c28bb87950abc94ce1d60b2d40
- sha256:8b87c0c6652495401acfbe029ede84a5f327664770561ba5f8b7fe9149f52dd0
- sha256:ec1a2ca4ac8784def146544fc7068db06a188d2da4fd7c4e134a76415b8bc1a8
- sha256:55e54df86207fa772302a6fc1e78eb60bd7e3ebd4f913ef7f5ad668ad69ab64d
- sha256:f4f00eaedec7933a48b09f3948c685c41d55f0bf5906295dd022c05b65082344
- sha256:5f0272c6e96d5cd8ea1c6507cfce81980d4b99322bd037d99250a79d4c0b9f1a
sha256:9853575bc4f955c5892dd64187538a6cd02dba6968eba9201854876a7a257034
はベースイメージの debian のレイヤーなので除外すると、5 つめのレイヤーの digest は sha256:f4f00eaedec7933a48b09f3948c685c41d55f0bf5906295dd022c05b65082344
であることがわかります。
blob ディレクトリに含まれるファイルは tar 形式となっており、cat
等で確認すると実際にコンテナ内に配置される 20-envsubst-on-templates.sh
の内容が確認できます。
$ file f4f00eaedec7933a48b09f3948c685c41d55f0bf5906295dd022c05b65082344
f4f00eaedec7933a48b09f3948c685c41d55f0bf5906295dd022c05b65082344: POSIX tar archive
$ cat f4f00eaedec7933a48b09f3948c685c41d55f0bf5906295dd022c05b65082344
docker-entrypoint.d/0000755000000000000000000000000014657440731013161 5ustar0000000000000000docker-entrypoint.d/20-envsubst-on-templates.sh0000755000000000000000000000571214657440704020223 0ustar0000000000000000#!/bin/sh
set -e
ME=$(basename "$0")
entrypoint_log() {
if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then
echo "$@"
fi
...
同様に、6 つめのレイヤー sha256:5f027...
では 30-tune-worker-processes.sh
の中身が確認できます。
$ cat 5f0272c6e96d5cd8ea1c6507cfce81980d4b99322bd037d99250a79d4c0b9f1a
docker-entrypoint.d/0000755000000000000000000000000014657440731013161 5ustar0000000000000000docker-entrypoint.d/30-tune-worker-processes.sh0000755000000000000000000001101314657440704020222 0ustar0000000000000000#!/bin/sh
# vim:sw=2:ts=2:sts=2:et
set -eu
LC_ALL=C
ME=$(basename "$0")
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
[ "${NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE:-}" ] || exit 0
...
また、tar を展開することで実際に /docker-entrypoint.d
以下にシェルスクリプトが配置されていることが確認できます。
$ mkdir test
$ tar xvf 5f0272c6e96d5cd8ea1c6507cfce81980d4b99322bd037d99250a79d4c0b9f1a -C test
docker-entrypoint.d/
docker-entrypoint.d/30-tune-worker-processes.sh
$ tree test
test
└── docker-entrypoint.d
└── 30-tune-worker-processes.sh
manifest の中で最も size の大きいレイヤー 72db5...
は一見するとベースイメージの debian レイヤーに対応してそうですが、レイヤーの top は 98535...
なのでこちらが debian のレイヤー、72db5...
は nginx Dockerfile の最初の RUN
に対応するレイヤーであることがわかります。
Layers:
- blobs/sha256/9853575bc4f955c5892dd64187538a6cd02dba6968eba9201854876a7a257034
- blobs/sha256/72db5db515fdd9ae82b759fc207fdfbcc31567c28bb87950abc94ce1d60b2d40
- ...
LayerSources:
sha256:72db5db515fdd9ae82b759fc207fdfbcc31567c28bb87950abc94ce1d60b2d40:
mediaType: application/vnd.oci.image.layer.v1.tar
size: 113963520
digest: sha256:72db5db515fdd9ae82b759fc207fdfbcc31567c28bb87950abc94ce1d60b2d40
sha256:9853575bc4f955c5892dd64187538a6cd02dba6968eba9201854876a7a257034:
mediaType: application/vnd.oci.image.layer.v1.tar
size: 77832704
digest: sha256:9853575bc4f955c5892dd64187538a6cd02dba6968eba9201854876a7a257034
sha256:ec1a2ca4ac87
Dockerfile
を見ると RUN
でパッケージのインストールなどいろいろな処理を行っていることでサイズが増えているようです。また、blobs ディレクトリの中にはマニフェストの Layers
に表示されていない digest もありますが、これらはベースイメージである debian イメージに関連するレイヤーとなっています。
ORAS で OCI Artifact を push/pull する
普段使っている docker イメージの内部構造を見て OCI アーティファクトの構造についてある程度イメージがつかめたので、本題に戻って ORAS で扱う OCI アーティファクトの内容を見ていきます。
まずは Quick start や How-to Guides Pushing and Pulling に沿ってアーティファクトを作成しますが、アーティファクトを push/pull 保存するためにアーティファクトレジストリが必要となるので zot
を docker で起動します。
docker run -d -p 5000:5000 --name zot ghcr.io/project-zot/zot-linux-amd64:latest
quickstart の通り artifact.txt
のテキストファイルのみを含む単純なアーティファクトを作成して zot に push します。
$ echo "hello world" > artifact.txt
$ oras push localhost:5000/hello-artifact:v1 \
./artifact.txt
✓ Exists application/vnd.oci.empty.v1+json 2/2 B 100.00% 0s
└─ sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
✓ Exists artifact.txt 12/12 B 100.00% 0s
└─ sha256:a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447
✓ Uploaded application/vnd.oci.image.manifest.v1+json 591/591 B 100.00% 17ms
└─ sha256:a91334e84d5371e2a5add8baf8c5052c21effcf352df39e4a44840d84d9c9bee
Pushed [registry] localhost:5000/hello-artifact:v1
ArtifactType: application/vnd.unknown.artifact.v1
Digest: sha256:a91334e84d5371e2a5add8baf8c5052c21effcf352df39e4a44840d84d9c9bee
次に oras manifest fetch localhost:5000/hello-artifact:v1
コマンドで zot に push したアーティファクトの内容を取得します。
schemaVersion: 2
mediaType: application/vnd.oci.image.manifest.v1+json
artifactType: application/vnd.unknown.artifact.v1
config:
mediaType: application/vnd.oci.empty.v1+json
digest: sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
size: 2
data: e30=
layers:
- mediaType: application/vnd.oci.image.layer.v1.tar
digest: sha256:a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447
size: 12
annotations:
org.opencontainers.image.title: artifact.txt
annotations:
org.opencontainers.image.created: "2024-08-27T14:33:13Z"
先程確認した docker イメージのマニフェスト (OCIManifest) と比較すると、上記の内容がイメージマニフェストに対応していることが確認できます。
config
に注目すると mediaType: application/vnd.oci.empty.v1+json
となっていますが、これは Manifest Specification における Guidance for an Empty Descriptor の記述方法に従っており、メタデータが空であることが確認できます data: e30=
は {}
を base64 encode したもので 2 文字なので size 2
となっています。
$ echo "e30=" | base64 -d
{}
アーティファクトに含まれるファイル等の情報は layers[]
以下に追加されます。
今回は artifact.txt
1 つのみを指定して push したためそれが含まれており、ファイルサイズが 12 byte なので size:12
となっています。mediaType
や annotations
は添付したファイルやディレクトリに応じて ORAS が自動で追加するようです。
アーティファクトレジストリ上に存在するアーティファクトを取得する場合は docker CLI のように oras pull localhost:5000/hello-artifact:v1
でローカルにダウンロードできます。この例では添付した artifact.txt
が直接取得できます。
$ oras pull localhost:5000/hello-artifact:v1
✓ Pulled artifact.txt 12/12 B 100.00% 113µs
└─ sha256:a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447
✓ Pulled application/vnd.oci.image.manifest.v1+json 591/591 B 100.00% 0s
└─ sha256:a91334e84d5371e2a5add8baf8c5052c21effcf352df39e4a44840d84d9c9bee
Pulled [registry] localhost:5000/hello-artifact:v1
Digest: sha256:a91334e84d5371e2a5add8baf8c5052c21effcf352df39e4a44840d84d9c9bee
$ ls
artifact.txt
複数のファイルを添付する場合は Pushing and Pulling のコマンド例のように複数の layers を設定するかディレクトリを指定することでアーティファクトに含めることができます。
mkdir docs
echo "Docs on this artifact" > ./docs/readme.md
echo "More content for this artifact" > ./docs/readme2.md
oras push localhost:5000/hello-artifact:v2 \
./docs
✓ Uploaded docs 193/193 B 100.00% 5ms
└─ sha256:a549a5915fee8fa9eff01724373616df4f0e8663e8c0244a84bdb7d0c9a763ba
✓ Exists application/vnd.oci.empty.v1+json 2/2 B 100.00% 0s
└─ sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
✓ Uploaded application/vnd.oci.image.manifest.v1+json 730/730 B 100.00% 5ms
└─ sha256:538eee55d7942b37ca99e563687a6ccd523c67a4657bdf30c600b3dbdacb0cd8
Pushed [registry] localhost:5000/hello-artifact:v2
ArtifactType: application/vnd.unknown.artifact.v1
Digest: sha256:538eee55d7942b37ca99e563687a6ccd523c67a4657bdf30c600b3dbdacb0cd8
ディレクトリを指定すると mediaType
は自動で ...tar+gzip
となります。ドキュメントでは tar となっていますが実際にやってみると gzip も付加されました。
schemaVersion: 2
mediaType: application/vnd.oci.image.manifest.v1+json
artifactType: application/vnd.unknown.artifact.v1
config:
mediaType: application/vnd.oci.empty.v1+json
digest: sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
size: 2
data: e30=
layers:
- mediaType: application/vnd.oci.image.layer.v1.tar+gzip
digest: sha256:a549a5915fee8fa9eff01724373616df4f0e8663e8c0244a84bdb7d0c9a763ba
size: 193
annotations:
io.deis.oras.content.digest: sha256:c1636d5e6d89031e478fc32fdb0e54a04a0bab70154def36652a46b0a83009b2
io.deis.oras.content.unpack: "true"
org.opencontainers.image.title: docs
annotations:
org.opencontainers.image.created: "2024-08-27T15:34:25Z"
ローカル環境で事前に tar.gz でまとめてから添付することも可能。
oras push localhost:5000/hello-artifact:v3 res.tar.gz
✓ Uploaded res.tar.gz 286/286 B 100.00% 2ms
└─ sha256:bf93d6344cae0c4cab7b0927cb7d4e1ebbc2177d5f88699f752649e390800d6e
✓ Exists application/vnd.oci.empty.v1+json 2/2 B 100.00% 0s
└─ sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
✓ Uploaded application/vnd.oci.image.manifest.v1+json 590/590 B 100.00% 0s
└─ sha256:0df32bf3ae06b0c6baae12af3e2dcefb9f087e6c5ec235e9aa71f179f83dc3f6
Pushed [registry] localhost:5000/hello-artifact:v3
ArtifactType: application/vnd.unknown.artifact.v1
Digest: sha256:0df32bf3ae06b0c6baae12af3e2dcefb9f087e6c5ec235e9aa71f179f83dc3f6
この場合の mediaType は application/vnd.oci.image.layer.v1.tar
となり size は tar.gz のサイズに一致している。
schemaVersion: 2
mediaType: application/vnd.oci.image.manifest.v1+json
artifactType: application/vnd.unknown.artifact.v1
config:
mediaType: application/vnd.oci.empty.v1+json
digest: sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
size: 2
data: e30=
layers:
- mediaType: application/vnd.oci.image.layer.v1.tar
digest: sha256:bf93d6344cae0c4cab7b0927cb7d4e1ebbc2177d5f88699f752649e390800d6e
size: 286
annotations:
org.opencontainers.image.title: res.tar.gz
annotations:
org.opencontainers.image.created: "2024-08-27T15:41:53Z"
$ du -b res.tar.gz
286 res.tar.gz
Annotation をつける
Annotation はオプションで設定可能な Key-Value 形式のメタデータです。OCI Image の仕様でいくつかルールや予約語が決められていますが、これを満たせば任意の値を設定することができます。ORAS では oras push
でアーティファクトを push する際に key-value 形式で指定したり、annotation を記載したファイルを指定することで設定できます。Using a JSON File の例では config, manifest, layer にそれぞれ annotation を追加しています。
{
"$config": {
"hello": "world"
},
"$manifest": {
"foo": "bar"
},
"cake.txt": {
"fun": "more cream"
}
}
push 時に --annotation-file
でファイルを指定。
oras push --annotation-file annotation.json localhost:5000/hello-artifact:ann
annotations
が追加されていることが確認できます。
$ oras manifest fetch localhost:5000/hello-artifact:ann | yq -P
schemaVersion: 2
mediaType: application/vnd.oci.image.manifest.v1+json
artifactType: application/vnd.unknown.artifact.v1
config:
mediaType: application/vnd.oci.empty.v1+json
digest: sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
size: 2
annotations:
hello: world
data: e30=
layers:
- mediaType: application/vnd.oci.empty.v1+json
digest: sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
size: 2
data: e30=
annotations:
foo: bar
org.opencontainers.image.created: "2024-08-27T17:29:08Z"
レジストリにもよりますが、例えば harbor ではアーティファクトに設定された annotations が overview に表示されます。
使い勝手としては docker label
のような感じでアーティファクトに関連する情報を付加するのが良さそうです。例えば ingress-nginx の helm chart のアーティファクトではソースコードの github の URL, version, 作成者などの情報が annotation に設定されています。
$ oras manifest fetch harbor.centre.com/helm/ingress-nginx:4.11.2 | yq -P
schemaVersion: 2
config:
mediaType: application/vnd.cncf.helm.config.v1+json
digest: sha256:f1fbe187a6e6a03bc72f5cc77ccdb50cfec0c3d620812b4d4e76784d55ed0f15
size: 728
layers:
- mediaType: application/vnd.cncf.helm.chart.content.v1.tar+gzip
digest: sha256:df79d6acb94c4c7c6e06624722a9c131c8cbc9fb5eaa5f3c962e003702aa8ea8
size: 58090
annotations:
artifacthub.io/changes: |
- Update Ingress-Nginx version controller-v1.11.2
artifacthub.io/prerelease: "false"
org.opencontainers.image.authors: cpanato, Gacko, puerco, rikatz, strongjz, tao12345666333
org.opencontainers.image.created: "2024-08-27T00:00:53+09:00"
org.opencontainers.image.description: Ingress controller for Kubernetes using NGINX as a reverse proxy and load balancer
org.opencontainers.image.source: https://github.com/kubernetes/ingress-nginx
org.opencontainers.image.title: ingress-nginx
org.opencontainers.image.url: https://github.com/kubernetes/ingress-nginx
org.opencontainers.image.version: 4.11.2
Distributing OCI Layouts
https://oras.land/docs/how_to_guides/distributing_oci_layouts/ の内容の話
OCI Image Layout
は OCI イメージが満たすべき OCI Image Layout Specification のディレクトリ構造を指し、以下のファイル、ディレクトリが含まれている必要があります。
- oci-layout
- index.json
- blobs
既に確認しているように docker イメージを docker save
コマンドで tar に書き出した中身はこの仕様に準拠しています。
oras ではローカルに oci-layout に準拠したディレクトリが存在している場合にその内容をアーティファクトとしてレジストリに push できるようになっています。ドキュメントに沿って docker buildx
で oci-layout のアーティファクトを作成してみます。
FROM alpine
CMD echo 'hello world!'
ビルド
$ docker buildx build . -f Dockerfile -o type=oci,dest=hello-world.tar -t hello-world:v4
これでローカル環境に hello-world.tar
が作成されるので hello-artifact:v4
としてレジストリに push
oras cp --from-oci-layout ./hello-world.tar localhost:5000/hello-artifact:v4
oras manifest fetch localhost:5000/hello-artifact:v4
でマニフェストを取得
schemaVersion: 2
mediaType: application/vnd.oci.image.manifest.v1+json
config:
mediaType: application/vnd.oci.image.config.v1+json
digest: sha256:31c62eb4c507b8934e284ced6bbebdcffb80c0346f5cc65efd43b182184f19ce
size: 794
layers:
- mediaType: application/vnd.oci.image.layer.v1.tar+gzip
digest: sha256:c6a83fedfae6ed8a4f5f7cbb6a7b6f1c1ec3d86fea8cb9e5ba2e5e6673fde9f6
size: 3622892
先程 oras push
で直接テキストファイルを追加して作成したアーティファクトのマニフェストと比較すると、config の size が 794 となっているので何らかの内容が入っていることがわかります。
config の内容は oras manifest fetch-config localhost:5000/hello-artifact:v4
で確認できます。見てみるとアーキテクチャの情報(amd64 のマシンでビルドしているので amd64 になっている)やイメージ内のコマンド、history などのメタデータが入っていることが確認できます。
architecture: amd64
config:
Env:
- PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Cmd:
- /bin/sh
- -c
- echo 'hello world!'
ArgsEscaped: true
created: "2024-07-22T22:26:43.778747613Z"
history:
- created: "2024-07-22T22:26:43.622487881Z"
created_by: '/bin/sh -c #(nop) ADD file:99093095d62d0421541d882f9ceeddb2981fe701ec0aa9d2c08480712d5fed21 in / '
- created: "2024-07-22T22:26:43.778747613Z"
created_by: '/bin/sh -c #(nop) CMD ["/bin/sh"]'
empty_layer: true
- created: "2024-07-22T22:26:43.778747613Z"
created_by: CMD ["/bin/sh" "-c" "echo 'hello world!'"]
comment: buildkit.dockerfile.v0
empty_layer: true
os: linux
rootfs:
type: layers
diff_ids:
- sha256:78561cef0761903dd2f7d09856150a6d4fb48967a8f113f3e33d79effbf59a07
この内容は元の hello-world.tar
の blob に含まれる 1 つに対応しています。
.
├── blobs
│ └── sha256
│ ├── 31c62eb4c507b8934e284ced6bbebdcffb80c0346f5cc65efd43b182184f19ce
│ ├── 3aee1155f4ace57c66a37bec188b626973416d5a5dc46a63220874de03a4b8ce
│ └── c6a83fedfae6ed8a4f5f7cbb6a7b6f1c1ec3d86fea8cb9e5ba2e5e6673fde9f6
├── index.json
└── oci-layout
architecture: amd64
config:
Env:
- PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Cmd:
- /bin/sh
- -c
- echo 'hello world!'
ArgsEscaped: true
created: "2024-07-22T22:26:43.778747613Z"
history:
- created: "2024-07-22T22:26:43.622487881Z"
created_by: '/bin/sh -c #(nop) ADD file:99093095d62d0421541d882f9ceeddb2981fe701ec0aa9d2c08480712d5fed21 in / '
- created: "2024-07-22T22:26:43.778747613Z"
created_by: '/bin/sh -c #(nop) CMD ["/bin/sh"]'
empty_layer: true
- created: "2024-07-22T22:26:43.778747613Z"
created_by: CMD ["/bin/sh" "-c" "echo 'hello world!'"]
comment: buildkit.dockerfile.v0
empty_layer: true
os: linux
rootfs:
type: layers
diff_ids:
- sha256:78561cef0761903dd2f7d09856150a6d4fb48967a8f113f3e33d79effbf59a07
残りの blob は 3aee...
が実際のマニフェストの json、c6a83fe...
がベースイメージに指定した alpine のレイヤーに対応しています。
schemaVersion: 2
mediaType: application/vnd.oci.image.manifest.v1+json
config:
mediaType: application/vnd.oci.image.config.v1+json
digest: sha256:31c62eb4c507b8934e284ced6bbebdcffb80c0346f5cc65efd43b182184f19ce
size: 794
layers:
- mediaType: application/vnd.oci.image.layer.v1.tar+gzip
digest: sha256:c6a83fedfae6ed8a4f5f7cbb6a7b6f1c1ec3d86fea8cb9e5ba2e5e6673fde9f6
size: 3622892
ORAS のサブコマンド
ORAS discover
まだプレビュー版の状況ですが、oras discover
コマンドではマニフェストの referrer を取得することができます。
referrer というのは Understanding References in the Open Container Initiative (OCI) Standard に説明がある通りイメージやアーティファクトを特定するための参照(ポインタのようなもの)を指しています。
docker では docker pull nginx
で dockerhub 上の latest タグに対応するイメージを pull できますが、省略されている部分を明示的に書くと docker.io/library/nginx@[digest]
となり、レジストリ、repository, タグに対応するイメージの digest を指定してイメージを一意に特定してから pull しています。
digest を用いることで対象のアーティファクトを一意に特定できますが、長くて人間にとっては覚えづらいのでタグを使う場合が多いかと思います。referrer はタグから digest への参照を取得するために使用されます。
例えば oras のチュートリアルで使用される zot registry では referrer API が実装されているため、v4
タグを指定するとそれに対応するアーティファクトの digest を含む参照が取得できます。
$ oras discover localhost:5000/hello-artifact:v4
localhost:5000/hello-artifact@sha256:3aee1155f4ace57c66a37bec188b626973416d5a5dc46a63220874de03a4b8ce
一方で referrer が使用できるかどうかは、アーティファクトを格納する registry が referrer の機能に対応しているかどうかに依存するようです。例えばコンテナイメージレジストリの Harbor は OCI アーティファクトに対応しているのでアーティファクトを push/pull できますが、 referrer API はまだ実装されていないので取得しようとしてもエラーとなります。
$ oras discover harbor.centre.com/helm/oras-test:latest
Error response from registry: unauthorized: un-recognized request: GET /v2/helm/oras-test/referrers/sha256:ac88e565f0f104bdd7189eaf658bc9973a6192f430a6f551635f7ef5d2fa7b2a: un-recognized request: GET /v2/helm/oras-test/referrers/sha256:ac88e565f0f104bdd7189eaf658bc9973a6192f430a6f551635f7ef5d2fa7b2a
ORAS blob
先程作成した alpine ベースイメージの OCI レイアウトでは 3 つの blob が含まれていますが、oras blob
コマンドではこの blob の内容を直接取得できます。
oras manifest
コマンドで各 blob に対応する digest が取得できるので、レジストリ上に保存されているアーティファクトの名前やタグがわかっていれば blob の値を知らなくても調べることができます。
まず oras manifest fetch localhost:5000/hello-artifact:v4
でアーティファクトのマニフェストを取得します。
schemaVersion: 2
mediaType: application/vnd.oci.image.manifest.v1+json
config:
mediaType: application/vnd.oci.image.config.v1+json
digest: sha256:31c62eb4c507b8934e284ced6bbebdcffb80c0346f5cc65efd43b182184f19ce
size: 794
layers:
- mediaType: application/vnd.oci.image.layer.v1.tar+gzip
digest: sha256:c6a83fedfae6ed8a4f5f7cbb6a7b6f1c1ec3d86fea8cb9e5ba2e5e6673fde9f6
size: 3622892
この結果を見ることで config に対応する digest が 31c..
, layers の 1 つが c6a...
であると判別できます。
また、イメージタグ v4 に対応する digest は oras discover
で取得できます。
$ oras discover localhost:5000/hello-artifact:v4
localhost:5000/hello-artifact@sha256:3aee1155f4ace57c66a37bec188b626973416d5a5dc46a63220874de03a4b8ce
よってアーティファクト localhost:5000/hello-artifact:v4
に関しては以下 3 つの blob が存在することがわかります。
内容 | digest |
---|---|
config の json | sha256:31c62eb4c507b8934e284ced6bbebdcffb80c0346f5cc65efd43b182184f19ce |
layer | sha256:c6a83fedfae6ed8a4f5f7cbb6a7b6f1c1ec3d86fea8cb9e5ba2e5e6673fde9f6 |
index | sha256:3aee1155f4ace57c66a37bec188b626973416d5a5dc46a63220874de03a4b8ce |
一方で、先程ローカルで展開した blob
ディレクトリは以下のようになっているので上記と一致しています。なので実際に oci-layout の中身を知らなくてもどのような blob が含まれているか確認できます。
.
├── blobs
│ └── sha256
│ ├── 31c62eb4c507b8934e284ced6bbebdcffb80c0346f5cc65efd43b182184f19ce
│ ├── 3aee1155f4ace57c66a37bec188b626973416d5a5dc46a63220874de03a4b8ce
│ └── c6a83fedfae6ed8a4f5f7cbb6a7b6f1c1ec3d86fea8cb9e5ba2e5e6673fde9f6
blob の内容を取得するには oras blob -o - fetch [digest]
で指定します。
-o
オプションは出力先を指定し、-o -
の場合は標準出力に表示します。
試しに config に対応する blob を取得してみます。
$ oras blob -o - fetch localhost:5000/hello-artifact@sha256:31c62eb4c507b8934e284ced6bbebdcffb80c0346f5cc65efd43b182184f19ce | yq -P
architecture: amd64
config:
Env:
- PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Cmd:
- /bin/sh
- -c
- echo 'hello world!'
ArgsEscaped: true
created: "2024-07-22T22:26:43.778747613Z"
history:
- created: "2024-07-22T22:26:43.622487881Z"
created_by: '/bin/sh -c #(nop) ADD file:99093095d62d0421541d882f9ceeddb2981fe701ec0aa9d2c08480712d5fed21 in / '
- created: "2024-07-22T22:26:43.778747613Z"
created_by: '/bin/sh -c #(nop) CMD ["/bin/sh"]'
empty_layer: true
- created: "2024-07-22T22:26:43.778747613Z"
created_by: CMD ["/bin/sh" "-c" "echo 'hello world!'"]
comment: buildkit.dockerfile.v0
empty_layer: true
os: linux
rootfs:
type: layers
diff_ids:
- sha256:78561cef0761903dd2f7d09856150a6d4fb48967a8f113f3e33d79effbf59a07
これは oras manifest fetch-config localhost:5000/hello-artifact:v4
を実行した際と同じ内容となっていますが、config の実態は blob 内に保存されている json ファイルであるため、 blob を取得することも確認できるということになっています。
layers
の方の blob はベースイメージの alpine のファイルシステム (tar) に対応しています。これは標準出力には表示できないのでファイルに出力してから展開することで確認できます。
$ oras blob fetch localhost:5000/hello-artifact@sha256:c6a83fedfae6ed8a4f5f7cbb6a7b6f1c1ec3d86fea8cb9e5ba2e5e6673fde9f6 -o output.tar.gz
✓ Downloaded application/octet-stream 3.46/3.46 MB 100.00% 0s
└─ sha256:c6a83fedfae6ed8a4f5f7cbb6a7b6f1c1ec3d86fea8cb9e5ba2e5e6673fde9f6
$ tar zxf output.tar.gz -C output
$ tree -L 1 output
output
├── bin
├── dev
├── etc
├── home
├── lib
├── media
├── mnt
├── opt
├── proc
├── root
├── run
├── sbin
├── srv
├── sys
├── tmp
├── usr
└── var
$ cat output/etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.20.2
PRETTY_NAME="Alpine Linux v3.20"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues"
最後の index の digest 3ae...
の blob の実態はマニフェストの json であるため、oras manifest fetch localhost:5000/hello-artifact:v4
と同じ内容になっています。
$ oras blob -o - fetch localhost:5000/hello-artifact@sha256:3aee1155f4ace57c66a37bec188b626973416d5a5dc46a63220874de03a4b8ce | yq -P
schemaVersion: 2
mediaType: application/vnd.oci.image.manifest.v1+json
config:
mediaType: application/vnd.oci.image.config.v1+json
digest: sha256:31c62eb4c507b8934e284ced6bbebdcffb80c0346f5cc65efd43b182184f19ce
size: 794
layers:
- mediaType: application/vnd.oci.image.layer.v1.tar+gzip
digest: sha256:c6a83fedfae6ed8a4f5f7cbb6a7b6f1c1ec3d86fea8cb9e5ba2e5e6673fde9f6
size: 3622892
このように oras blob
コマンドではアーティファクト内に含まれる特定の blob を取得したりダウンロードすることができます。ただこのコマンドを使って直接 BLOB を見る機会があるかどうかは不明。
ORAS を使用するプロジェクト
Helm
Helm では v3.8.0 から OCI 機能がサポートされ OCI アーティファクトレジストリに helm chart を push/pull できるようになっています。
このページでは特に ORAS の記載はありませんが、github で検索してみると ORAS の go 言語用のライブラリ oras-go が裏で使用されていることが伺えます。
また、helm chart 自体が OCI アーティファクトであるので今までと同様にアーティファクトの push/pull や情報の取得ができます。試しに nginx ingress controller の helm chart をローカルに立てた harbor レジストリに push してアーティファクトを見てみます。
helm repo add nginx https://kubernetes.github.io/ingress-nginx
helm pull nginx/ingress-nginx
helm registry login harbor.centre.com
helm push ingress-nginx-4.11.2.tgz oci://harbor.centre.com/helm/helm-nginx
oras manifest fetch
で chart のマニフェストが取得できます。helm chart では config.mediaType
が helm 用に設定されていたり、annotation に chart の詳細などが記載されています。
$ oras manifest fetch harbor.centre.com/helm/ingress-nginx:4.11.2 | yq -P
schemaVersion: 2
config:
mediaType: application/vnd.cncf.helm.config.v1+json
digest: sha256:f1fbe187a6e6a03bc72f5cc77ccdb50cfec0c3d620812b4d4e76784d55ed0f15
size: 728
layers:
- mediaType: application/vnd.cncf.helm.chart.content.v1.tar+gzip
digest: sha256:df79d6acb94c4c7c6e06624722a9c131c8cbc9fb5eaa5f3c962e003702aa8ea8
size: 58090
annotations:
artifacthub.io/changes: |
- Update Ingress-Nginx version controller-v1.11.2
artifacthub.io/prerelease: "false"
org.opencontainers.image.authors: cpanato, Gacko, puerco, rikatz, strongjz, tao12345666333
org.opencontainers.image.created: "2024-08-27T00:00:53+09:00"
org.opencontainers.image.description: Ingress controller for Kubernetes using NGINX as a reverse proxy and load balancer
org.opencontainers.image.source: https://github.com/kubernetes/ingress-nginx
org.opencontainers.image.title: ingress-nginx
org.opencontainers.image.url: https://github.com/kubernetes/ingress-nginx
org.opencontainers.image.version: 4.11.2
oras manifest fetch-config
で config の中身が取得できます。
$ oras manifest fetch-config harbor.centre.com/helm/ingress-nginx:4.11.2 | yq -P
name: ingress-nginx
home: https://github.com/kubernetes/ingress-nginx
sources:
- https://github.com/kubernetes/ingress-nginx
version: 4.11.2
description: Ingress controller for Kubernetes using NGINX as a reverse proxy and load balancer
keywords:
- ingress
- nginx
maintainers:
- name: cpanato
- name: Gacko
- name: puerco
- name: rikatz
- name: strongjz
- name: tao12345666333
icon: https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Nginx_logo.svg/500px-Nginx_logo.svg.png
apiVersion: v2
appVersion: 1.11.2
annotations:
artifacthub.io/changes: |
- Update Ingress-Nginx version controller-v1.11.2
artifacthub.io/prerelease: "false"
kubeVersion: '>=1.21.0-0'
oras pull
か oras blob fetch -o [output] [chart]:[layer digest]
で chart に対応するアーティファクトが取得できます。
$ oras blob -o chart.tar.gz fetch harbor.centre.com/helm/ingress-nginx:4.11.2@sha256:df79d6acb94c4c7c6e06624722a9c131c8cbc9fb5eaa5f3c962e003702aa8ea8
✓ Downloaded application/octet-stream 56.7/56.7 kB 100.00% 421µs
└─ sha256:df79d6acb94c4c7c6e06624722a9c131c8cbc9fb5eaa5f3c962e003702aa8ea8
中身は Github の chart の内容 になっています。
$ mkdir ingress-nginx
$ tar xvf -C ingress-nginx
$ tree ingress-nginx -L 2
ingress-nginx
└── ingress-nginx
├── Chart.yaml
├── OWNERS
├── README.md
├── README.md.gotmpl
├── changelog
├── ci
├── templates
├── tests
└── values.yaml
また、helm chart を管理する際によく用いられる Artifact Hub でも ORAS CLI で OCI アーティファクトを push する例が記載されています。
Link
- OCI Image Index Specification
- OCI Image Manifest Specification
- OCI Image Layout Specification
- OCI Distribution Specification
-
OCI Image Media Types
- OCI マニフェストの
mediaType
に指定される値のリスト
- OCI マニフェストの
Discussion