🛠️

CIで利用するCLIツールをaquaで管理してみよう

5 min read 12

2022/01/08 更新: aqua-installerのGitHub Actionsのinput名の間違いを修正しました。
2022/01/09 更新: キャッシュの設定を見直しました。(コメント欄参照)
2022/01/11 更新: aqua v0.10.0 の変更に対応

Kubernetes向けのカスタムコントローラーやアプリケーションを開発する際には、kubectl, kustomize, helm, kind, kubebuilderなど様々なCLIツールを利用します。
これらのCLIツールは定期的にアップデートすることになりますが、なるべく少ない手間で更新したいですよね。

今回はaquaというツールを利用して、CIで利用するCLIツールをメンテナブルに管理する方法を紹介したいと思います。

https://aquaproj.github.io

これまでの管理方法

まずは、CLIツールをどのように管理しているのか、いくつかのOSSを調べてみました。
多くの場合は以下のようにMakefileやシェルスクリプト内にCLIツールのバージョンとセットアップの処理を書いていました。

KUSTOMIZE_VERSION = 4.4.1
HELM_VERSION = 3.7.1

KUSTOMIZE := $(shell pwd)/bin/kustomize
.PHONY: kustomize
kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary.
$(KUSTOMIZE):
	mkdir -p bin
	curl -fsL https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv$(KUSTOMIZE_VERSION)/kustomize_v$(KUSTOMIZE_VERSION)_linux_amd64.tar.gz | \
	tar -C bin -xzf -

HELM := $(shell pwd)/bin/helm
.PHONY: helm
helm: $(HELM) ## Download helm locally if necessary.
$(HELM):
	mkdir -p $(BIN_DIR)
	curl -L -sS https://get.helm.sh/helm-v$(HELM_VERSION)-linux-amd64.tar.gz \
	  | tar xz -C $(BIN_DIR) --strip-components 1 linux-amd64/helm

しかし、この方式ではあまりメンテしやすくはありません。
Makefile内に埋め込まれているバージョン番号を見つけて手動で更新する必要がありますし、Makefileの記述も長くなりがちです。

一方、Argo CDでは以下のような専用のインストールスクリプトを用意しています。

https://github.com/argoproj/argo-cd/tree/master/hack/installers

Makefile内に直書きするのに比べるとメンテしやすくなってますが、こういったスクリプトを用意するのはなかなか大変です。

また、私は以前asdfというツールを利用してCLIツールを管理する仕組みを構築しようとしました。
しかし、インストールするCLIごとにプラグインをインストールする必要があるなど、あまり使い勝手のよいものではありませんでした。

aquaによるCLIツールの管理

CLIツールの管理方法を模索していたところ、以下のような記事を見つけました。

https://zenn.dev/shunsuke_suzuki/articles/what-is-aqua

aquaを利用すると、私が課題に感じていたことを解決できそうです。そこでaquaを利用してCLIツールを管理してみようと思います。

まずは下記のページの通り、aquaのインストールをおこないます。

https://aquaproj.github.io/docs/tutorial-basics/quick-start

次に対象のリポジトリで初期化をおこないます。下記のコマンドを実行するとaqua.yamlが生成されます。

$ aqua init

次にaqua gコマンドでインストールしたいツールを検索し、aqua.yamlに追加していきます。

$ aqua g >> aqua.yaml

最終的なaqua.yamlは以下のような内容になりました。

---
# aqua - Declarative CLI Version Manager
# https://aquaproj.github.io/
registries:
  - type: standard
    ref: v0.13.1 # renovate: depName=aquaproj/aqua-registry

packages:
  - name: kubernetes/kubectl@v1.23.0
  - name: kubernetes-sigs/kubebuilder@v3.2.0
  - name: kubernetes-sigs/kustomize@kustomize/v4.4.1
  - name: kubernetes-sigs/kind@v0.11.1
  - name: tilt-dev/tilt@v0.23.4
  - name: tilt-dev/ctlptl@v0.7.0

