本記事は
2年目ウィーク
3日目の記事です。
📈
2日目
▶▶ 本記事 ▶▶
4日目
✒️

こんにちは、奥山です。気づいたら前回のブログ執筆から半年経っていました。
今回はAWS CodeBuild(以下、CodeBuild)の話をします。
CodeBuildでビルドが失敗したとき、ログを確認して一時的なエラー(ネットワークエラーなど)だと判断した場合はビルドを再試行することがあります。
CodeBuildには、プロジェクト単位でビルド全体を再試行する方法があります。これとは別に、buildspec(CodeBuildで実行するコマンドを定義するYAML形式の設定ファイル)では、フェーズ単位で失敗時の動作を指定できます。
今回の記事では、CodeBuildで利用できるリトライの設定と、実際に試して分かった挙動を整理します。
プロジェクト単位のリトライ
CodeBuildでは失敗したビルドを再試行できます。方法は2つあり、1つは失敗したビルドを選んで手動で再試行する方法、もう1つはプロジェクトに自動再試行の回数を設定しておく方法です。後者では、ビルドが失敗した際に指定した回数まで自動で再試行されます。
自動再試行の回数はプロジェクト作成時に指定するか、作成後にプロジェクトを編集して変更できます。

参考:
自動再試行の上限
2026年09月現在、マネジメントコンソールには記載がないのですが、「自動再試行の上限」として設定できる最大値は10回となっています。11回以上を指定してプロジェクトを作成しようとすると、エラーが発生して作成できません。

自動再試行の動作を確認する
実際の動きを確認するため、必ず失敗するbuildspecを用意しました。
(以後、buildspecはフェーズの部分のみ記載します)
version: 0.2 phases: build: commands: - echo "Hello World" - exit 1
自動再試行を3回に設定してビルドを実行すると、最初のビルドが失敗したあと、指定どおり3回再試行されました。初回の失敗を含めて、計4回のビルドが実行されます。
再試行は元のビルドとは別のビルドとして実行されるため、ビルド履歴にもそれぞれ記録されます。

自動再試行に必要なIAM権限
再試行されたビルドの「送信者」は、プロジェクトのサービスロールになっていました。これはCodeBuildの仕様で、公式ドキュメントには次のように説明されています。
CodeBuild は、ビルドが失敗した後、指定された制限回数までプロジェクトのサービスロールを使用して自動的に RetryBuild を呼び出します。
CodeBuildのサービスロール(CodeBuildがビルド処理でAWSリソースを操作するための権限を定義するIAMロール)は、プロジェクト作成時に自動で作成できます。今回の検証でもそのサービスロールを利用しました。
権限を確認すると、CloudWatch Logsへの書き込みなどの権限に加えて、codebuild:RetryBuildが許可されていました。これはプロジェクト作成時に「自動再試行の上限」を設定したためです。上限を設定せずに作成した場合は、CloudWatch Logsへの書き込みなど、基本的な権限だけが付与されるようです。

試しにこの権限を外してからビルドを実行すると、最初のビルドが失敗しても再試行は行われませんでした。権限がないとエラーは出ず、単に再試行されないだけなので、設定したつもりでも動いていないことに気づきにくくなります。
サービスロールを自動作成ではなくIaCなどで定義している場合は、自動再試行を使うにはcodebuild:RetryBuildを明示的に許可する必要があります。
フェーズ単位のリトライ
プロジェクト単位のリトライとは別に、buildspecでは各フェーズの失敗時の動作をon-failureで指定できます。指定できるのはinstall、pre_build、build、post_buildの4フェーズです。
なお、on-failureはCodeBuildが提供するEC2コンピューティングのイメージでのみ動作し、Lambdaコンピューティングや予約済みキャパシティではサポートされていません。
参考: CodeBuild のビルド仕様に関するリファレンス
例えば、buildフェーズが失敗した場合に再試行するなら次のように書きます。
build: on-failure: RETRY commands: - npm run build
on-failureには、ビルドを中止するABORTと、次のフェーズに進むCONTINUEも指定できます。再試行については、次の4種類が使えます。
RETRY
RETRY-{count}
RETRY-{regex}
RETRY-{count}-{regex}
{count}は再試行の回数です。プロジェクト単位の「自動再試行の上限」が0〜10回であるのに対して、こちらは0〜100回まで指定できます。{regex}では、指定した正規表現に一致するエラーメッセージが発生した場合に再試行するよう設定できます。
RETRYの基本動作
先ほどと同じように必ず失敗する処理へRETRYを設定してみます。
build: on-failure: RETRY commands: - echo "Hello World" - exit 1
実行ログは次のようになりました。同じビルドの中で再試行されているのが確認できます。

