Image Registry - Multi-Architecture Image (inspect / pull)

Image Registry では、`Image Manifest V 2, Schema 2` から `Multi-Architecture Image` の使用が可能になりました。これによって、単一の Image Repository で複数 Architecture Image を扱う事ができます。
この実装は以前から広く利用されていますが、Image Registry の利用者にとっては、あまり馴染みの無いものです。

ここでは、利用者から見た `Multi-Architecture Image` の確認方法と取得方法をご紹介します。
#### Image Manifest V 2, Schema 2 
`Image Manifest V 2, Schema 2` の説明は以下に記載されています。
https://github.com/distribution/distribution/blob/main/docs/spec/manifest-v2-2.md#manifest-list
https://docs.docker.com/registry/spec/manifest-v2-2/
> This second schema version has two primary goals. The first is to allow multi-architecture images, through a "fat manifest" which references image manifests for platform-specific versions of an image.

`fat manifest（別称 Manifest List）` とは、以下の Media Type を指します。
```shell
application/vnd.docker.distribution.manifest.list.v2+json: Manifest list, aka "fat manifest"
```
Multi-Architecture の情報は、`.manifests[].platform` に記載されます。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2128125/eb095da0-255b-7d1e-9ddd-f6297f082060.png)

#### Image Manifest の確認
ここでは、Docker Hub の Alpine Image を例にとって、Image Manifest を確認してみます。
また、暗黙の `latest` Tag の影響を避けるために、明示的に `3.15` Tag を使用します。

__alpine Docker Official Image__
A minimal Docker image based on Alpine Linux with a complete package index and only 5 MB in size!
https://hub.docker.com/_/alpine

`podman manifest inspect` コマンドで、Image Manifest の Media Type を確認します。
```shell
$ podman manifest inspect docker.io/library/alpine:3.15 | jq -r '.mediaType'
application/vnd.docker.distribution.manifest.list.v2+json
```
この Manifest が `Manifest List` であることが分かります。

