【入門編】大規模開発の味方!GitHub Actionsの「Concurrency Group」で排他制御を行い、デプロイ事故を未然に防ぐ – バージョン管理・CI/CD活用バイブル

大規模開発の味方!GitHub Actionsの「Concurrency Group」で排他制御を行い、デプロイ事故を未然に防ぐ

皆さん、こんにちは!開発チームの皆さん、毎日のコードプッシュ、お疲れ様です。

「あれ?さっきデプロイしたばかりなのに、もう新しいバージョンが…?」「いや、待てよ、もしかして前のデプロイがまだ終わってなかったかも…?」

こんな経験、ありませんか?特に大規模な開発チームになると、複数の開発者が同時に作業を進めるため、意図せずデプロイが競合してしまったり、最新のコードが反映されなかったりといった「デプロイ事故」が起こりやすくなります。せっかく自動化を進めているのに、これでは本末転倒ですよね。

でも、安心してください!GitHub Actionsには、そんな悩みを解決してくれる強力な機能、「Concurrency Group」があるんです。これを使いこなせば、デプロイの競合を防ぎ、より安全で安定したデプロイメントを実現できます。

今回は、この「Concurrency Group」の具体的な設定方法から、実務での運用ルール構築術まで、先輩エンジニアの視点から、皆さんの日々の作業が劇的に楽になるような、現場で震えるほど役立つ極限の知見を、魂を込めてお伝えします!

そもそも「Concurrency Group」って何? なぜ必要なの?

「Concurrency Group」とは、簡単に言うと、「特定のジョブ(ワークフローの実行単位)が同時に複数実行されないように制御する仕組み」のことです。

大規模開発では、以下のような状況でデプロイ競合が発生しやすくなります。

  • 複数の開発者が同時に、同じブランチにコードをプッシュした場合:

それぞれのPushがトリガーとなってCI/CDパイプラインが走り、デプロイが複数回実行される可能性があります。

  • CI/CDパイプラインの実行に時間がかかる場合:

前のデプロイが完了する前に次のデプロイが開始されてしまい、意図しない状態になることがあります。

このような状況で、もしデプロイ処理が冪等性(何度実行しても同じ結果になる性質)を持っていなかったり、状態管理が複雑だったりすると、以下のような悲惨な結果を招く可能性があります。

  • 古いコードがデプロイされる
  • デプロイが途中で失敗する
  • 本番環境が不安定になる

「Concurrency Group」は、これらの問題を「排他制御」という形で解決します。これは、あるリソース(この場合はデプロイ処理)を同時に利用できるのは1つだけ、という考え方です。

例えば、`main` ブランチへのデプロイは、常に1つだけ実行されるように制限することで、デプロイ事故のリスクを大幅に減らすことができます。

GitHub Actionsでの「Concurrency Group」設定方法:YAMLで直感的に!

GitHub ActionsのワークフローはYAMLファイルで定義されます。ここで「Concurrency Group」を設定するのは驚くほど簡単です!

ワークフローのYAMLファイルのトップレベルに `concurrency` キーを追加するだけです。

.github/workflows/deploy.yml

name: Deploy to Production

on:
push:
branches:

  • main # mainブランチへのpushでトリガー

jobs:
deploy:
runs-on: ubuntu-latest

# ここでconcurrencyを設定します!
concurrency:
# グループ名を定義します。同じグループ名を持つワークフローは排他制御されます。
# ここでは、特定のブランチ名とリポジトリ名を組み合わせることで、
# 各ブランチごとに独立した排他制御を実現しています。
# ${{ github.ref }} は現在のブランチ名(例: refs/heads/main)を表します。
# sanitize_ref を使うと、refから不要なプレフィックスを取り除いた、より分かりやすい名前になります。
group: deploy-${{ github.ref_name }}
# concurrency:
# group: deploy-${{ github.repository }}-${{ github.ref_name }} # リポジトリ名も含める場合
# group: deploy-main # 特定のブランチに限定する場合