RETRYでは最大3回再試行されるため、この例では最初の実行を含めて4回、ビルドフェーズで定義したコマンドがはじめから実行されました。
RETRYのregexが評価するエラーメッセージ
先ほど挙げたとおり、RETRY-{regex}を使うと、エラーメッセージによって再試行するかどうかを指定できます。
接続先の一時的なサーバエラーのときだけ再試行できると便利そうなので、検証しました。
まずエラーメッセージの確認のため、接続できないlocalhostのポートへアクセスするbuildspecを実行してみます。
build: commands: - curl -sS http://localhost:65432
実行すると、ビルドログにエラーメッセージが表示されます。
[Container] 2026/09/08 12:15:19.107654 Running command curl -sS http://localhost:65432 curl: (7) Failed to connect to localhost port 65432 after 0 ms: Could not connect to server [Container] 2026/09/08 12:15:19.761742 Command did not exit successfully curl -sS http://localhost:65432 exit status 7 [Container] 2026/09/08 12:15:19.768580 Phase complete: BUILD State: FAILED [Container] 2026/09/08 12:15:19.768598 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: curl -sS http://localhost:65432. Reason: exit status 7
ビルドログにはcurlがstderrに出力したエラーメッセージ(curl: (7) Failed to connect to localhost port 65432 after 0 ms: Could not connect to server)が表示されているため、次のように設定してみます。
build: on-failure: "RETRY-2-.*Failed to connect.*" commands: - curl -sS http://localhost:65432
ところが、この設定では再試行されず、一回きりの実行でそのままbuildフェーズが終了してしまいました。

on-failureを付けずに実行したビルドログを見直すと、buildフェーズの最後でCodeBuildは次のエラーメッセージを出力していました。
Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: curl -sS http://localhost:65432. Reason: exit status 7
そこで、このメッセージに含まれる文字列をregexへ指定します。
build: on-failure: "RETRY-2-.*Error while executing command: curl -sS http://localhost:65432.*" commands: - curl -sS http://localhost:65432
すると、今度は指定した2回の再試行が行われました。

on-failureのregexは、フェーズのエラーメッセージ(phase contextsのmessage)を評価しているようです。
コマンドの実行に失敗した場合、このメッセージには実行したコマンド自体も含まれます。このため、実行コマンドに含まれる文字列をregexで指定し、特定のコマンドが失敗した場合をリトライ対象にすることができます。
一方で、コマンドがstderrに出力した内容は評価対象に含まれないため、詳細なエラー内容に応じて再試行することは、on-failureでは実現できなさそうです。
finallyが失敗した場合のon-failureの動作
buildspecの各フェーズでは、commandsとは別にfinallyを指定できます。このfinallyはcommandsの成功・失敗にかかわらず実行されるブロックです。
では、finallyの中でエラーになった場合、on-failureはどう動くのでしょうか。
build: on-failure: 'RETRY-2-.*echo "finally".*' commands: # commandsブロックは成功する - echo "commands 1" - echo "commands 2" finally: - echo "finally" && exit 1 # finallyブロックは失敗する
実行したところ、finallyの失敗も再試行の対象となり、2回再試行されました。フェーズが成功するのはcommandsとfinallyのコマンドがすべて成功した場合で、どちらのブロックであっても失敗すればフェーズの失敗になります。このためfinallyの失敗もon-failureの評価対象となり、フェーズのcommandsから実行し直されました。

まとめ
on-failureについてはbuildspecの書き方を確認していたときに知ったのですが、検証してみて分かった想定と違う動作もありました。業務でCodeBuildを利用するときも、各仕様を押さえて使っていければと思っています。
ここまで読んでいただきありがとうございます! この記事が少しでも参考になれば幸いです。