LoginSignup
14
1

More than 1 year has passed since last update.

はじめに

本記事では、AWS CodeBuildのキャッシュを利用してビルド時間を短縮する方法について紹介します。

AWSマネージドサービスを使用したCICDパイプラインでは、AWS Codeシリーズ1を使用します。検証環境でAWS Codeシリーズを用いてCICDパイプラインを構築・運用した際、特にCodeBuildを使用したビルド時間に大幅な時間を費やしていることに対して課題と感じました。本記事では、その課題に対する改善方法についての調査結果を共有します。

尚、本記事の目的は、CodeBuildによるビルド時間を短縮する方法の情報提供となります。測定は検証環境で要したサンプルアプリ2を使用して得られたものであり、測定結果の正確性、有用性などにつきましては、一切保証するものではない点をご了承ください。

また、本記事ではAWS各種サービスについての概要、およびビルド時間短縮に対して直接的に関係しない以下の設計・構築方法については割愛させていただきます。

  • ブランチ戦略(Git flowなど)
  • パラメータ管理方法
  • デプロイ方法(ローリングアップデート/カナリアリリース/Blue Greenデプロイなど)

検証の背景

検証環境にて、疑似システム(以降、モデルシステムと呼称)の開発を行った際、以下のCICDパイプラインを構築しました。

CICD_Pipeline.png

開発者がローカル開発環境で開発したソースコードをAWS CodeCommitへPushしたことを契機に、CICDパイプラインが起動します。アプリケーションのビルド、Dockerイメージの作成、ECRにDockerイメージをPush、CodeDeployによるECS on Fargateへのデプロイまでのプロセスを自動化しています。

なお、モデルシステムの実行環境はECS on Fargateを採用していますが、以降に説明するビルド時間短縮方法は、それ以外のAWSサービス(例えば、EC2やECS on EC2、EKS on EC2など)でも実現可能です。

課題

CICDパイプラインを構築してデリバリを自動化し、開発を効率化することができました。一方、頻繁にソースコードの更新を行い、CICDパイプラインを実行すると、アプリケーションのビルド~デプロイまでに時間がかかることがストレスに感じました。
工程ごとに処理時間を調査したところ、CodeBuildによるPROVISIONINGPRE_BUILDに大幅な時間を費やしていることが分かりました。

build_phase_long_time.png

PROVISIONINGとは、ビルド用のコンテナを準備している工程です。以下のようなビルド環境の条件に左右されます。

  • ビルド用のコンテナの環境イメージ
  • ビルド用のコンテナをVPCに配置するか否か
  • ビルド用のコンテナのスペック

例えば、ビルド用コンテナのコンピューティングリソースのスペックを上げる方法などでPROVISIONINGの処理時間を短縮できますが、本記事では対象外とします。

PRE_BUILDとは、ビルドする前の準備工程です。具体的には、以下のビルド仕様を定義したbuildspec.ymlpre_buildで実行している箇所になります。

buildspec.yml
version: 0.2
env:
  parameter-store:
    AAC_REPOSITORY_URI: "mdlsys-ssm-pram-aac-ecr-uri"
    AAC_IMAGE_TAG: "mdlsys-ssm-pram-aac-img-tag"
phases:
  install:
    runtime-versions:
      docker: 19
  pre_build:
    commands:
      - echo Entered the pre_build phase...
      - apt-get update -y
      - apt-get install -y maven
      - mvn -f aac/ctr/pom.xml package
  build:
    commands:
      - echo Entered the build phase...
      - echo Logging in to Amazon ECR...
      - $(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email)    
      - echo Building the Docker image...
      - docker build -f ./aac/ctr/src/main/docker/Dockerfile.jvm -t $AAC_REPOSITORY_URI:latest ./aac/ctr
      - docker tag $AAC_REPOSITORY_URI:latest $AAC_REPOSITORY_URI:$AAC_IMAGE_TAG
  post_build:
    commands:
      - echo Entered the post_build phase...
      - echo Build completed on `date`
      - echo Pushing the Docker image...
      - docker push $AAC_REPOSITORY_URI:$AAC_IMAGE_TAG
      - printf '{"Version":"1.0","ImageURI":"%s"}' $AAC_REPOSITORY_URI:$AAC_IMAGE_TAG > imageDetail.json
artifacts:
  files:
    - imageDetail.json

buildspec.ymlpre_buildでは、大きく以下の2つを実施しています。

  1. apt-get update -yapt-get install -y mavenにより、Apache Mavenをインストール
  2. mvn -f aac/ctr/pom.xml packageにて、ソースコードをコンパイル、テストして、ビルド出力アーティファクトにパッケージ化

特に、No.2にて、パッケージ化する際、依存関係のあるライブラリをインターネット経由で、外部のリポジトリから取得する工程(以降、ライブラリ取得工程と呼称)に大幅な時間を費やしていることが課題でした。

目的

一般的にCICDパイプラインを構築すると、課題のようにライブラリ取得工程に時間がかかります。
本記事では、その工程にかかる時間を短縮できる方法を調査し、結果を共有することで、少しでも読者の皆さんが構築しているCICDパイプラインの改善の参考になれば、と思い執筆しました。

