AIコーディングやDevOpsの推進などにより、短時間に複数のデプロイを実行する開発組織が増えつつあります。本番であれステージング・検証環境であれ、デプロイ頻度が増加すると、「デプロイタスクの競合」による障害リスクが発生します。1つ目のデプロイタスクが完了する前に2つ目のデプロイが実行されることにより、「アプリケーションコードは2つ目のコードなのに、DBのスキーマは1つ目のコードベースのまま」といった不整合が発生し、そのままシステム障害に直結します。
この記事では、CircleCIを利用してデプロイ作業を競合させず、直列実行させる方法について、job-groupsとserial-groupの2機能を使った方法を紹介します。
デプロイジョブを直列化すべき理由
テストや静的解析などの検証タスクは、並列化させることで高速化できます。しかしデプロイについては、1つの環境に対して直列で実行することをお勧めします。それは冒頭に触れたように、「デプロイ作業同士の競合による障害」リスクを回避するためです。
たとえば「deploy ステップでコードをデプロイし、その後追加の設定変更作業等を実施する release ステップも実行する」デプロイフローを考えましょう。並列でデプロイを実行していると、release ステップが終わる前に後続のdeploy ステップが実行されてしまいます。
ステップ単位の順序設定であれば、requiresを利用して依存関係を設定します。今回のようなワークフロー単位で実行順序を制御する場合は、serial-groupを使いましょう。
serial-groupを使った直列化
serial-group をYAMLに設定しましょう。同じserial-groupキーを設定されたジョブは、CircleCIの組織単位で1つのキューを経由します。そのため、仮に複数のジョブがキューに入ったとしても、キューへ入った順番に直列処理されます。
version: 2.1
jobs:
deploy:
docker:
- image: cimg/base:stable
steps:
- run: echo "Deploying..."
workflows:
main:
jobs:
- deploy:
serial-group: production-deploys
同じラベル(ここでは production-deploys)を付けたジョブは、複数のパイプラインから同時にトリガーされても同時には走りません。
CircleCIのUIを確認すると、実行したジョブに[serial-start]と[serial-end]ステップが追加されていることがわかります。この2つに挟まれたジョブは、CircleCIが内部に持つキューを経由して直列実行されます。
CI/CD ジョブの直列制御3パターン
ここからはいくつかの組み合わせパターンを紹介します。
job-groupsを組み合わせた複数ステップの直列化
複数のステップを1まとめにして直列化させたい場合、job-groupsを組み合わせて利用します。例えばデプロイジョブとリリースジョブをまとめて直列化したいケースでは、次のようなYAMLを作ります。
version: 2.1
jobs:
build:
docker:
- image: cimg/base:stable
steps:
- run: echo "Building..."
deploy:
docker:
- image: cimg/base:stable
steps:
- run: echo "Deploying..."
release:
docker:
- image: cimg/base:stable
steps:
- run: echo "Releasing..."
notify:
docker:
- image: cimg/base:stable
steps:
- run: echo "Notifying..."
# deploy と release を 1 グループとして定義する
job-groups:
deploy-and-release:
jobs:
- deploy
- release:
requires:
- deploy
workflows:
main:
jobs:
- build
- deploy-and-release:
requires:
- build
serial-group: production-critical
- notify:
requires:
- deploy-and-release
workflowsのjobsにjobを直接登録する代わりに、job-groupsにジョブ内容をまとめています。job-groupsで定義した物をworkflowsへ追加し、そこにserial-groupを設定することにより、複数のジョブについても直列実行を指示できます。
こちらも実行結果を見てみましょう。[serial-start]と[serial-end]の間に[deploy]と[release]両方のジョブが挟まれています。
job-groupsとserial-groupを組み合わせることで、deployおよびreleaseの2ジョブが完了するまで、後続のdeployジョブはスタートされずに待機します。
直列化する範囲をプロジェクト単位に限定する
serial-groupを含むジョブを、URL Orbsやgit submoduleで配布しているケースを考えましょう。このケースでは、複数のプロジェクトで同じ名前のserial-groupを使用することになります。そうなると「Aのプロジェクトのデプロイが終わるまで、Bのプロジェクトでもデプロイが行えなくなる」といった意図しない直列化が発生します。
そこで、serial-group の値にパイプラインコンテキストを埋め込むと、直列化のスコープをプロジェクト単位に絞れます。
workflows:
main:
jobs:
- deploy-and-release:
- serial-group: production-critical
+ serial-group: << pipeline.project.slug >>/production-critical
<< pipeline.project.slug >> のようなプロジェクト固有の値を組み合わせたグループ名にすることで、意図しない競合解決・直列化によるCDフローのつまりを解消できます。

左が vitest-test-splitting の Pipeline #3、右が ci-sandboxes の Pipeline #42 です。同一サフィックスの serial-group でも、プロジェクト slug により別キューとなり、両ワークフローが同時期に完走しています。
デプロイが失敗した時のロールバック措置
serial-groupを指定したjog-groupsが途中でfailした場合、例えばコードのデプロイは成功したけども、DBの変更などが未完了という状況が発生します。不整合が起きた状況のまま、後続のワークフローが走り出すと、さらに問題が複雑になる可能性があります。
そのため、serial-groupとjob-groupsを利用したフローを設計する際は、requiresを利用したロールバックジョブまで設定することをお勧めします。
job-groups:
deploy-and-release:
jobs:
- deploy
- release:
requires:
- deploy
- rollback:
requires:
- deploy:
- failed
- canceled
例えばこのYAMLでは、deployジョブが失敗あるいはキャンセルされた時だけ、rollbackジョブが実行されます。
terraformやAWS CDK / Helmなどを利用したインフラのコード化(IaC)で、宣言的なデプロイを実現できます。これらに加えてrollbackジョブを整備することで、冪等化の難しい作業もロールバックできるようにしましょう。
serial-groupを使う際の注意点
serial-group含むワークフローを再実行すると、グループ内のジョブもまとめて再実行されます。そのため、「グループ内の特定ジョブだけ再実行する」のではなく、グループ内のジョブは全て再実行される想定で運用フローなどを準備しましょう。
まとめ
job-groups と serial-group を組み合わせると、「複数ジョブを 1 つの塊として直列化する」という要件を、設定ファイルだけで表現できます。また、途中で失敗した時のロールバック方法などについても、requiresなどを組み合わせることで、YAMLにて表現が可能です。
複雑なリリース作業はIaCによる宣言的デプロイの実践と、柔軟なCDパイプラインの制御で自動化・効率化を目指しましょう。
参考リンク
- Controlling serial execution across your organization(CircleCI Docs): https://circleci.com/docs/guides/orchestrate/controlling-serial-execution-across-your-organization/
- Job groups: serialize multiple jobs as one unit(Changelog): https://circleci.com/changelog/job-groups-serialize-multiple-jobs-as-one-unit/
- Using CircleCI Serial Groups Instead of the Queue Orb(Support): https://support.circleci.com/hc/en-us/articles/45093983294491



