Docker Registry HTTP API V2を使ってOracle Container Registryからイメージタグ一覧を取得してみました

Docker Registry HTTP API V2を使ってOracle Container Registryからイメージタグ一覧を取得してみました

Clock Icon2025.05.28

初めに

Oracle DBやOracle JDKなどのOracleの各種製品を使って環境を構築することはあるかと思います。

コンテナで環境を立てようと思った際にベースイメージを探してみるとDocker Hub等メジャーなコンテナレジストリーでは公開がなく、現時点で私が調べる限り検索エンジン結果より直接公開されているようなサイトは出てくるのを見つけられませんでした。

※ 「oracle jdk docker image」で調べると先にOracle公式リポジトリのイメージの作り方のREADMEが出てきたりします。

https://github.com/oracle/docker-images/blob/main/OracleJava/

が、実際にはOracle Container Registry(OCR)と呼ばれるOracle公式のレジストリがあり、こちらで各種Oracle製品系のコンテナイメージが提供されています。
現時点ではOracle Container Registryで調べても検索結果の上位にこのサイト自体直接上がってこないので辛い部分ではありますが、一応先ほどのワードでも調べて出てくるOracleのドキュメント等で副次的に辿り着くことはできます。

https://container-registry.oracle.com/

Github Actionsでこちらから取得したOracleJDKイメージをベースにコンテナをビルドしECSにデプロイ、という処理を日常的に行っていましたがどうも時折調子が悪いのかイメージの取得の際にエラーとなってしまう現象が発生しておりました。

現在は開発中であまりクリティカルではないものの本番リリース後にこちらに当たると少し苦しいため、定期的にOCRからイメージを取得しECRに移すようにすることになりましたが、その際に最新のタグってどうやって取得するんだっけ?となって調べたので備忘録として残しておきます。

※ 通常はメジャーバージョンの指定(マイナー・パッチバージョンは常に最新)ですが、もしも用にメジャー未満指定のイメージを直近数世代持っておこうということより取得しています

Docker Container Registry APIの仕様で提供されていた

前述の通りOCR自体が検索にうまく引っかからないためか関連する情報が出て来ず、かつOCRで調べるとOracle Cloud側で自身がホストするタイプのものしか出てこない...という状態ですが各レジストリ個別の仕様ではなくDocker Registry HTTP APIの標準仕様で共通定義されているようです。

Docker Registry HTTP APIはDockerイメージを配布するレジストリ側で提供するAPIの標準規格であり、こちらに準拠しているレジストリであれば今回実装するタグ一覧の取得のような操作をユーザ側からはどのレジストリでも同じような操作で実現することができます。

本記事執筆時点ではメジャーなのはV2のようです。

https://matsuand.github.io/docs.docker.jp.onthefly/registry/spec/api/

https://docs.oracle.com/ja-jp/iaas/Content/Registry/Concepts/registryoverview.htm
Container Registryは、DockerレジストリHTTP API (およびOracle Cloud Infrastructure API)を使用してイメージを管理できるDockerプロトコルを完全に実装していることに注意してください。リージョン・エンドポイントのリストはコンテナ・レジストリの準備を、DockerレジストリのHTTP APIの使用についてはDockerのドキュメントを参照してください。

いまだに区分けが自分の中で微妙でこちらのドキュメントで良いかわかりませんが、おそらく前述のOracle管理の各種イメージ提供のレジストリも準拠しているものとみられます。

Docker CLIではリモート上のイメージのメタ情報を取得するようなものは用意されていないため、取得操作は別の手段で実現する必要があります。

直接APIを叩く

良い方法は後述しますがせっかくなのでこういうのはストロングスタイルで直接APIを叩いて理解しましょう。

タグのリストは/v2/{{リポジトリ}}/tags/listとなるようです。

https://matsuand.github.io/docs.docker.jp.onthefly/registry/spec/api/#listing-image-tags