ローカルで開発する場合は、以下のコマンドを実行することでCLIツールが利用できるようになります。

$ aqua i -l

GitHub ActionsでCLIツールのセットアップ

続いて、GitHub ActionsでCLIツールをセットアップします。
Actionが用意されているのでこれを利用しましょう。

https://github.com/aquaproj/aqua-installer

以下のように、uses: aquaproj/aqua-installerと記述するだけで、aqua.yamlに記述したツールがインストールされます。

name: CI
jobs:
  test:
    name: Test
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2
      - uses: aquaproj/aqua-installer@v0.6.0
        with:
          aqua_version: v0.10.0
      - run: make test

ツールのパスはGITHUB_PATH環境変数に追加されているので、以降のステップではkubectlやkustomizeなどのCLIが実行できます。

CLIツールのキャッシュ

CIを実行するたびにCLIツールをダウンロードしていると、実行時間が長くなることもあるでしょう。
そこで一度ダウンロードしたツールはキャッシュしておきます。

name: CI
jobs:
  test:
    name: Test
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2
      - name: Cache Tools
        id: cache-tools
        uses: actions/cache@v2
        with:
          path: ~/.local/share/aquaproj-aqua
          key: cache-tools-${{ hashFiles('aqua.yaml') }}
      - uses: aquaproj/aqua-installer@v0.6.0
        with:
          aqua_version: v0.10.0
	  aqua_opts: ""
      - run: make test

このようにaqua.yamlのハッシュ値をキーにして、~/.local/share/aquaproj-aquaディレクトリをキャッシュしておけば、CLIツールのバージョンが同一であればキャッシュが利用されるようになります。

また、aqua-installerではデフォルトでaqua iコマンドに-lオプション[1]が付与されており、実際にツールを利用するまでダウンロードが遅延されるようになっています。
そのためキャッシュを利用する場合は、aqua_optsに空文字を指定して-lオプションを取り除くようにしておきます。
ただし-lオプションを取り除くと、CI内で利用していないツールもダウンロードされてしまうので、余計に時間がかかってしまう場合もあります。キャッシュの有無は状況に応じて使い分けるようにしてください。

Renovateによる自動更新

aquaはRenovateにも対応しています。

https://github.com/aquaproj/aqua-renovate-config

Renovateを導入済みであれば[2]renovate.jsonに以下のような設定を追加するだけです。

{
  "extends": [
    "config:base",
    "github>aquaproj/aqua-renovate-config#0.1.8"
  ]
}

CLIツールの新しいバージョンがリリースされると、以下のようなPRが自動生成されます。

https://github.com/zoetrope/website-operator/pull/34

これをマージするだけで、CLIツールの更新が完了します。

まとめ

aquaを利用することで、いいかんじにCLIツールの管理ができるようになりました。
GitHub ActionsやRenovateに対応しているので非常に導入しやすいですね。

以下のリポジトリにaquaを利用したツール管理の仕組みを取り入れたので、よろしければ参考にしてみてください。

https://github.com/zoetrope/website-operator
脚注
  1. 詳しくは以下のページを参照: https://aquaproj.github.io/docs/tutorial-basics/install-only-link ↩︎

  2. Renovateの導入方法については公式ページをご覧ください: https://github.com/marketplace/renovate ↩︎

Discussion

素晴らしい記事をありがとうございます!一点だけ訂正させてください。

aqua-installer の GitHub Actions の input は v0.4.0 で version から aqua_version に変更されました。

https://github.com/aquaproj/aqua-installer/releases/tag/v0.4.0

これは、 Renovate によって aqua を自動で update するためです。

https://github.com/aquaproj/aqua-renovate-config/blob/0.1.8/default.json#L11-L18

GitHub Actions は input の名前が間違っていていも warning を出力するだけで失敗してくれません。
そのため version の値に関わらず、デフォルトで aqua の最新バージョンが使われてしまいます。

