GitHub Actionsのconcurrencyでworkflowの同時実行を制御してみる

CI/CD

GitHub ActionsでCI/CDを組んでおり、terraform apply をCDに組み込んでいる。
terraformの機能として、terraform apply が同時に実行されるとstateがロックされ、他のユーザーが重複して処理を行なうことを防いでくれる。State Locking
ただ、terraform apply を同時実行したり、途中で中断したりするとstateがロックされているが故にエラーになる。TerraformでState Lockエラーが発生したら

もし、CDで terraform apply が同時実行され、エラーになると色々と面倒なことになる。CDはPRがマージされたら実行されるようにしているので、そんな同時にPRがマージされることはないと思いたいが、100%ないわけではない。なので仕組みで対策することを検討した。

concurrencyってなに?

workflowの同時実行の制御ができるパラメータ。 pushとかPRの更新とかをトリガーにしていたら、よくこんな感じでworkflowが同時実行されると思う。
このworkflowが同時実行されるという状態を制御できる。

作成したworkflow

検証用に以下のworkflow定義ファイルを作成。とりあえず使えそうなところだけ抽出して作成してみた。詳しくは公式ドキュメントを参照してほしい。

name: Concurrency Test Workflow

# 手動実行とプッシュ時に実行されるように設定
on:
  push:
    branches:
      - main
  workflow_dispatch:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: false
  # cancel-in-progress: true

jobs:
  test-job-1:
    runs-on: ubuntu-latest
    steps:
      - name: Long running step
        run: |
          echo "Starting job..."
          sleep 30
          echo "Job completed"

説明

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: false

concurrencyではgroupcancel-in-progressが設定できる。

group

concurrencyで制御するグループの設定。
${{ github.workflow }}は実行中のGitHub Actionsのworkflowの名前が入る。自分が作成したworkflowだと、”Concurrency Test Workflow”になる。
${{ github.ref }}は現在実行中のワークフローがトリガーされたブランチ名やタグ名が入る。自分が作成したworkflowだと、mainブランチへのpushがトリガーなので、refs/heads/mainが入る。
※手動実行は異なる場合があるが、ここでは説明を省く

つまり、${{ github.workflow }}-${{ github.ref }}は”Concurrency Test Workflow-refs/heads/main”となる。設定したgroupが他のworkflow定義ファイルと同じ場合、意図しないworkflowの挙動をするかもなので、リポジトリ内で一意なものにしておきたい。
大抵のユースケースでは${{ github.workflow }}-${{ github.ref }}でOKかなと思う。

cancel-in-progress

名前通りではあるが、新たにworkflowがQueueに入った場合、進行中のworkflowをキャンセルするかどうかの設定。
trueの場合は進行中のworkflowがキャンセルされ、Queueに入っているworkflowが実行される。
falseの場合は進行中のworkflowがキャンセルされず、実行中のworkflowが完了するまでQueueに入っているworkflowが実行されない。

動かしてみる

cancel-in-progress: falseの場合

まずはcancel-in-progress: falseの場合で動かしてみる。同時に2回workflowを手動実行する。
するとこんな感じで2つ目のworkflowがPendingになる。
※スクショするタイミングが悪かったので、1つ目のworkflowがQueuedになってる、、

2つ目のworkflowの中を見てみるとこんな表示になっている。
“This workflow is waiting for Concurrency Test Workflow to complete before running. Learn more about concurrency.”と表示され、実行中のworkflowの完了待ちであることが分かる。

cancel-in-progress: trueの場合

次にcancel-in-progress: falseの場合で動かしてみる。同時に2回workflowを手動実行する。
するとこんな感じで1つ目のworkflowがキャンセルされて、2つ目のworkflowが実行される。

1つ目のworkflowの中を見てみるとこんな表示になっている。
“Canceling since a higher priority waiting request for ‘Concurrency Test Workflow-refs/heads/main’ exists “と表示され、2つめのworkflowによってキャンセルされたことが記載されている。

おわりに

今回はGitHub Actionsのcuncurrencyを試してみた。これにより、CDの同時実行によるエラーを回避することができそう。また、workflowを動作させるランナーのリソースを節約したい時にも使えそうだった。