% curl https://container-registry.oracle.com/v2/oracle/jdk/tags/list
{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":[{"Type":"repository","Class":"","Name":"oracle/jdk","Action":"pull"}]}]}

...認証エラーが起きますね。一旦最小のAPIで/v2/が動作確認に使えるみたいなのでそちらを試してみましょう。

% curl https://container-registry.oracle.com/v2/
{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":null}]}

こちらもダメなようです。

https://matsuand.github.io/docs.docker.jp.onthefly/registry/spec/api/
If a 401 Unauthorized response is returned, the client should take action based on the contents of the “WWW-Authenticate” header and try the endpoint again. Depending on access control setup, the client may still have to authenticate against different resources, even if this check succeeds.

OCRのログイン情報をAuthorizationヘッダに含めてもダメで何を入れたら...というところでしたがどうやら認証が必要な場合WWW-Authenticateに認証用のURLが入っているのでまずはそちらで認証を行う必要があるようです。

HEADリクエストをかけてみるとwww-authenticateにそれらしきURLが入っていますね。

% curl https://container-registry.oracle.com/v2/ -I
HTTP/2 401
content-type: application/json; charset=utf-8
content-length: 87
docker-distribution-api-version: registry/2.0
www-authenticate: Bearer realm="https://container-registry.oracle.com/auth",service="Oracle
...

% curl https://container-registry.oracle.com/v2/java/jdk/tags/list -I
HTTP/2 401
content-type: application/json; charset=utf-8
content-length: 151
docker-distribution-api-version: registry/2.0
www-authenticate: Bearer realm="https://container-registry.oracle.com/auth",service="Oracle Registry",scope="repository:java/jdk:pull"

こちらにブラウザでアクセスしてみるとBasic認証の画面が表示されたのでOCRで利用する認証情報でログインしてしまえば良さそうです。

ocr-basic-auth

OKうまくとれました。access_tokentokenの値は一緒だったのでどちらでも良さそうでしょうか?

% curl -u "{{username}}:{{password}}" "https://container-registry.oracle.com/auth"
{"access_token":"xxxxx", "token": "xxxxx"}

こちらをのせて再度送信してみます。

% curl -H "Authorization: Bearer xxxx" https://container-registry.oracle.com/v2/java/jdk/tags/list -I
HTTP/2 401
content-type: application/json; charset=utf-8
content-length: 151
docker-distribution-api-version: registry/2.0
www-authenticate: Bearer realm="https://container-registry.oracle.com/auth",service="Oracle Registry",scope="repository:java/jdk:pull",error="invalid_token"

まだ認証を求められます...。最初の結果と合わせScopeというワードが気になり少し調べてみたところトークン発行時にはその利用スコープの指定が必要なようです。

https://matsuand.github.io/docs.docker.jp.onthefly/registry/spec/auth/scope/

上記ページにABNF表記でフォーマットが記載されておりそれぞれの要素をコロンで繋げたものをクエリパラメータに付与すれば良さそうです。各種値の情報は先ほどのwww-authenticateやレスポンスボディに含まれている値を利用します。

scope                   := resourcescope [ ' ' resourcescope ]*
resourcescope           := resourcetype  ":" resourcename  ":" action [ ',' action ]*
resourcetype            := resourcetypevalue [ '(' resourcetypevalue ')' ]
resourcetypevalue       := /[a-z0-9]+/
resourcename            := [ hostname '/' ] component [ '/' component ]*
hostname                := hostcomponent ['.' hostcomponent]* [':' port-number]
hostcomponent           := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
port-number             := /[0-9]+/
action                  := /[a-z]*/
component               := alpha-numeric [ separator alpha-numeric ]*
alpha-numeric           := /[a-z0-9]+/
separator               := /[_.]|__|[-]*/

リクエストは以下の通りです。Serviceに空白が混じってるので%20にしておきましょう(URLエンコード)。

% curl -u "{{username}}:{{password}}" "https://container-registry.oracle.com/auth?service=Oracle%20Registry&scope=repository:java/jdk:pull"
{"access_token":"xxxxx", "token": "xxxxx"}

先ほど同様に上記のtokenの値をAuthorizationヘッダに乗せてリクエストかけましょう。

 % curl -H "Authorization: Bearer xxxx" https://container-registry.oracle.com/v2/java/jdk/tags/list
 {"name":"java/jdk","tags":["1.8.0_371-oraclelinux7","1.8.0_371-oraclelinux8","1.8.0_371","1.8.0_381-oraclelinux7","1.8.0_381-oraclelinux8","1.8.0_381","1.8.0_391-oraclelinux7","1.8.0_391","1.8.0_401-oraclelinux7","1.8.0_401-oraclelinux8","1.8.0_401","1.8.0_411-oraclelinux7","1.8.0_411-oraclelinux8","1.8.0_411","1.8.0_421-oraclelinux7","1.8.0_421-oraclelinux8","1.8.0_421","1.8.0_431-oraclelinux7","1.8.0_431-oraclelinux8","1.8.0_431","1.8.0_441-oraclelinux8","1.8.0_441",...,"latest"]}

OK!取れました。あとは必要に応じてjqコマンドなどでよしなに加工して必要な値を取りましょう。

% curl... | jq '[.tags[] | select(test("^21\\.0\\.[0-9]+$")?)]'
[
  "21.0.1",
  "21.0.2",
  "21.0.3",
  "21.0.4",
  "21.0.5",
  "21.0.6",
  "21.0.7"
]

そんなことをしなくてもskopeoがある

大体こういった標準化されているかつメジャーなものはみんな使うはずで調べてみればあるでしょう、ということでskopeoというものがあるようです。
スター数も多くOrgを見るとpodmanを管理しているところなので自分が知らないだけで比較的メジャーなツールなのかな?と思います。

https://github.com/containers/skopeo

こちらであれば認証情報と操作、対象のイメージのURLを渡すだけで認証から取得まで1コマンドでよしなにやってくれます。楽ですね。

 % skopeo list-tags docker://container-registry.oracle.com/java/jdk --username xxxxx --password xxxxx
{
    "Repository": "container-registry.oracle.com/java/jdk",
    "Tags": [
        "1.8.0_371-oraclelinux7",
        "1.8.0_371-oraclelinux8",
        "1.8.0_371",
        "1.8.0_381-oraclelinux7",

一応生のAPIと見比べるとキー名が違う(大文字開始、nameじゃなくてRepositoryになっている)等あるので、生のAPIを叩いてる処理を差し替える場合は後続の処理も少し変える形になりそうです。

終わりに

今回はOCRからコンテナタグの一覧取得を試してみました。

ツールがある程度自由に入れられる環境であればskopeoを使うのがより楽ではありますが、環境上の兼ね合いで導入難しくてもjqcurlあたりがあれば少し加工してやればできそうなので利用はお手軽かと思います。

また、今回はOCRで試しましたが標準化された仕様あり他のレジストリでも利用可能な汎用的な手段となりますので、覚えておいて損はないでしょう。

## ECRの場合
## パブリックリポジトリでも一応認証は必要らしい
% curl https://public.ecr.aws/v2/cloudwatch-agent/cloudwatch-agent/tags/list -I
HTTP/2 401
date: Wed, 28 May 2025 02:34:05 GMT
content-type: application/json; charset=utf-8
content-length: 58
docker-distribution-api-version: registry/2.0
www-authenticate: Bearer realm="https://public.ecr.aws/token/",service="public.ecr.aws",scope="aws"

## OCRと違い特定のユーザでログインする必要はないのでBasic認証の指定は不要
% curl 'https://public.ecr.aws/token/?service=public.ecr.aws&scope=aws'
{"token":"xxxxx"}

% curl -H "Authorization: Bearer xxxxxx" https://public.ecr.aws/v2/cloudwatch-agent/cloudwatch-agent/tags/list
{"name":"cloudwatch-agent/cloudwatch-agent","tags":["1.300053.0b1046", "..."]}

余談:認証方式の変更によりCLI操作時のOCRのパスワードの扱いが変わるようです(2025/06/30より)

OCRトップに表示されていますが、Docker CLIを使う場合(Docker CLIではないですが今回の方法もそうでしょうか?)のパスワードがWeb側のログインに使うものと別物になるようです。

ocr-auth-warning

専用のトークンの発行とはなりますが対応としては画面右上の自身のユーザ名より「Auth Token」に遷移、さらにGenerate Secret Keyを押すと発行&クリップボードにコピーされる専用のパスワード文字列を今後利用するだけですので忘れないうちに差し替えておきましょう。

ocr-menu-auth-token

ocr-token-generate

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.