# 競合発生時の挙動を指定します。
# cancel-in-progress: true にすると、新しいワークフローが開始された際に、
# 実行中の古いワークフローをキャンセルします。
# false (デフォルト) の場合は、新しいワークフローはキューに入れられ、
# 実行中のワークフローが終了するまで待ちます。
cancel-in-progress: true

steps:

  • name: Checkout code

uses: actions/checkout@v4

  • name: Set up Node.js

uses: actions/setup-node@v4
with:
node-version: ’20’ # 使用するNode.jsのバージョンを指定

  • name: Install dependencies

run: npm ci # npm install ではなく npm ci を使うことで、lockファイルに基づいた確実なインストールを行います。

  • name: Build application

run: npm run build # アプリケーションのビルド

  • name: Deploy to production

run: |
echo “Deploying to production…”
# ここに実際のデプロイコマンドを記述します。
# 例: aws s3 sync ./dist s3://my-production-bucket –delete
# 例: docker push your-docker-repo/your-app:latest && kubectl set image deployment/your-app your-app=your-docker-repo/your-app:latest
sleep 60 # デモ用に60秒待機
echo “Deployment finished successfully!”

各設定項目の解説

  • `concurrency`: このブロックで並行実行の制御を設定します。
  • `group`:
  • 排他制御のグループ名を指定します。同じ `group` 名を持つワークフロー実行は、同時に1つしか実行されません。
  • `${{ github.ref_name }}` は、ワークフローをトリガーしたブランチ名(例: `main`, `develop`)を表します。これにより、ブランチごとに独立した排他制御が実現できます。例えば、`main` ブランチへのデプロイは `deploy-main` グループ、`develop` ブランチへのデプロイは `deploy-develop` グループのように、それぞれ独立して管理できます。
  • `${{ github.repository }}` を含めると、リポジトリ名もグループ名に含めることができます。これは、複数のリポジトリで同じワークフローを使用している場合に便利です。
  • 特定のブランチのみを対象にする場合は、`deploy-main` のように直接ブランチ名を指定することも可能です。
  • `cancel-in-progress: true`:
  • これが非常に強力な設定です!
  • 新しいワークフロー実行が開始され、かつ同じ `concurrency` グループに属するワークフローが既に実行中の場合、実行中の古いワークフローを自動的にキャンセルします。
  • これにより、古いデプロイが完了する前に新しいデプロイが始まってしまう、という状況を防ぐことができます。常に最新のコードでのデプロイを保証しやすくなります。
  • `false` (デフォルト) の場合、新しいワークフローはキューに入り、実行中のワークフローが終了するまで待機します。これは、デプロイの順番が重要な場合に有効ですが、最新性を優先する場合は `true` がおすすめです。

なぜ `${{ github.ref_name }}` を使うのがおすすめなのか?

`group: deploy-${{ github.ref_name }}` という設定は、以下のようなメリットがあります。

1. ブランチごとの独立した排他制御: `main` ブランチへのデプロイと `develop` ブランチへのデプロイは、それぞれ別のグループとして扱われます。つまり、`main` ブランチのデプロイが実行中でも、`develop` ブランチのデプロイは並行して実行される可能性があります(もちろん、`develop` ブランチ内での排他制御は維持されます)。これにより、開発の並行性を保ちつつ、デプロイの競合を防ぐことができます。
2. 柔軟性: 特定のブランチ(例: `main`)だけを厳密に排他制御したい場合は、`group: deploy-main` のように直接指定することも可能です。

実務での運用ルール構築術:CI/CDを「信頼できるもの」にするために

「Concurrency Group」を設定するだけで、デプロイ競合のリスクは減らせますが、さらに盤石な体制を築くためには、チーム内での運用ルールを明確にすることが重要です。