AWS CodeBuildによるビルドキャッシュ方法

ライブラリ取得工程に費やす時間はキャッシュの活用により短縮できます。
キャッシュの活用方法は、以下の3つがあります。

# キャッシュ方法 概要 ユースケース
1 CodeBuildのローカルキャッシュ ビルドホストのみが利用できるキャッシュをそのビルドホストのローカルに保存する。
  • キャッシュはビルドホストですぐに利用できるため、中から大規模へのビルドアーティファクトに適している
  • ビルドの頻度が低い場合は向かない
2 CodeBuildのS3キャッシュ 複数のビルドホスト間で利用できるキャッシュをAWS S3バケットに保存する。
  • 小から中間サイズのビルドアーティファクトに適している
  • ビルド頻度が低い場合や複数のパイプラインでキャッシュを使いまわす場合
3 EFSキャッシュ AWS EFSにキャッシュを保存する。CodeBuildネイティブではないため、ユーザ側でキャッシュ処理を行う必要がある。(※)
  • ビルドコンテナのディスク容量を多く使いたい場合
  • 複数のビルド間でキャッシュを使いまわす場合
  • データの読み取り(ダウンロード)を高速化したい場合

※事前にキャッシュ対象をZip化しEFSにコピーする必要があります。キャッシュ利用時はCodeBuildがEFSをマウントし、EFSからZipをローカルにダウンロードし展開して利用します。

複数のビルドホスト間でキャッシュを使いまわすことがモデルシステム開発の要件であった為、#2 CodeBuildのS3キャッシュ#3 EFSキャッシュについて、どのくらい時間が短縮できるか検証しました。

検証

検証観点

単純にライブラリ取得工程に費やす時間を検証観点とします。

また、S3キャッシュとEFSキャッシュともに、以下のビルド用コンテナを使用し、実行環境の条件を同一にして検証を進めます。

  • ビルドコンテナのイメージ : aws/codebuild/standard:4.0-20.03.13
  • コンピューティングリソース : 3 GB メモリ、2 vCPU
  • ビルドコンテナ : VPC内に配置

検証手順

S3キャッシュ

実際に検証してみましょう。まず、S3キャッシュの設定方法について簡単に説明します。

事前作業

以下の事前作業を行ってください。本記事では、手順を割愛させていただきます。

  • キャッシュを保存するS3バケットの作成

手順

CodeBuildのS3キャッシュを使用する為の手順は、以下2つのステップのみです。

1. ビルドプロジェクトの編集

対象となるビルドプロジェクトを選択し、「編集」「アーティファクト」を押下します。

how_to_set_up_s3_cache_1.png

青枠で囲われたように、キャッシュを保存するS3バケット(事前作業にて作成したS3バケット)を指定します。

Inkedhow_to_set_up_s3_cache_2_v2.jpg

2. buildspec.ymlにて、cacheフェーズを追加

S3バケットにキャッシュとしてアップロードする対象のファイルを指定し、キャッシュを利用できるようにします。
mavenのデフォルト設定で、依存関係のあるライブラリは.m2配下に保存されるため、今回はそれらがキャッシュ対象となります。
cacheフェーズに、キャッシュ対象を追記します。

buildspec.yml
version: 0.2
env:
  parameter-store:
    AAC_REPOSITORY_URI: "mdlsys-ssm-pram-aac-ecr-uri"
    AAC_IMAGE_TAG: "mdlsys-ssm-pram-aac-img-tag"
phases:
  install:
    runtime-versions:
      docker: 19
  pre_build:
    commands:
      - echo Entered the pre_build phase...
      - apt-get update -y
      - apt-get install -y maven
      - mvn -f aac/ctr/pom.xml package
  build:
    commands:
      - echo Entered the build phase...
      - echo Logging in to Amazon ECR...
      - $(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email)    
      - echo Building the Docker image...
      - docker build -f ./aac/ctr/src/main/docker/Dockerfile.jvm -t $AAC_REPOSITORY_URI:latest ./aac/ctr
      - docker tag $AAC_REPOSITORY_URI:latest $AAC_REPOSITORY_URI:$AAC_IMAGE_TAG
  post_build:
    commands:
      - echo Entered the post_build phase...
      - echo Build completed on `date`
      - echo Pushing the Docker image...
      - docker push $AAC_REPOSITORY_URI:$AAC_IMAGE_TAG
      - printf '{"Version":"1.0","ImageURI":"%s"}' $AAC_REPOSITORY_URI:$AAC_IMAGE_TAG > imageDetail.json
artifacts:
  files:
    - imageDetail.json
cache:
  paths:
    - '/root/.m2/**/*'    

以上がS3キャッシュの設定方法となります。

結果

実際にCICDパイプラインを実行し、CodeBuildのフェーズ詳細を確認してみましょう。

build_phase_short_time_by_s3.png

PRE_BUILDフェーズの時間が短縮されており、キャッシュが効いていることが分かります。

EFSキャッシュ

