Docker のサブコマンド docker-tags を作ってみました

皆さんこんにちは,D2 の三宅です.
本日は Docker のサブコマンドを作ってみた話をしていきたいと思います.

はじめに

突然ですが,Docker イメージ (コンテナイメージ) のタグを探すために Docker Hub をさまよった経験のある人はいますか?
Docker は search サブコマンドでイメージの検索はできるのですが,なぜか タグの検索はできないんですよね.

サンドボックス的に使うならタグ指定なし (= latest) でいいですが,開発環境といった統一性が重視される場合では適切なタグの指定は重要です.
しかし,その度にわざわざブラウザを開き,Docker Hub を開き,イメージを検索し,目的のタグを探すのはなかなか手間です.

というわけで,今回はイメージのタグを検索できるdocker-tagsコマンドを作成していこうと思います.
しかし,ただのツールとして作るのは面白くないので,Docker のサブコマンドとしてdocker tagsでも動作するように作成していきます.

Docker のサブコマンド?

皆さんが Docker を使う際に必ず使う docker run の ‘run’ にあたる部分がサブコマンドですね.
‘run’ の他にも色々ありますが,実はこのサブコマンドは後付けすることができるんです.
実は,docker composedocker build がこれにあたり,それぞれ docker-composedocker-buildx という別コマンドが呼び出されているんです.
これはdocker infoを実行すると確認でき,出力の上の方に各コマンドの本当のコマンド名やパスが書いてあります.

自家製ツールのサブコマンド化

このように別のコマンドをサブコマンドとして認識させるには以下の2つの条件があります.

  1. ツールが特定のディレクトリに配置されていること.
    作成したツールは (Linux の場合) /usr/exec/docker/cli-plugins または $HOME/.docker/cli-plugins 以下に配置されていなければなりません.
  2. ツールがdocker-*という名前になっていること
    作成したツールは docker-composeのように,docker から始まるコマンド名でなくてはなりません.
  3. ツールが特定のサブコマンドを実装していること.
    作成したツールは <ツール名> docker-cli-plugin-metadata というサブコマンドを実装する必要があります.
    また,このサブコマンドの出力は以下のようなYAML形式のスキーマになっている必要があります.
     {
         "SchemaVersion": "0.1.0",
         "Vendor": "miyake13000",
         "Version": "0.1.0",
         "ShortDescription": "Show all tags of the specified image"
     }
    

docker-tags コマンドの作成

というわけで,サブコマンドについては説明したので,指定したイメージの全タグを出力するdocker-tagsコマンドを作成していこうと思います.

要件

  • コマンド入力
      docker-tags [Option] IMAGE_NAME
    

    IMAGE_NAME は検索するコンテナイメージの名前,出力はタグを一覧にして表示します.

実装言語

Rustしか勝たん.
(Rust のコマンドパーサ (clap) があまりに強力すぎて,コマンドラインツールを作るならRustの右に出るものはいないと思っています.)

タグの取得方法

Docker Hub は優しいのでちゃんと API があります.
https://registry.hub.docker.com/v2/repositories/<user>/<image_name>/tags?page_size=<page_size>

  • user: イメージの所有者 (なお,公式イメージの場合は library)
  • image_name: イメージの名前
  • page_size: 1回あたりに取得できるタグの数 (ちなみに最大は100で,それ以上の値を入れても100件しか取ってこれない)

落とし穴

これ,作ったあとに気付いたんですが,Docker のサブコマンドとして使用すると,コマンドライン引数が1つずつずれるんですよね.

  • 普通に使用: $ docker-tags(#1) alpine(#2)
  • Dockerから使用: $ docker(#1) tags(#2) alpine(#3)

というわけで,第1引数がdockerであれば,第1引数を取り除いてパースするようにします.

結果

というわけで,完成品がこちらになります.

Release ページからダウンロードして,$HOME/.docker/cli-plugins 下に配置してもらうと,Docker から使うことができるようになります.

wget https://github.com/miyake13000/docker-tags/releases/latest/download/docker-tags
chmod +x docker-tags
mkdir -p $HOME/.docker/cli-plugins
mv docker-tags $HOME/.docker/cli-plugins/
docker tags alpine

Screenshot from 2024-12-19 13-19-59.png

おまけ

今回 API を叩くために HTTP クライアントの reqwest クレートを使ったのですが,async / await がわからないためブロッキング処理で書いちゃってるんです…
なので,タグが数百あるようなイメージだとすごく時間がかかってしまうんですね.
async / await,ちゃんと勉強しないとなぁ…

もし良かったら star 付けてってください!



Comment

No comment