🛠️

GitLab CIジョブの環境構築にaquaを使っている

2023/12/07に公開

この記事は GitLab Advent Calendar 2023 の 7日目の記事です。


GitLab CIジョブの環境構築

CI上でTerraformやAWS CLI、kubectlなどさまざまなツールを使って仕事をさせるのですが、環境構築において以下のような課題がありました。

  • ツールのインストール方法がまちまち
    • 同じツールでもリポジトリによって違ったりさえする
  • バージョンを固定していたりしていなかったりする
  • バージョンアップが忘れられている
    • 手動で更新すること自体が面倒
    • 加えて、インストール方法がまちまちなので変更箇所もまちまち

GitLab CIではGitHub Actionsのようにサードパーティーアクションをuseする仕組みもないため、ツールのバージョン管理はよりカオスになりやすい気がします。

https://github.com/hashicorp/setup-terraform

aquaって?

公式サイトの紹介そのままですが「宣言的CLIバージョンマネージャー」です。

https://aquaproj.github.io/

YAMLファイルにツールのバージョンを「宣言」して管理します。

registries:
- type: standard
  ref: v4.93.0 # renovate: depName=aquaproj/aqua-registry
packages:
- name: aws/aws-cli@2.14.1
- name: helm/helm@v3.13.2
- name: helmfile/helmfile@v0.159.0
- name: kubernetes/kubectl@v1.25.9
- name: mikefarah/yq@v4.40.3

aquaで管理することによって以下の恩恵を受けることができます。

  • インストール方法が統一できる
  • バージョンを固定できる、一箇所で確認できる
  • Renovateで簡単に更新ができる(config presetが提供されているため)

aquaの導入方法(詳細割愛)

aquaは公式ドキュメントが充実しています(英語)。
また、作者のShunsuke Suzukiさんが執筆された日本語の入門書もありますので、導入の詳細は割愛します。

https://aquaproj.github.io/docs/install

https://zenn.dev/shunsuke_suzuki/books/aqua-handbook/viewer/install

簡単にまとめると以下の通りです。

  • ローカルにaquaのバイナリを入手
  • aqua initaqua.yamlを生成
  • aqua g -iで欲しいツールをaqua.yamlに登録
  • aqua.yamlをリポジトリに追加
  • CI上でもaquaのバイナリを入手してaqua i実行
  • ツールのダウンロード完了(実行可能になる)

aquaの制約

いろんなパッケージマネージャーに共通することですが、registryに登録されていないツールは管理できません。
aqua gコマンドで表示されないものはstandard registryにはありません。

ほしいものがなかった場合、standard registryであるaqua-registryにコントリビュートして追加するか、local/private registryを作成することになります。

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

https://aquaproj.github.io/docs/develop-registry/local-registry

https://aquaproj.github.io/docs/develop-registry/create-private-registry

standard registryへの登録例

GitLab CIでaquaを使う

GitLab CIでaquaを使うには、各リポジトリにaqua.yamlを設置するだけでなく、ジョブ本来の仕事にかかる前にaquaそのものをインストールする過程が共通して必要になります。

なので、テンプレートを用意して各リポジトリでそれをincludeすることにします。

https://docs.gitlab.com/ee/ci/yaml/includes.html

aqua自身のインストールに使う共通テンプレート

aqua自身をインストールするためのaqua-installerというツールが用意されています。
シェルスクリプト版とGitHub Actions版があり、シェルスクリプト版を使います。

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

foo-groups/gitlab-ci-templatesリポジトリに
templates/aqua-installer.gitlab-ci.ymlというファイル名で設置します。

.aqua_installer_before_script:
  image:
    name: ubuntu:22.04
  before_script:
    - apt update
    - apt install -y curl wget
    - curl -sSfL https://raw.githubusercontent.com/aquaproj/aqua-installer/v2.2.0/aqua-installer | bash -s -- -v v2.16.4
    - export PATH="${AQUA_ROOT_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/aquaproj-aqua}/bin:$PATH"
    - aqua install --only-link

