GitHub Actionsで1つしかキューされない挙動について

はじめまして。Unityエンジニアの佐々木です。
弊社ではUnityアプリケーションのCI/CD環境をJenkinsで構築しています。
CI/CD環境をGitHub Actionsに移行する検証をしており、その際にワークフローの実行が1つしかキューされないことに気づきました。

今回はそのJenkinsと異なるキューイング挙動について紹介します。

背景:JenkinsからGitHub Actionsへの移行

Jenkinsを使用するうえで以下の点に悩まされていました。

  1. プラグインやJenkins自体をアップデートするとJenkinsが動かなくなることがある
    (プラグイン間やJenkins自体のプラグインへの依存度が高い)
  2. ジョブの内容をJenkinsのGUI上でべた書きしていて共通化やバージョン管理ができていない
  3. ジョブやJenkins自体の設定がコード化されておらず管理ができていない

アップデートにより動かなくなる問題以外はJenkinsfileやプラグインを使うことでJenkinsのまま解決することも可能です。
しかし今回は特にアップデート問題に悩まされていたため別のツールを検討しました。

いくつかCI/CDツールはありますが以下の理由からGitHub Actionsを使用する方針としました。

  • Jenkinsと比べプラグイン間やGitHub Actions自体のプラグインへの依存度が低く、
    アップデート問題を解決できる可能性が高い(1の問題)
  • 2と3の問題について標準機能で解決可能
  • Jenkinsで既に使用しているオンプレミスのマシンで実行可能(セルフホステッドランナー)
  • 既にGitHub Actionsを静的解析などビルド以外の用途で使用しており知見がある

GitHub Actionsに移行する方針に決めたためJenkinsのジョブを移植する検証を進めていました。
検証中に同時実行制限時の挙動が異なっていることを見つけ、その挙動をJenkinsに合わせる必要があることに気づきました。

同時実行制限の方法

※ 以下GitHub Actionsについての説明のため、実行するビルド処理をジョブではなくワークフローと呼称します

1つのUnityプロジェクトに対して同時に複数のビルドを実行することはできません。
そのため同時実行制限をする必要があります。

GitHub Actionsではconcurrency設定で以下のように同時実行を制限することができます。
GitHub Actions のワークフロー構文 concurrency

name: Unity Build
on:
  workflow_dispatch # 手動実行トリガー
concurrency:
  group: ${{ github.workflow }}
  cancel-in-progress: false # 実行中のワークフローがキャンセルされない設定
jobs:
  build:
    runs-on: self-hosted # セルフホステッドランナーを指定
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          lfs: true
          clean: false # Unityプロジェクトはサイズが大きいためビルド後に破棄しない
      - name: Build Unity Project
        run: |
          # Unity ビルドコマンド(省略)
          echo "Building Unity Project..."
  • concurrencygroupとして指定した値が同じワークフローは同時実行されなくなる
  • cancel-in-progressfalseとすると実行中のワークフローがキャンセルされなくなる

同時実行制限のキューイング挙動

上記の設定にすることでJenkinsのように同時実行されずに2番目以降がキューされると思っていました。
しかし実際には、1つだけはキューされるものの、それ以降は古いキューが破棄され、最新のもののみがキューされるという挙動でした。

例えば、
1回目の手動実行: ワークフローが実行開始。
2回目の手動実行: 1回目実行中にトリガーされ、キューに追加。

3回目の手動実行: さらにトリガーすると、キューされていた2番目がキャンセルされ、3番目が新たにキューに追加。

つまり、常に「実行中のもの1つ」と「最新のキュー1つ」が保持され、古いキューは破棄されてしまう挙動となっています。

現在のプロジェクトではUnityビルドに10分以上かかり、異なるビルドパラメータで連続してビルドしたいこともよくあります。
そのためJenkinsのように2つ目以降もキューできるようにする必要があります。

解決案

この挙動は、GitHub Actionsのconcurrency機能の仕様となっています。
コンカレンシー グループには、最大 1 つの実行ジョブと 1 つの保留中のジョブが存在できることを意味します。
そのためGitHub Actionsのみでは解決ができず、他のサービスを併用しキューの挙動を実装する必要があります。

まだ試せていないためあくまで案ではありますが以下のような方法を検討しています。

ワークフローを直接実行せずにAWS SQSのようなメッセージキューサービスを介して実行させる。

  • トリガー用のワークフローを別で用意する
  • そのワークフローを実行した際にビルド中でなければ、そのままビルドのワークフローを実行する
  • ビルド中の場合のキューサービスのキューに入れる
  • ビルド完了時にキューがあるかチェックし、キューがある場合はそれを実行する

ただしこのままだとキュー状況を確認するためにAWS上で確認する必要があり不便です。
そのため別途キュー状況を確認するための管理画面を作成する想定です。

まとめ

GitHub ActionsはJenkinsで感じていた課題を解決することができる有用なツールですが、Jenkinsとは挙動が異なる点が多々あります。
そのためJenkinsの挙動が当たり前と思わずにGitHub Actionsの挙動をよく理解して使用することが大切だと学びました。

関連記事

  1. Unityでメモリ位置を表示するツール

  2. Android15(APIレベル35)への対応が本当にできたのか不安な…

サービスサイト