実装されている Architecture も確認してみます。
```shell
$ podman manifest inspect docker.io/library/alpine:3.15 | jq -rc '.manifests[].platform'
{"architecture":"amd64","os":"linux"}
{"architecture":"arm","os":"linux","variant":"v6"}
{"architecture":"arm","os":"linux","variant":"v7"}
{"architecture":"arm64","os":"linux","variant":"v8"}
{"architecture":"386","os":"linux"}
{"architecture":"ppc64le","os":"linux"}
{"architecture":"s390x","os":"linux"}
```
`skopeo inspect --raw` コマンドでも、同様に確認することができます。
```shell
$ skopeo inspect --raw docker://docker.io/library/alpine:3.15 | jq -r '.mediaType'
application/vnd.docker.distribution.manifest.list.v2+json

$ skopeo inspect --raw docker://docker.io/library/alpine:3.15 | jq -rc '.manifests[].platform'
{"architecture":"amd64","os":"linux"}
{"architecture":"arm","os":"linux","variant":"v6"}
{"architecture":"arm","os":"linux","variant":"v7"}
{"architecture":"arm64","os":"linux","variant":"v8"}
{"architecture":"386","os":"linux"}
{"architecture":"ppc64le","os":"linux"}
{"architecture":"s390x","os":"linux"}
```
`oc image info` コマンドでも、実装されている Architecture を確認することができます。
```shell
$ oc image info docker.io/library/alpine:3.15
error: the image is a manifest list and contains multiple images - use --filter-by-os to select from:

  OS             DIGEST
  linux/amd64    sha256:4ff3ca91275773af45cb4b0834e12b7eb47d1c18f770a0b151381cd227f4c253
  linux/arm/v6   sha256:3c66139adbd2513f9fc56eff206513ffc8356b282bed31a4e74c7eb926b850aa
  linux/arm/v7   sha256:0615cdd745d0b78e7e6ac3a7b1f02e4daefa664eae0324120955f4e4c91bea3f
  linux/arm64/v8 sha256:c3c58223e2af75154c4a7852d6924b4cc51a00c821553bbd9b3319481131b2e0
  linux/386      sha256:72af6266bafde8c78d5f20a2a85d0576533ce1ecd6ed8bcf7baf62a743f3b24d
  linux/ppc64le  sha256:0f3aeb63bb71ccd3ba0020772f5617e50946a4f2713953c3f494203f1544ea03
  linux/s390x    sha256:83167bc8418071fc178d191ed604f44792c94ad3c3ac26350c29d2445f6a9644
```
#### 特定 Architecture Image の確認（inspect）
`skopeo inspect` コマンドで、特定 Platform の Manifest を確認することができます。
先程、`skopeo inspect --raw` コマンドで確認した情報を使用してみます。
```shell
$ skopeo inspect --override-arch=arm --override-os=linux --override-variant=v7 docker://docker.io/library/alpine:3.15
{
    "Name": "docker.io/library/alpine",
    "Digest": "sha256:4edbd2beb5f78b1014028f4fbb99f3237d9561100b6881aabbf5acce2c4f9454",
    "RepoTags": [
        "2.6",
        "2.7",
        "20190228",
        "20190408",
        "20190508",
        "20190707",
        "20190809",
        "20190925",
        "20191114",
        "20191219",
        "20200122",
        "20200319",
        "20200428",
        "20200626",
        "20200917",
        "20201218",
        "20210212",
        "20210730",
        "20210804",
        "20220316",
        "20220328",
        "3",
        "3.1",
        "3.10",
        "3.10.0",
        "3.10.1",
        "3.10.2",
        "3.10.3",
        "3.10.4",
        "3.10.5",
        "3.10.6",
        "3.10.7",
        "3.10.8",
        "3.10.9",
        "3.11",
        "3.11.0",
        "3.11.10",
        "3.11.11",
        "3.11.12",
        "3.11.13",
        "3.11.2",
        "3.11.3",
        "3.11.5",
        "3.11.6",
        "3.11.7",
        "3.11.8",
        "3.11.9",
        "3.12",
        "3.12.0",
        "3.12.1",
        "3.12.10",
        "3.12.11",
        "3.12.12",
        "3.12.2",
        "3.12.3",
        "3.12.4",
        "3.12.5",
        "3.12.6",
        "3.12.7",
        "3.12.8",
        "3.12.9",
        "3.13",
        "3.13.0",
        "3.13.1",
        "3.13.10",
        "3.13.2",
        "3.13.3",
        "3.13.4",
        "3.13.5",
        "3.13.6",
        "3.13.7",
        "3.13.8",
        "3.13.9",
        "3.14",
        "3.14.0",
        "3.14.1",
        "3.14.2",
        "3.14.3",
        "3.14.4",
        "3.14.5",
        "3.14.6",
        "3.15",
        "3.15.0",
        "3.15.0-rc.4",
        "3.15.1",
        "3.15.2",
        "3.15.3",
        "3.15.4",
        "3.16",
        "3.16.0",
        "3.2",
        "3.3",
        "3.4",
        "3.5",
        "3.6",
        "3.6.5",
        "3.7",
        "3.7.3",
        "3.8",
        "3.8.4",
        "3.8.5",
        "3.9",
        "3.9.2",
        "3.9.3",
        "3.9.4",
        "3.9.5",
        "3.9.6",
        "edge",
        "latest"
    ],
    "Created": "2022-04-04T23:57:35.203088109Z",
    "DockerVersion": "20.10.12",
    "Labels": null,
    "Architecture": "arm",
    "Os": "linux",
    "Layers": [
        "sha256:57fb4b5f1a47c953ca5703f0f81ce14e5d01cf23aa79558b5adb961cc526e320"
    ],
    "Env": [
        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
    ]
}
```
`"Architecture": "arm"` `"Os": "linux"` と表示されていることが確認できます。

また、`skopeo inspect --config` コマンドで、`Image Configuration` を確認することができます。
__OCI Image Configuration__
https://github.com/opencontainers/image-spec/blob/main/config.md