以下は version: v0.8.11 を指定しているにも関わらず最新の v0.8.13 が使われてしまっている例です。

https://github.com/suzuki-shunsuke/test-github-action/runs/4748029903?check_suite_focus=true

ご指摘ありがとうございます!
修正しておきます。

CLIツールのキャッシュ に少々問題があることに気づきました。

if: steps.cache-tools.outputs.cache-hit != 'true' を消せば正常に動くはずです。
これがなくても aqua はツールがキャッシュに含まれている場合、インストールがスキップされるので install はすぐ終わるはずです(試しにローカルで aqua i を2回連続で実行してみれば、2回目が即座に終わるのが確認できるはずです)。

aqua でインストールしたツールを実行するには、 aqua 自体が必要です。
これはツールの実行時に aqua が設定ファイルを読んで動的にどのツールのどのバージョンを実行するか決めるからです。

aqua の仕組みに関しては https://aquaproj.github.io/docs/reference/lazy-install が参考になるかもしれません。

そして aqua-installer はデフォルトで /usr/local/bin/aqua に aqua をインストールするので、 aqua はキャッシュには含まれません。
aqua-installer の実行をスキップすると、 aqua 自体がインストールされないので、後続のステップでツールを実行しようとしても実行できません。

なるほど。
確かにif: steps.cache-tools.outputs.cache-hit != 'true'は必要なさそうですね。

ただ、aqua-installerを実行しなくてもキャッシュから読み出したツールは実行できているようです。

https://github.com/zoetrope/website-operator/runs/4747114938?check_suite_focus=true

aqua本体がなくても、aqua-proxyがあればツールの実行はできると思っていたのですが、そういうわけではないのでしょうか?

ありがとうございます。
確かに手元の環境で試してみたところaquaなしでは動きませんでした。
ブログの内容は修正しました。

テストが通ってたのは、使ってるツールが最初からインストール済みだったんですね…😓

aqua-installerを呼び出さないと、GITHUB_PATHも追加されないですね…。

あとキャッシュする場合の注意点としては、
aqua-installer はデフォルトで -l option をつけて aqua i を実行します。
-l はシンボリックリンクだけ作ってツールのダウンロードをスキップするオプションです(ツールの実行時に自動でインストールされます)。

-l option について: https://aquaproj.github.io/docs/tutorial-basics/install-only-link

なのでキャッシュが生成される build でたまたまあるツールが実行されなかった場合、そのツールはキャッシュされないので、キャッシュが効かない場合があります。
確実にキャッシュが効くようにするには -l option を取る必要がありますが、
-l option を取ると必要ないツールまで余計にインストールされる場合もあるし、
キャッシュサイズが大きくなる場合もあるので、どうすべきか一概には言えない気もします。

個人的には最近はキャッシュせずに -l オプションをつけるようにしていますが、
これがベストかは分かりません。ユースケースにもよるかもしれません。

なるほどー。
ダウンロード時間がそれほど長くないのであれば、-lオプションで毎回インストールしてもよさそうですね。

aqua v0.10.0 がリリースされました。

https://github.com/aquaproj/aqua/releases/tag/v0.10.0
https://github.com/aquaproj/aqua/issues/532

デフォルトの install 先が変わり、 XDG Base Directory Specification に従うようになりました。
なので、 cache する場合は、 cache するディレクトリのパスを修正してください。

      - name: Cache Tools
        id: cache-tools
        uses: actions/cache@v2
        with:
          # 正確には ${XDG_DATA_HOME:-$HOME/.local/share}/aquaproj-aqua
          path: ~/.local/share/aquaproj-aqua
          key: cache-tools-${{ hashFiles('aqua.yaml') }}
      - uses: aquaproj/aqua-installer@v0.6.0 # >= v0.6.0 が必要
        with:
          aqua_version: v0.10.0
	  aqua_opts: ""

ありがとうございます。修正しておきました。

ログインするとコメントできます