⚙️

タグを使って Cloud Build の同時実行のキャンセル機能を作る

に公開

はじめに

Cloud Build は Google Cloud の強力な CI/CD サービスで、アプリケーションのビルド、テスト、デプロイを自動化します。この記事では、Cloud Build のタグ機能をつかって複数の同時実行ビルドを制御するための方法について紹介します。(GitHub Actions でいうところの concurrency機能 のようなものです)

タグってなに

Cloud Build はビルド構成ファイルという YAML/JSON形式のファイルに基づいて、タスクを実行していきます。この構成ファイルには、タグ[1]というのものがあり、グルーピングやフィルタに用いることができます。
例えば以下のようなビルド構成ファイルがあったときに、gcloud builds list --filter "tags='mytag1'"といった具合でフィルターをかけることができます。[2]

sample
steps:
- name: 'gcr.io/cloud-builders/docker'
  ...
- name: 'ubuntu'
  ...
tags: ['mytag1']

他のタグ

他にも "タグ" という名前の別のものがあります。これらは今回の話では関係ありません。頭がこんがらがりますね。

Git のタグ

"新しいタグを push する" など、トリガーを起動するリポジトリイベントとしてのタグです。[3] gcloud での以下のオプションが意味するタグも Git タグのことを言っています。

# create a build trigger for a GitHub repository
gcloud builds triggers create github --tag-pattern=REGEX

# create a build trigger with a manual trigger event
gcloud builds triggers create manual --tag=TAG

トリガー設定のタグ

トリガーに情報として付与するタグです。コマンドで以下のように確認できます。

gcloud builds triggers list --format='table(id,name,tags)'

ID                                    NAME                   TAGS
86201062-3b14-4b6a-a2fb-4ee924e8b1dd  trigger-001            ['tag9']

設定するにはコンソールか、gcloud beta builds triggers exportimport [4]でやる必要がありそうです。トリガー作成時に設定する方法はわかりませんでした。

trigger-001
createTime: '2022-05-26T21:56:11.830784153Z'
filename: cloudbuild.yaml
github:
  ...
id: 86201062-3b14-4b6a-a2fb-4ee924e8b1dd
includeBuildLogs: INCLUDE_BUILD_LOGS_WITH_STATUS
name: trigger-001
tags:
- tag9


トリガーの編集

docker イメージのタグ

gcloud builds submit を使うと Dockerfile を使用してビルド構成ファイル無しでビルドすることができます。このとき、以下のようにタグを指定します。[5]

gcloud builds submit \
--region=us-west2 \
--tag us-west2-docker.pkg.dev/${PROJECT_ID}/quickstart-docker-repo/quickstart-image:tag1

タグを使って Cloud Build の同時実行を制御する

本題の前に

https://mixi-developers.mixi.co.jp/strongest-terraform-terragrunt-ci-e4c350d627e6#3d1e

この記事[6]の「ジョブのキャンセルをしよう」で cancelot.sh というものが紹介されています。このツールを使うと、指定の条件に一致したジョブをキャンセルすることができます。ちなみに、おおもとのcloud-builders-community[7]の方はメンテナンスされておらず、docker build すらできません。

cancelot.sh の拡張

このツールの--same_trigger_onlyオプションを使うと、実行されているビルド自身の「トリガーID」に基づいてキャンセルするということができます。ただし、トリガーIDのないgcloud builds submit によるビルドの実行では動作しません😢。以前の記事ではロックファイルを作成して実行を制御する方法を考案しましたが、複雑すぎるため今回はタグをつかった方法で実現しようと思います。

https://zenn.dev/dev_commune/articles/d2c46b93212e30

コード

リポジトリからファイルをダウンロードしてきます。
https://github.com/siberex/cancelot

# ダウンロードコマンド
curl -OL https://raw.githubusercontent.com/siberex/cancelot/main/cancelot.sh
# 実行権限を付与
chmod +x cancelot.sh

今回は以下のオプションをcancelot.shに追加します。カンマ区切りでタグを受けとり、各タグを OR でフィルターする仕様とします。

--tags "tag1,tag2"

はじめにオプションを受け取るためのコードを追加します。

cancelot.sh
    SAME_TRIGGER_ONLY=0
+   TAGS=""
    ---
        --region)
            REGION="$2"
            shift 2
            ;;
+       --tags)
+           TAGS="$2"
+           shift 2
+           ;;
        --help)
            usage
            ;;

次に、オプションから FILTERS としてフィルター条件を追加します。--filter=" ~略~ AND (tags='TAG1' OR tags='TAG2')" のような感じに生成されます。

cancelot.sh
    if [[ -n $TARGET_BRANCH ]]; then
        FILTERS="$FILTERS AND substitutions.BRANCH_NAME=$TARGET_BRANCH"
    fi

