CodeBuildの失敗ビルドを自動的にLambdaで再試行する

:::note warn
AWS CodeBuildプロジェクトで「自動再試行の上限」を設定できるようになりました。
https://docs.aws.amazon.com/codebuild/latest/userguide/auto-retry-build.html

この記事の方法は、その設定ができない場合のみ採用をご検討ください。
:::

この記事では、AWS CodeBuildで失敗したビルドを自動的に再試行する例をご紹介します。実務で必要になり、LambdaとEventBridgeで実装したものです。

なお、実務で適用したビルドプロジェクトには、下記のような特徴があります。

- 毎日早朝にビルドが始まるようスケジューリングしている
- 開始から完了まで数十分かかる
- たまに失敗するが、根本的な修正はすぐには難しい
  - 連続して失敗することもある
- 再試行が翌営業日になっても大きな問題はない

以下、上記の前提を念頭にご覧ください。

# EventBridgeで失敗を検知する

さて、まずは、CodeBuildのビルド失敗をEventBridgeで検知します。

EventBridgeルールで、イベントパターンを下記のように指定すると、ビルドプロジェクト `project-name` のビルド失敗が検知できます。

```json
{
  "source": ["aws.codebuild"],
  "detail-type": ["CodeBuild Build State Change"],
  "detail": {
    "build-status": ["FAILED"],
    "project-name": ["project-name"]
  }
}
```

イベントパターンの詳細については、公式ドキュメントをご参照ください。

https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/sample-build-notifications.html

# Lambdaで再試行する

続いて、EventBridgeルールのターゲットとなるLambda関数を作成します。下記がコード例です。

```python
import os
import boto3


SUCCESS_CHECK_WINDOW = int(os.getenv("SUCCESS_CHECK_WINDOW", 5))


def lambda_handler(event, context):
    project_name = event["detail"]["project-name"]

    # リトライ条件：
    #   1. 直近のビルドに失敗していること
    #   2. 直近 SUCCESS_CHECK_WINDOW 件のビルドのうち、少なくとも1件に成功していること
    codebuild = boto3.client("codebuild")
    response = codebuild.list_builds_for_project(projectName=project_name)
    builds_detail = codebuild.batch_get_builds(
        ids=response["ids"][:SUCCESS_CHECK_WINDOW]
    )
    if builds_detail["builds"][0]["buildStatus"] == "FAILED" and any(
        b["buildStatus"] == "SUCCEEDED" for b in builds_detail["builds"]
    ):
        response = codebuild.retry_build(id=builds_detail["builds"][0]["id"])
        print(
            f"Rebuild initiated for project '{project_name}' "
            f"with ID '{response['build']['id']}'"
        )
```

この例では、Lambda関数が同時に複数実行されても1件の再試行に抑えるため、`直近のビルドに失敗していること` をチェックしています。

また、ビルド失敗の無限ループを避けるため、`直近 SUCCESS_CHECK_WINDOW 件のビルドのうち、少なくとも1件に成功していること` もチェックしています。`SUCCESS_CHECK_WINDOW` は環境変数です。

Lambda関数の実行ロールには、CodeBuildアクションの許可が必要です。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "codebuild:BatchGetBuilds",
                "codebuild:ListBuildsForProject",
                "codebuild:RetryBuild"
            ],
            "Effect": "Allow",
            "Resource": "arn:aws:codebuild:{Region}:{Account ID}:project/project-name"
        }
    ]
}
```

このLambda関数を、EventBridgeルールのターゲットとして指定すれば、失敗ビルドが自動的に再試行されるようになります。

:::note
実務では、ビルド状況をSlackに通知しています。
https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/notification-rule-create.html

これにより、もしLambdaでの再試行がうまくいかなくても、翌営業日には気づけ、人力で再試行できます。
:::

# 料金はほとんどかからない

これらを実装するとLambdaの料金が発生します。ただし、微々たるものなので、ほとんどの場合は無視できるでしょう。

https://aws.amazon.com/jp/eventbridge/pricing/

https://aws.amazon.com/jp/lambda/pricing/

# おわりに

この記事では、CodeBuildの失敗ビルドを自動的に再試行するために、EventBridgeとLambdaを利用する例をご紹介しました。

実務では、これまで人力で再試行していた手間が省け、担当者に喜んでいただけました。導入から2ヶ月あまりで、人力での再試行は数回だけです。

このように「ちょっとの手間を我慢して担当者が人力対応しているタスク」は探せば色々あると思います。EventBridgeやLambdaをうまく使えば、そのようなタスクが自動化できるかもしれません。

ちなみに、記載したLambda関数のコードは、OpenAIのgpt-4モデルとチャットしながら作ったものでした。関数の目的を伝えたたけで、ほぼ意図どおりのコードが出力されてしまいました。すごい時代ですね。