```shell
$ skopeo inspect --config --override-arch=arm --override-os=linux --override-variant=v7 docker://docker.io/library/alpine:3.15
{
    "created": "2022-04-04T23:57:35.203088109Z",
    "architecture": "arm",
    "variant": "v7",
    "os": "linux",
    "config": {
        "Env": [
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
        ],
        "Cmd": [
            "/bin/sh"
        ]
    },
    "rootfs": {
        "type": "layers",
        "diff_ids": [
            "sha256:f3cad9f8ac1ee2f04e09db4d66d14c7806b881917a632bed9ae1117c6b10e1f9"
        ]
    },
    "history": [
        {
            "created": "2022-04-04T23:57:34.600819269Z",
            "created_by": "/bin/sh -c #(nop) ADD file:20f8cdddc53a4a8bd78945fc32fe08e9f80ab3b16dc20a9aa4ba73b79f2bc71c in / "
        },
        {
            "created": "2022-04-04T23:57:35.203088109Z",
            "created_by": "/bin/sh -c #(nop)  CMD [\"/bin/sh\"]",
            "empty_layer": true
        }
    ]
}
```
`"architecture": "arm"` `"variant": "v7"` `"os": "linux"` と表示されていることが確認できます。
#### 特定 Architecture Image の取得（Pull）
`Multi-Architecture Image` の Image Repository から、特定 Architecture Image を取得することもできます。
何も指定せずに Image を Pull すると、現在の環境に対応した Image が取得されます。
```shell
$ podman pull docker.io/library/alpine:3.15
Trying to pull docker.io/library/alpine:3.15...
Getting image source signatures
Copying blob df9b9388f04a done
Copying config 0ac33e5f5a done
Writing manifest to image destination
Storing signatures
0ac33e5f5afa79e084075e8698a22d574816eea8d7b7d480586835657c3e1c8b

$ podman image ls
REPOSITORY                TAG         IMAGE ID      CREATED       SIZE
docker.io/library/alpine  3.15        0ac33e5f5afa  2 months ago  5.86 MB

$ podman inspect 0ac33e5f5afa | jq -rc '.[].Architecture'
amd64

$ podman image inspect 0ac33e5f5afa
[
     {
          "Id": "0ac33e5f5afa79e084075e8698a22d574816eea8d7b7d480586835657c3e1c8b",
          "Digest": "sha256:4edbd2beb5f78b1014028f4fbb99f3237d9561100b6881aabbf5acce2c4f9454",
          "RepoTags": [
               "docker.io/library/alpine:3.15"
          ],
          "RepoDigests": [
               "docker.io/library/alpine@sha256:4edbd2beb5f78b1014028f4fbb99f3237d9561100b6881aabbf5acce2c4f9454",
               "docker.io/library/alpine@sha256:a777c9c66ba177ccfea23f2a216ff6721e78a662cd17019488c417135299cd89"
          ],
          "Parent": "",
          "Comment": "",
          "Created": "2022-04-05T00:19:59.912662499Z",
          "Config": {
               "Env": [
                    "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
               ],
               "Cmd": [
                    "/bin/sh"
               ]
          },
          "Version": "20.10.12",
          "Author": "",
          "Architecture": "amd64",
          "Os": "linux",
          "Size": 5859398,
          "VirtualSize": 5859398,
          "GraphDriver": {
               "Name": "overlay",
               "Data": {
                    "UpperDir": "/var/lib/containers/storage/overlay/4fc242d58285699eca05db3cc7c7122a2b8e014d9481f323bd9277baacfa0628/diff",
                    "WorkDir": "/var/lib/containers/storage/overlay/4fc242d58285699eca05db3cc7c7122a2b8e014d9481f323bd9277baacfa0628/work"
               }
          },
          "RootFS": {
               "Type": "layers",
               "Layers": [
                    "sha256:4fc242d58285699eca05db3cc7c7122a2b8e014d9481f323bd9277baacfa0628"
               ]
          },
          "Labels": null,
          "Annotations": {},
          "ManifestType": "application/vnd.docker.distribution.manifest.v2+json",
          "User": "",
          "History": [
               {
                    "created": "2022-04-05T00:19:59.790636867Z",
                    "created_by": "/bin/sh -c #(nop) ADD file:5d673d25da3a14ce1f6cf66e4c7fd4f4b85a3759a9d93efb3fd9ff852b5b56e4 in / "
               },
               {
                    "created": "2022-04-05T00:19:59.912662499Z",
                    "created_by": "/bin/sh -c #(nop)  CMD [\"/bin/sh\"]",
                    "empty_layer": true
               }
          ],
          "NamesHistory": [
               "docker.io/library/alpine:3.15"
          ]
     }
]
```
ここで、先程と同様に `--arch=arm` `--os=linux` `--variant=v7` を指定してみます。
```shell
$ podman pull --arch=arm --os=linux --variant=v7 docker.io/library/alpine:3.15
Trying to pull docker.io/library/alpine:3.15...
Getting image source signatures
Copying blob 57fb4b5f1a47 done
Copying config d378343b49 done
Writing manifest to image destination
Storing signatures
d378343b49e42a7f34e8c5a63abea857964e5ebc62628e6f9d21dda419f0efc3

$ podman image ls
REPOSITORY                TAG         IMAGE ID      CREATED       SIZE
<none>                    <none>      0ac33e5f5afa  2 months ago  5.86 MB
docker.io/library/alpine  3.15        d378343b49e4  2 months ago  4.09 MB

$ podman inspect d378343b49e4 | jq -rc '.[].Architecture'
arm

$ podman inspect d378343b49e4
[
     {
          "Id": "d378343b49e42a7f34e8c5a63abea857964e5ebc62628e6f9d21dda419f0efc3",
          "Digest": "sha256:4edbd2beb5f78b1014028f4fbb99f3237d9561100b6881aabbf5acce2c4f9454",
          "RepoTags": [
               "docker.io/library/alpine:3.15"
          ],
          "RepoDigests": [
               "docker.io/library/alpine@sha256:4edbd2beb5f78b1014028f4fbb99f3237d9561100b6881aabbf5acce2c4f9454",
               "docker.io/library/alpine@sha256:dc18010aabc13ce121123c7bb0f4dcb6879ce22b4f7c65669a2c634b5ceecafb"
          ],
          "Parent": "",
          "Comment": "",
          "Created": "2022-04-04T23:57:35.203088109Z",
          "Config": {
               "Env": [
                    "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
               ],
               "Cmd": [
                    "/bin/sh"
               ]
          },
          "Version": "20.10.12",
          "Author": "",
          "Architecture": "arm",
          "Os": "linux",
          "Size": 4087379,
          "VirtualSize": 4087379,
          "GraphDriver": {
               "Name": "overlay",
               "Data": {
                    "UpperDir": "/var/lib/containers/storage/overlay/f3cad9f8ac1ee2f04e09db4d66d14c7806b881917a632bed9ae1117c6b10e1f9/diff",
                    "WorkDir": "/var/lib/containers/storage/overlay/f3cad9f8ac1ee2f04e09db4d66d14c7806b881917a632bed9ae1117c6b10e1f9/work"
               }
          },
          "RootFS": {
               "Type": "layers",
               "Layers": [
                    "sha256:f3cad9f8ac1ee2f04e09db4d66d14c7806b881917a632bed9ae1117c6b10e1f9"
               ]
          },
          "Labels": null,
          "Annotations": {},
          "ManifestType": "application/vnd.docker.distribution.manifest.v2+json",
          "User": "",
          "History": [
               {
                    "created": "2022-04-04T23:57:34.600819269Z",
                    "created_by": "/bin/sh -c #(nop) ADD file:20f8cdddc53a4a8bd78945fc32fe08e9f80ab3b16dc20a9aa4ba73b79f2bc71c in / "
               },
               {
                    "created": "2022-04-04T23:57:35.203088109Z",
                    "created_by": "/bin/sh -c #(nop)  CMD [\"/bin/sh\"]",
                    "empty_layer": true
               }
          ],
          "NamesHistory": [
               "docker.io/library/alpine:3.15"
          ]
     }
]
```
先に取得した amd64 Image（IMAGE ID: 0ac33e5f5afa）と REPOSITORY、TAG が重複するため、amd64 Image が `Dangling Image` として扱われている事が分かります。
__Prune unused Docker objects__
https://docs.docker.com/config/pruning/

なお、当然ながら arm Image を amd64 環境で実行することはできません。
```shell
$ podman inspect docker.io/library/alpine:3.15 | jq -rc '.[].Architecture'
arm

$ podman run -ti docker.io/library/alpine:3.15 sh
standard_init_linux.go:228: exec user process caused: exec format error
```
このように、`podman` コマンドや `skopeo` コマンドを利用して、`Multi-Architecture Image` の Manifest の確認や、特定 Architecture Image の取得をすることができます。