タグを使って 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]
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 export
と import
[4]でやる必要がありそうです。トリガー作成時に設定する方法はわかりませんでした。
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 の同時実行を制御する
本題の前に
この記事[6]の「ジョブのキャンセルをしよう」で cancelot.sh
というものが紹介されています。このツールを使うと、指定の条件に一致したジョブをキャンセルすることができます。ちなみに、おおもとのcloud-builders-community[7]の方はメンテナンスされておらず、docker build すらできません。
cancelot.sh の拡張
このツールの--same_trigger_only
オプションを使うと、実行されているビルド自身の「トリガーID」に基づいてキャンセルするということができます。ただし、トリガーIDのないgcloud builds submit
によるビルドの実行では動作しません😢。以前の記事ではロックファイルを作成して実行を制御する方法を考案しましたが、複雑すぎるため今回はタグをつかった方法で実現しようと思います。
コード
リポジトリからファイルをダウンロードしてきます。
# ダウンロードコマンド
curl -OL https://raw.githubusercontent.com/siberex/cancelot/main/cancelot.sh
# 実行権限を付与
chmod +x cancelot.sh
今回は以下のオプションをcancelot.sh
に追加します。カンマ区切りでタグを受けとり、各タグを OR でフィルターする仕様とします。
--tags "tag1,tag2"
はじめにオプションを受け取るためのコードを追加します。
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')"
のような感じに生成されます。
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]をつかって設定します。
- 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
ビルド履歴 before
そのため、deploy の方にもcancelot.sh
の呼び出しを追加します。追加することで、submit された deploy もキャンセルすることができます👍
ビルド履歴 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
期待通り 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 の公式機能として組み込まれるとうれしいですね🚀
Discussion