エムオーテックス株式会社が運営するテックブログです。

AWS CodeBuild のバッチビルド機能を利用するとビルド効率が 1.5 倍に向上した話

はじめに

こんにちは、サービス戦略課の森田です。
サービス戦略課では LANSCOPE エンドポイントマネージャー クラウド版の技術的負債解消やフローの自動化など開発者の生産性向上のためのサービス改善に日々取り組んでいます。

今回は AWS CodeBuild のバッチビルド機能を使って、ビルド時間を短縮した方法についてご紹介したいと思います。

AWS CodeBuild のバッチビルドとは

まず、AWS CodeBuild は、開発者がアプリケーションのビルド、テスト、デプロイメントを効率的に管理できる AWS のクラウドベースのサービスです。

CI / CD パイプラインの一部として AWS CodeBuild を活用することで、アプリケーションのビルドプロセスを自動化し、開発プロジェクトの効率性を向上させ、迅速なデプロイメントを実現できます。

しかし、大規模で複雑なアプリケーションでは、ビルドやテストに時間がかかることがあるかもしれません。

AWS CodeBuild のバッチビルドを利用することで、複数のビルドタスクを同時に実行できます。
これにより、ビルド時間を大幅に短縮することができます。

弊社の課題

弊社のあるアプリケーションでは、AWS CodeBuild でのビルドに約 1 時間近くかかっていました。
これにより、開発者がアプリケーションを修正し、AWS 環境にデプロイして動作確認する際、開発者の待ち時間が著しく増加してしまいました。
同様に、本番環境へのリリース作業も時間がかかり、開発者にとって負担となっていました。

ビルド対象のアプリケーションの構成を確認すると、主に以下の 3 つのコマンドが実行されていました。

  1. API Gateway に統合された Lambda のデプロイ。
  2. 他のマイクロサービスからのイベントを受けて動作する Lambda のデプロイ。
  3. 脆弱性を検出するためのスクリプトの実行。

これらの 3 つのコマンドは直列で実行されており、それぞれに時間がかかっていたことから、ビルドに合計で約 1 時間かかっているという状況です。

解決方法

課題解決のために、以下の方法を検討しました。

  • ビルドプロセスを並列化する。
  • テストを並列化する。
  • ビルド成果物をキャッシュする。
  • ビルドスクリプトから不要なものを省く。
  • コンテナイメージのサイズを小さくする。
  • CodeBuild のイメージが旧世代の場合、最新のバージョンに更新する。
  • キャッシュされた Docker イメージを利用する。
  • ビルド時に不要なソースファイルのアップロードを回避する。
  • ビルド実行環境のスペックを向上させる。
  • 依存関係のパッケージのダウンロード順序を調整する。
  • コンテナのビルドに BuildKit を利用する。

しかし、実際はほとんどの対策が実施されていまして、ビルドプロセスの並列化はできていませんでした。
そこで、費用対効果が高そうだと判断し、取り組むことにしました。

今回、ビルドプロセスの並列化は CodeBuild のバッチビルド機能を活用しました。*1

バッチビルド機能は、以下の 3 つのタイプから選択できます。

  1. ビルドグラフ:
    • 複数のビルドタスクが相互に依存している場合、実行順序を定義し、順次にビルドを実行できます。
  2. ビルドリスト:
    • 複数のビルドタスクを同時に実行できます。
  3. ビルドマトリックス:
    • 同じビルドプロセスで異なるパラメーターが必要な場合、それぞれの組み合わせを並列でビルドできます。

これらのオプションを適切に選択することで、ビルドプロセスを最適化し、効率的な並列ビルドを実現できます。

対象のアプリケーションでは、ビルドタスク間に依存関係はなかったため ビルドリスト を選択しました。

改修内容

AWS CodeBuild は buildspec ファイルにビルドプロセスを定義します。
改修内容としては、主に buildspec ファイルを変更しています。

変更前

1 つの buildspec ファイルに 5 つのコマンドを定義していました。

buildspec.yml
 └①脆弱性スキャンの実行
 └②API のデプロイ
 └③Consumer1 のデプロイ
 └④Consumer2 のデプロイ
 └⑤Consumer3 のデプロイ
 └⑥Consumer4 のデプロイ

buildspec ファイルのイメージは以下のとおりです。

version: 0.2
phases:
  install:
    runtime-versions:
      java: corretto8
  pre_build:
    commands:
      - ①脆弱性スキャンの実行
  build:
    commands:
    - ビルド実行
  post_build:
    commands:
    - ②API のデプロイ
    - ③Consumer1 のデプロイ
    - ④Consumer2 のデプロイ
    - ⑤Consumer3 のデプロイ
    - ⑥Consumer4 のデプロイ
cache:
  paths:
  - '/root/.ivy2/cache/**/*'

変更後

buildspec ファイルを以下の構成に分割しました。

buildspec_app_root.yml  ・・・ ビルド・デプロイを並列で行うためのルート定義。
 └buildspec_api.yml    ・・・ API
 └buildspec_consumer.yml ・・・ Consumer(1~4)
 └buildspec_scan.yml   ・・・ 脆弱性スキャン

以下は、それぞれの buildspec ファイルの例です。

# buildspec_app_root.yml
version: 0.2
batch:
  fast-fail: true
  build-list:
    - identifier: api
      buildspec: buildspec_api.yml
    - identifier: consumer
      buildspec: buildspec_consumer.yml
    - identifier: scan
      buildspec: buildspec_scan.yml
# buildspec_api.yml と buildspec_consumer.yml と buildspec_scan.yml はほとんど同じ以下の内容です。
version: 0.2
phases:
  install:
    runtime-versions:
      java: corretto8
  pre_build:
    commands:
      - XXXXX
  build:
    commands:
      - XXXXX
  post_build:
    commands:
      - XXXXX
cache:
  paths:
    - '/root/.ivy2/cache/**/*'

すべての buildspec ファイルをプロジェクトフォルダのルートに配置します。
buildspec_app_root.yml ファイルを CodeBuild プロジェクトに設定することで、並行してビルドが実行されます。

権限の付与

AWS CodePipeline から AWS CodeBuild を呼び出す際に、バッチビルドを利用するためには、以下の権限を付与する必要があります。
(適切な権限範囲に絞ってご利用ください。)

  • codebuild:StartBuildBatch
  • codebuild:StopBuildBatch
  • codebuild:RetryBuildBatch
  • codebuild:BatchGet*

改善効果

ビルドを 3 つに並列化することで、単純に 3 分の 1 にはなりませんが、大幅に短縮できることが確認できました。

  • 改善前:57分21秒
  • 改善後:
    • API:37分58秒
    • Consumer:26分52秒
    • 脆弱性スキャン:25分36秒

最も時間がかかる API がバッチビルド全体の最大ビルド時間となります。
つまり、約20分 (34%) の短縮効果を得ることができるため、従来のビルド時間と比較すると、ビルド効率が1.5倍に向上しました。

おわりに

AWS CodeBuild のバッチビルド機能を活用して、ビルド時間の改善に取り組んだ過程をご紹介いたしました。
ここまでお読みいただき、誠にありがとうございます。
本内容がお役に立てれば幸いです。

*1:実際には、バッチビルド機能を利用する以外にも、もう一つの対策を行っています。弊社独自のライブラリを使用したデプロイから、AWS Serverless Application Model (AWS SAM) を使用したデプロイに変更しました。
これにより、最大で 30 分の短縮が可能となっています。この変更については、別の記事で詳しく取り上げる予定です。