続いて、EFSキャッシュの設定方法を簡単に説明します。

事前作業

以下の事前作業を行ってください。本記事では、手順を割愛させていただきます。

  • キャッシュを保存するEFSの作成
  • ローカル開発環境にて、ビルド時に取得した.m2配下のライブラリをZip化し、EFSにアップロード

手順

CodeBuildのEFSキャッシュを使用する為の手順は、以下の2つのステップで設定できます。

1. ビルドプロジェクトの編集

対象となるビルドプロジェクトを選択し、「編集」「環境」を押下します。

how_to_set_up_efs_cache_1.png

CodeBuildがEFSをマウントできるように設定します。事前作業で作成したEFSを指定してください。

how_to_set_up_efs_cache_2.png

2. buildspec.ymlにて、EFSにあるZipファイルを展開する処理を追加

pre_buildフェーズに事前作業でアップロードしたZipファイルをCodeBuildのビルドホストで解凍する処理を追記します。

buildspec.yml
version: 0.2
env:
  parameter-store:
    AAC_REPOSITORY_URI: "mdlsys-ssm-pram-aac-ecr-uri"
    AAC_IMAGE_TAG: "mdlsys-ssm-pram-aac-img-tag"
phases:
  install:
    runtime-versions:
      docker: 19
  pre_build:
    commands:
      - echo Entered the pre_build phase...
      - apt-get update -y
      - apt-get install -y maven
      - unzip $CODEBUILD_MDLSYS_BUILD_CACHE/repository.zip
      - mkdir ~/.m2/repository      
      - mv root/.m2/repository/* ~/.m2/repository
      - mvn -f aac/ctr/pom.xml package -Dmaven.test.skip=true
  build:
    commands:
      - echo Entered the build phase...
      - echo Logging in to Amazon ECR...
      - $(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email)    
      - echo Building the Docker image...
      - docker build -f ./aac/ctr/src/main/docker/Dockerfile.jvm -t $AAC_REPOSITORY_URI:latest ./aac/ctr
      - docker tag $AAC_REPOSITORY_URI:latest $AAC_REPOSITORY_URI:$AAC_IMAGE_TAG
  post_build:
    commands:
      - echo Entered the post_build phase...
      - echo Build completed on `date`
      - echo Pushing the Docker image...
      - docker push $AAC_REPOSITORY_URI:$AAC_IMAGE_TAG
      - printf '{"Version":"1.0","ImageURI":"%s"}' $AAC_REPOSITORY_URI:$AAC_IMAGE_TAG > imageDetail.json
      - echo Entered the post_build phase...
artifacts:
  files:
    - imageDetail.json

以上がEFSキャッシュの設定方法になります。

結果

実際にパイプラインを実行し、CodeBuildのフェーズ詳細を確認してみましょう。

build_phase_short_time_by_efs.png

EFSキャッシュも同様にPRE_BUILDフェーズの時間が短縮されており、キャッシュが効いていることが分かります。

検証結果まとめ

検証結果は以下になります。

# キャッシュ方法 時間(秒)
1 キャッシュなし 299
2 S3キャッシュ 53
3 EFSキャッシュ 38

まとめ

CodeBuildのキャッシュ方法を調査し、実際に検証してライブラリ取得工程の時間を測定しました。
検証観点ライブラリ取得工程で費やす時間だと、単純にデータの読み取り(ダウンロード)速度がS3に比べて速いEFSに軍配が上がりました。

ただ、実際の開発現場だと観点が時間だけではなく、使用するAWSサービスの費用や運用負荷、シンプルな構成等が観点に入ると思います。
今回の場合、以下の理由から私はS3キャッシュを採用しています。

  • S3キャッシュとEFSキャッシュの時間差が15秒と少なく、S3キャッシュの時間が許容範囲であった
  • リソースの費用がS3の方が安い

また、EFSキャッシュのほうが構築作業に工数がかかるため、多数のパイプラインを作成するようなケースでは、その点も考慮する必要があります。

これらの検証結果を踏まえて、皆さんの快適なCICDライフの参考になれば幸いです。

補足

モデルシステム

モデルシステムは、以下のようにECS on Fargate上で稼働しているシンプルなアーキテクチャで構成しています。
mdlsys.png

参考情報


  • Amazon Web Services およびその他のAWS 商標は,米国およびその他の諸国におけるAmazon.com,Inc.またはその関連会社の商標です。
  • Javaは,Oracle Corporationおよびその子会社,関連会社の米国およびその他の国における登録商標です。
  • Dockerは、Docker Inc. の米国およびその他の国における登録商標もしくは商標です。
  • Mavenは、Apache Software Foundationの米国およびその他の国における登録商標もしくは商標です。
  • その他記載の会社名、製品名などは、それぞれの会社の商標もしくは登録商標です。

  1. AWS CodeCommit, CodePipeline, CodeBuild, CodeDeploy 

  2. サンプルアプリは、Quarkusの初期プロジェクト作成時に自動生成されるアプリベースに作成。本記事では、サンプルアプリの名前をAACと呼称 

14
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
1