S3でCodeBuildの排他制御をする

CodeBuildで同時ビルド数を一つに制限したいことがあって、そういうときはConcurrent build limit1に設定して運用するようにしていた。

[Concurrent build limit] (同時ビルド制限) で、このジョブで許可される同時実行の最大数を設定します。

しかしConcurrent build limitでは制限を超えるビルドが実行されると「待ち状態」にはならずに即座にビルドが失敗する。

それが原因でCIに組み込んだCodeBuildのビルドが失敗することがしばしばあったのでS3の制御を使って「他のビルドの終了を待てる」仕組みを作ってみた。

s3lock

github.com

s3lockはS3の条件付き書き込みを使った排他制御ツールで、If-None-Match: *をつけてオブジェクトを作成することでPutObjectで上書きできないようにしている。

# URLを指定してロックを取得
$ s3lock lock s3://my-bucket/lock-object
s3://my-bucket/lock-object has been locked
create lock-object.lock

# 他のプロセスではロックを取れない
# $ s3lock lock s3://my-bucket/lock-object
# s3lock: error: lock already held

# 何か処理をやってからロックを解除
$ s3lock unlock lock-object.lock
s3://my-bucket/lock-object has been unlocked
delete lock-object.lock

lockサブコマンドには-w UINTオプションがあって、即時終了しないで指定秒数ロックの取得を待つことができる。

# すでにロックがとられていた場合、解除されるまで600秒待つ
$ s3lock lock -w 600 s3://my-bucket/lock-object

CodeBuild+s3lock

CodeBuildでs3lockを使って排他制御してみる。

buildspecはこんな感じ。

version: 0.2

phases:
  install:
    commands:
      - curl -sSfLO https://github.com/winebarrel/s3lock/releases/download/v0.1.0/s3lock_0.2.0_amd64.deb
      - dpkg -i s3lock_0.2.0_amd64.deb
  build:
    commands:
      - s3lock lock -w 600 s3://winebarrel/lock-object
      - sleep 60
    finally:
      - |
        if [ -e lock-object.lock ]; then
          s3lock unlock lock-object.lock
        fi

コマンドラインから連続してビルドを実行してみる。

$ aws codebuild start-build --project-name hello
{
    "build": {
        "buildNumber": 4,
        "startTime": "2026-02-08T10:37:12.198000+09:00",
...
$ aws codebuild start-build --project-name hello
{
    "build": {
        "buildNumber": 5,
        "startTime": "2026-02-08T10:37:13.866000+09:00",
...

buildNumber: 4では普通にロックを取得して、sleep 60してからロック解除して終了。

[Container] 2026/02/08 01:37:24.824871 Entering phase BUILD
[Container] 2026/02/08 01:37:24.826554 Running command s3lock lock -w 600 s3://winebarrel/lock-object > lock-object.lock
[Container] 2026/02/08 01:37:24.903498 Running command sleep 60
[Container] 2026/02/08 01:38:24.910839 Running command s3lock unlock lock-object.lock
[Container] 2026/02/08 01:38:25.026469 Phase complete: BUILD State: SUCCEEDED

buildNumber: 5ではs3lock lock -w 600でロックの取得を待ち、60秒たってロックを取得できてからsleepに進んでいる。

[Container] 2026/02/08 01:37:25.642655 Entering phase BUILD
[Container] 2026/02/08 01:37:25.643898 Running command s3lock lock -w 600 s3://winebarrel/lock-object > lock-object.lock
[Container] 2026/02/08 01:38:25.742380 Running command sleep 60
[Container] 2026/02/08 01:39:25.750361 Running command s3lock unlock lock-object.lock
[Container] 2026/02/08 01:39:25.881432 Phase complete: BUILD State: SUCCEEDED

最終的に同時ビルド数を1に制限しつつ、複数のビルドを正常終了することができた。