Docker Hubのイメージのタグ一覧や対応アーキテクチャを調べるコマンドを作る

`docker search` コマンドで，Docker Hubのイメージ名を検索できます。しかし，そのイメージにどのようなタグがあるのかを調べる方法は用意されていません。そこで，Docker Hubのイメージのタグ一覧を取得するコマンドを作ってみましょう。

本記事ではそのコマンドを構築する過程を解説しますが，**手っ取り早く完成形を見たい人は，[記事の一番下](#完成形)に飛んでください。**

# API v1 は停止されている

「Docker Hubのタグ一覧を取得するには」といった内容でGoogle検索すると，

```
https://registry.hub.docker.com/v1/repositories/＜イメージ名＞/tags
```

というURLの HTTP API に `curl` でアクセスする方法がよくヒットします。しかし，Docker Hub の API v1 は2022年を最後に停止されており，現在ではこの方法は使えません。

# API v2 を使う方法（※個数制限あり）

次に，API v2 を使う簡単な方法を調べてみると，

```
https://hub.docker.com/v2/repositories/＜ネームスペース名＞/＜イメージ名＞/tags?page_size=100
```

というURLにアクセスして得られるJSONをパースする方法がヒットします。`＜ネームスペース名＞` の部分は，公式イメージならば `library`, 個々のユーザのイメージの場合はユーザ名を指定します。例えば Ubuntu の公式イメージのタグをリストアップするのであれば，

```bash
curl -s "https://hub.docker.com/v2/repositories/library/ubuntu/tags?page_size=100"
```

としてアクセスすると，

```json
{
  "count": 621,
  "next": "https://hub.docker.com/v2/repositories/library/ubuntu/tags?page=2&page_size=100",
  "previous": null,
  "results": [
    {
      "creator": 7,
      "id": 2343,
      "images": [
      （中略）,
      "name": "latest",
      （後略）
```

というJSONが得られます。これを`jq`でパースし，キー `"name"` に対応する値を取り出せば取り出せばよいというわけです。

そのために，次のように `jq -r '.results[].name'` にパイプで渡しましょう。

```bash
curl -s "https://hub.docker.com/v2/repositories/library/ubuntu/tags?page_size=100" | jq -r '.results[].name'
```

すると，確かにタグのリストが得られます。

`jq`がインストールされていないシステムの場合，`apt` や `brew`, `port` といったパッケージ管理コマンドでインストールするのが一手です。ソースコードからコンパイルするのもお手軽です。その方法は次の記事で示しました。

https://qiita.com/doraTeX/items/74afe5a6594723f5daf4

## 問題点：個数100個制限

ただし，この方法には問題点があります。URLの `?page_size=100` というクエリ文字列に現れているように，このURLにアクセスすると，100個までしかタグを取得できません。ならばこの`100`をもっと大きい数字に指定すればよいのでは，と思われるでしょうが，この数字は**最大値が100**であり，これ以上大きな値を指定しても100個で止められてしまいます。

先程のJSONの返値に

```json
"count": 621,
"next": "https://hub.docker.com/v2/repositories/library/ubuntu/tags?page=2&page_size=100"
```

という行があります。これは，

* 全部で621個ある
* 次の100個を見るにはこのURLにアクセスしてね

ということを意味しています。よって，「次のURL指定があればそこにアクセスする」という行為を繰り返せば，全部のタグを取得できることになります。

しかし，そのような判定＆反復を行うスクリプトを書くのは若干手間がかかりますし，HTTPアクセスを無用に繰り返すことになるのは何とも筋が悪いです。何とかして，全部のタグを一括取得する方法はないでしょうか。

# API v2 で全タグを一括取得するには（個数制限なし）

個数制限なくタグを一括取得する方法は，次の記事に調査結果がまとめられています。

https://qiita.com/inajob/items/d4e13f85eb855e760e76

この記事での調査結果に基づくと，Docker Hub のイメージのタグ名を取得するには，

1. まず `https://auth.docker.io/token?scope=repository%3A＜ネームスペース名＞%2F＜イメージ名＞%3Apull&service=registry.docker.io` に取得して `token` を取得する。このトークンは300秒間有効。
1. 次に HTTP のリクエストヘッダに `Authorization: Bearer ＜得られたトークン＞` を付け加えた上で，`https://registry-1.docker.io/v2/＜ネームスペース名＞/＜イメージ名＞/tags/list` というURLに対して HTTP GET Request を投げると，タグの一覧がJSONとして返ってくる。

となっています。`curl` と `jq` を駆使してそのようなアクセスをするには，次のようにします。

```bash
export NAMESPACE=library
export IMAGE=ubuntu
curl -s -H "Authorization: Bearer $(curl -s "https://auth.docker.io/token?scope=repository%3A${NAMESPACE}%2F${IMAGE}%3Apull&service=registry.docker.io" | jq -r '.token')" "https://registry-1.docker.io/v2/${NAMESPACE}/${IMAGE}/tags/list" | jq
```

```json
{
  "name": "library/ubuntu",
  "tags": [
    "10.04",
    "12.04",
    "12.04.5",
    （後略）
```

という形のJSONが得られるので，これを `jq -r '.tags[]'` で抽出すれば，タグのリストを一括で取得できる，というわけです。

# API v2 でイメージの対応CPUアーキテクチャを調べるには

Apple Silicon Mac を使っている場合などには，各イメージの対応CPUアーキテクチャが気になります。イメージは `amd64` のみ対応というケースが多いので，Apple Silicon ユーザの場合は `arm64` に対応しているかどうかに関心を持つケースが多いでしょう。

対応アーキテクチャを調べるには，イメージのマニフェストを調べる必要があります。[API ドキュメント](https://distribution.github.io/distribution/spec/api/)によれば，イメージの マニフェストは次の方法で取得できることが分かります。

1. まず `https://auth.docker.io/token?scope=repository%3A＜ネームスペース名＞%2F＜イメージ名＞%3Apull&service=registry.docker.io` に取得して `token` を取得する。このトークンは300秒間有効。
1. 次に HTTP のリクエストヘッダに `Authorization: Bearer ＜得られたトークン＞` を付け加えた上で，`https://registry-1.docker.io/v2/＜ネームスペース名＞/＜イメージ名＞/manifests/＜タグ名＞` というURLに対して HTTP GET Request を投げると，manifest がJSONとして返ってくる。

## 2パターンのマニフェストJSONに注意

`docker build` でビルドされた単一アーキテクチャイメージと，`docker buildx` でビルドされたマルチアーキテクチャイメージでは，返ってくるマニフェストJSONの構造が異なるため，その後のJSONのパースにおいて注意が必要です。

### 単一アーキテクチャイメージの場合

```json
{
  "schemaVersion": 1,
  "name": "mysql/mysql-server",
  "tag": "latest",
  "architecture": "amd64"
（後略）
```

このように，素直に直下に `"architecture"` というキーがあるので，その値を `jq -r '.architecture'` で取り出せば済みます。

### マルチアーキテクチャイメージの場合

```json
{
  "manifests": [
    {
      "digest": "sha256:28a2b328825ac02db5c29d9356bfba80ba6c4b6ebfcac5a91a4656fa3453775a",
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      },
      "size": 424
    },
    {
      "digest": "sha256:c94d54dd82aba50b58c698accfc4a0a849955ba93f3a1de76353326caf2e9229",
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "platform": {
        "architecture": "arm",
        "os": "linux",
        "variant": "v7"
      },
      "size": 424
    },
  （後略）
  ```
  
  というように，各アーキテクチャ用イメージのマニフェストがリストで並んできます。これをパースして `"architecture"` の値だけを取り出すには，`jq -r '.manifests[].platform.architecture'` で抽出する必要があります。

両方のパターンに対応するため，

```sh
jq -r '.architecture // .manifests[].platform.architecture'
```

でフィルタすることにしましょう。

# 完成形

こうして完成した手法を，シェル関数の形でまとめました。

## シェル関数のソース
https://github.com/doraTeX/search-dockerhub

## 使い方

### 1. `docker-list-tags` 関数：全タグのリストアップ

`docker-list-tags` 関数を指定します。引数には，公式イメージならばイメージ名のみ，各ユーザに属するイメージの場合は `ユーザ名/イメージ名` を指定します。

```bash
docker-list-tags ubuntu
docker-list-tags mysql/mysql-server
```

#### オプション

* `-n`, `--with-name`: デフォルトでは `22.04` のようにタグ名だけを出力しますが，このオプションを付けると，`ubuntu:22.04` のようにイメージ名を付けた出力になります。
* `-j`, `--json`: タグ一覧を改行区切りテキストではなく，JSONとして出力します。出力結果を `jq` でさらに加工したい場合に使えます。

### 2. `docker-inspect-architecture` 関数：対応CPUアーキテクチャの調査

次のような書式で指定します。タグ名を略した場合は `:latest` が補われます。マルチアーキテクチャイメージの場合は対応する全アーキテクチャが列挙されます。

```bash
docker-inspect-architecture ubuntu
docker-inspect-architecture ubuntu:22.04
docker-inspect-architecture mysql/mysql-server
docker-inspect-architecture mysql/mysql-server:8.0
```