1. デプロイ対象ブランチの定義と `concurrency` グループの設計

  • 本番デプロイ: `main` や `master` のような本番環境に直接デプロイされるブランチは、最も厳格な排他制御が必要です。`group: deploy-main` のように、専用のグループを設定しましょう。`cancel-in-progress: true` を設定し、常に最新のコードがデプロイされるようにするのがおすすめです。
  • ステージング/プレビュー環境デプロイ: 開発中の機能確認や、本番に近い環境でのテストのために、`develop` や `staging` ブランチなどへのデプロイも考えられます。これらのブランチに対しても、`group: deploy-develop` のように、ブランチごとに独立した `concurrency` グループを設定しましょう。
  • フィーチャーブランチのデプロイ: 各開発者が作成するフィーチャーブランチ(`feature/xxx`)へのデプロイは、通常、CIでのテスト実行に留まります。もしデプロイまで行う場合でも、`group: deploy-${{ github.ref_name }}` であれば、各フィーチャーブランチごとに排他制御されるため、他のフィーチャーブランチのデプロイを妨げることはありません。

2. `cancel-in-progress` の使い分け

  • 本番デプロイ: `cancel-in-progress: true` を強く推奨します。万が一、デプロイが長引いたり、複数の開発者が同時に `main` にPushしたりした場合でも、古いデプロイを中断し、最新のコードでのデプロイを優先させます。
  • ステージング/開発環境デプロイ: 状況によりますが、`true` が有効な場合が多いでしょう。しかし、デプロイプロセスが非常に長く、かつ「この順番で実行したい」という明確な意図がある場合は、`false` を検討しても良いかもしれません。ただし、その場合でも、デプロイが完了するまで新しいデプロイは待機するため、デプロイに時間がかかることへの注意は必要です。

3. デプロイメント戦略との連携

「Concurrency Group」は、あくまで「ワークフローの同時実行」を制御するものです。実際のデプロイメント戦略(Blue/Green Deployment, Canary Releaseなど)とは、連携して考える必要があります。

例えば、Blue/Green Deployment の場合、新しいバージョンへの切り替え処理自体がデプロイの一部となります。この切り替え処理が複数同時に走ってしまうと、予期せぬダウンタイムやロールバックの失敗につながりかねません。`concurrency` グループとデプロイメント戦略を組み合わせることで、より安全なデプロイを実現しましょう。

4. チーム内での周知とドキュメント化

どんなに優れた機能も、チームメンバーが理解していなければ宝の持ち腐れです。

  • ワークフローの意図を共有: なぜ `concurrency` グループを設定しているのか、その目的(デプロイ事故防止、最新性の確保など)をチーム全体で共有しましょう。
  • ドキュメント化: GitHubリポジトリの `README.md` や、チームのWikiなどに、CI/CDパイプラインの構成、特に `concurrency` の設定意図と運用ルールを明記しておきましょう。これにより、新しいメンバーもスムーズに理解できます。

まとめ:GitHub Actionsの「Concurrency Group」で、デプロイの迷いを断ち切る!

いかがでしたでしょうか?

GitHub Actionsの `concurrency` グループは、大規模開発におけるデプロイの競合という、まさに「現場で震える」ような課題を解決してくれる、非常に強力な機能です。

  • `group` で排他制御の対象を定義し、
  • `cancel-in-progress: true` で実行中の古いワークフローをキャンセルすることで、
  • 常に最新かつ安全なコードをデプロイできる ように環境を整えられます。

これをマスターすれば、皆さんの毎日のデプロイ作業が、よりスムーズで、そして何よりも「安心」できるものに変わるはずです。

ぜひ、皆さんのプロジェクトでも「Concurrency Group」を活用して、デプロイ事故の不安から解放され、より生産性の高い開発ライフを送ってくださいね!

もし、さらに深掘りしたい点や、具体的な運用での疑問点があれば、いつでも気軽に質問してください。皆さんのCI/CDライフを全力でサポートします!

タイトルとURLをコピーしました