やっていること

  • aqua-installer実行の前提となるcurlとwgetを入手
  • aqua-installerのv2.2.0を実行し、aquaのv2.16.4をインストール
  • aquaのインストール先にPATHを通す
  • aqua.yamlに定義されたツールのLazy Installをする(--only-linkオプション)

Lazy Installをしないとaqua.yamlに書かれたすべてのツールを一気にダウンロードします。
Lazy Installをするとツール本体のダウンロードがツールを最初に呼び出す時点まで後回しにされます。

すべてのジョブでaqua.yamlに書かれたすべてのツールを使うわけではないためLazy Installを利用します。

aqua-installer呼び出し行のRenovateによる自動更新

config presetを利用すると、templates/aqua-installer.gitlab-ci.yml内部のaqua-installerおよびaquaのバージョンも自動更新ができます。

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": [
    "github>aquaproj/aqua-renovate-config:installer-script#1.13.0(templates/aqua-installer.gitlab-ci.yml)"
  ]
}

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

aqua-installerの引数に指定したaquaバージョンの自動更新

元々config presetはaqua-installerの自動更新に対応していましたが、引数に指定したaquaのバージョン更新には未対応でした。
aqua-renovate-config v1.3.0からaqua自身の自動更新にも対応しました。

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

テンプレート利用側の.gitlab-ci.yml

作成したテンプレートをincludeしてextendsします。
テンプレートのbefore_scriptaqua install --only-linkを済ませているので、ジョブで使うコマンドをいきなり実行することができます。

include:
  - project: foo-group/gitlab-ci-templates
    file: /templates/aqua-installer.gitlab-ci.yml

some-kubectl-job:
  extends: .aqua_installer_before_script
  script:
    - kubectl hogehoge

aquaを利用している感想

抱えていた課題が解消され、見通しのいい環境構築と快適なバージョンアップができるようになりました。

簡単に使い始めることができ、またドキュメントが充実している、アップデートが活発であるためおすすめできるツールだと思います。
フィードバックが早くて厚いため、コントリビューションも非常にしやすいプロジェクトだと思います。

少しだけ乗りこなしのいるところ

aquaの悪いところというわけではなく、実運用してみるまで気づかなかった豆知識みたいなものです。

ブランチがコンフリクトする→グルーピングを活用する

とくに設定しなければ、Renovateはaqua.yamlに書かれた各ツールそれぞれに1つのブランチとマージリクエストを作成します。
1つのMRをマージすると別のMRでコンフリクトが発生することがあり、手動でrebaseするかRenovateを再実行してRenovateにrebaseしてもらうことになります。
CIの待ち時間が長いリポジトリだと少し不便です。

registries:
- type: standard
  ref: v4.93.0 # renovate: depName=aquaproj/aqua-registry
packages:
- name: aws/aws-cli@2.14.1
- name: helm/helm@v3.13.2
- name: helmfile/helmfile@v0.159.0
- name: kubernetes/kubectl@v1.25.9
- name: mikefarah/yq@v4.40.3

これに対してはrenovate.json5側でグルーピングを設定し、ブランチとMRをまとめることで回避しています。

  "packageRules": [
    {
      "automerge": true,
      "groupName": "automergeable-tools",
      "matchPackagePatterns": [
        "aws-cli",
        "helm",
        "helmfile",
        "kubectl",
        "yq",
      ]
    }
グルーピングの留意事項

一般にたくさんまとめるほどCIが壊れやすくなり、原因特定も大変になることには注意します。

https://docs.renovatebot.com/noise-reduction/#be-smart-about-grouping-dependencies

Lazy Installと同時実行→実行前にインストールを済ませる

共通テンプレートでLazy Installを有効にしている影響で、複数のディレクトリで同時にterraformを実行するようなジョブが失敗しました。
「最初の呼び出し」が同時多発的に行われるためダウンロードも同時に複数回試みられ、ファイルが破損したのが原因でした。

そのようなジョブでは同時実行を始める前にterraform versionコマンドなどで先にダウンロードを済ませておくことで問題を回避しました。

Discussion