+   if [[ -n $TAGS ]]; then
+       # Convert comma-separated tags into OR conditions
+       IFS=',' read -ra TAG_ARRAY <<< "$TAGS"
+       TAG_FILTER=""
+       for tag in "${TAG_ARRAY[@]}"; do
+           if [[ -n $TAG_FILTER ]]; then
+               TAG_FILTER="$TAG_FILTER OR "
+           fi
+           TAG_FILTER="$TAG_FILTER tags='$tag'"
+       done
+       FILTERS="$FILTERS AND ($TAG_FILTER)"
+   fi

    if [[ $SAME_TRIGGER_ONLY -eq 1 ]]; then
        # Get Trigger Id from current build
        FILTERS="$FILTERS AND buildTriggerId=$BUILD_TRIGGER_ID"
        echo "Filtering Trigger Id: $BUILD_TRIGGER_ID"
    fi

利用するときは以下のように step を定義します。shell で利用する各変数 "CURRENT_BUILD_ID", "PROJECT_ID", "REGION" には、デフォルトの置換[8]をつかって設定します。

cloudbuild.yaml
  - id: cancelot
    name: 'gcr.io/cloud-builders/gcloud-slim'
    entrypoint: bash
    args:
      - -c
      - |
        ./cancelot.sh --tags "target-tag"
    env:
      - 'CURRENT_BUILD_ID=$BUILD_ID'
      - 'PROJECT_ID=$PROJECT_ID'
      - 'REGION=$LOCATION'

テスト

build と deploy の2つの構成ファイルを用意します。deploy は build から submit で実行されます。通常だと submit された deploy は build 側がキャンセルされても実行され続けます。

# 10 sec 待ってから実行する
gcloud builds submit --region=us-central1 \
--config build.yaml \
--async && sleep 10 && \
gcloud builds submit --region=us-central1 \
--config build.yaml \
--async 

https://github.com/sikeda107/tech-blog/blob/main/cloudbuilds-cancel-tags/before/build.yaml
https://github.com/sikeda107/tech-blog/blob/main/cloudbuilds-cancel-tags/before/deploy.yaml


ビルド履歴 before

そのため、deploy の方にもcancelot.shの呼び出しを追加します。追加することで、submit された deploy もキャンセルすることができます👍

https://github.com/sikeda107/tech-blog/blob/551c27281001bcc3e3e5a07c0e0751941e32a749/cloudbuilds-cancel-tags/after/deploy.yaml#L2-L12


ビルド履歴 after

テスト - 複数タグ

タグを複数指定しても問題ないかチェックします。tag の設定が違うだけの2つのファイルを良いします。cancelot.sh のオプションには --tags "tag1,tag2" を指定します。

# はじめはほぼ同時に実行し、再度 10 sec 待ってから実行する
gcloud builds submit --region=us-central1 --config build-tag1.yaml --async & \
gcloud builds submit --region=us-central1 --config build-tag2.yaml --async & \
sleep 10 && \
gcloud builds submit --region=us-central1 --config build-tag1.yaml --async

https://github.com/sikeda107/tech-blog/blob/551c27281001bcc3e3e5a07c0e0751941e32a749/cloudbuilds-cancel-tags/multi-tag/build-tag1.yaml#L2-L12

期待通り tag1 と tag2 のどちらもキャンセルされることが確認できました🎉


ビルド履歴 複数タグ

テスト - global リージョン

global リージョンでも動作します。

gcloud builds submit --region=global --config build-tag1.yaml --async & \
gcloud builds submit --region=global --config build-tag2.yaml --async & \
sleep 10 && \
gcloud builds submit --region=global --config build-tag1.yaml --async


ビルド履歴 global

おわりに

今回は cancelot.sh をベースに、タグによるビルドの同時実行制御機能を実装しました。この拡張により、トリガーIDに依存せず、複数のタグを横断してビルドをキャンセルできるようになります。コストやリソース競合の防止に役立つと思うので、いつか Cloud Build の公式機能として組み込まれるとうれしいですね🚀

脚注
  1. ビルド構成ファイルのスキーマ > tags ↩︎

  2. ビルド結果を表示する > タグを使用してビルド結果をフィルタする ↩︎

  3. ビルドトリガーの作成と管理 > ビルドトリガーの作成 ↩︎

  4. ビルドトリガーの作成と管理 > ビルドトリガーの更新 ↩︎

  5. クイックスタート: Cloud Build を使用して Docker イメージのビルドし、pushする ↩︎

  6. CloudBuild で最強の Terraform & Terragrunt CI 環境を作る MIXI DEVELOPERS ↩︎

  7. cloud-builders-community/cancelot at master · GoogleCloudPlatform/cloud-builders-community · GitHub ↩︎

  8. 変数値の置換 > デフォルトの置換の使用 ↩︎

Discussion