【第10回】AWS CodeBuildを使用したCI環境構築(前編)


AWS CodeBuildを用いた継続的インテグレーション自動化1


本連載では、以下のイメージに沿って「CodeBuild」「SonarQube」を使った継続的インテグレーション(CI:Continuous integration)環境を実際に構築しています。

../_images/sample-continuous-integration.png


前回までに、以下のイメージに沿った、マイクロサービスアプリケーションや、それを呼び出すWebアプリケーションの単体・結合・E2E(EndToEnd)テストのコードを実装しました。


../_images/MicroServiceArchitecture.png


続く今回は、GitHubにプッシュしたこのアプリケーションのソースコードやテストコードをAWS CodeBuildを使ってビルド・テストを行った後、カバレッジなどのソースコードメトリクスをスキャンした結果をSonarQuberServerへ連携する環境(最初のイメージの(3)〜(6))の構築を進めていきます。


CodeBuildの概要


AWS CodeBuildはクラウドでアプリケーションのビルドを行う従量課金型サービスです。 ビルドの元になるソースコードはS3に保存したものに加え、AWS CodeCommit、GitHub、BitBucketなどの各Gitベースのバージョン管理システムをサポートします。 Jenkins等同様のCIツールでも同じことは行えるものの、クラウドでマネージドな環境下で行われるため、大規模開発でのコミットやプルリクエスト後のテスト、ビルド処理をマシンリソースを気にせず実行できることがメリットです。

CodeBuildを利用するには、アプリケーションソースコードプロジェクト内のディレクトリに、ビルドを行う単位でbuildspec.ymlというビルド処理の手順を示したファイルを作成する必要があります。 このbuildspec.ymlを元に、CodeBuildが、Ubuntuをベースとしたデフォルトのビルド用コンテナイメージをAWSクラウド内で実行し、成果物アーティファクトを生成するようなかたちでビルド処理が実行されます。


シンプルなアプリケーションビルド処理の場合は、ルートディレクトリに配置したbuildspec.ymlをGitHub等へコミットして、AWSコンソールのCodeBuildサービス内で、ソースコードプロジェクトの配置場所やビルドコンテナの条件等の設定を行うにより簡単に環境構築できます。しかし、 実プロジェクトでアプリケーションをビルドする場合は、マルチプロジェクト構成や日本語ロケールなどに起因してトラブルが発生しがちであるため、まず最初に、buildspec.ymlのデバッグ環境構築を目的としてAWSから提供されている、buildspec.ymlの挙動をローカル端末で確認可能な CodeBuild Localの環境を構築し、buildspec.ymlを簡単に検証できるようにします。

また、CodeBuildを使ったビルド中に、静的解析結果やカバレッジなどを取得して、SonarQubeServerへ連携するSonarScannerの実行を盛り込みます。実行に必要なパラメータを環境変数として、一元的に管理できるAWS Systems Manager Parameter Storeも合わせて設定していきます。


CodeBuild Localの環境設定


CodeBuild Local は2018年5月からAWSによってサポートされています。 CodeBuild Localの利用により、以下のようなことができるようになります。

  • buildspec.ymlのローカルでの検証
  • codebuildと同等の環境でコミット前にアプリケーションをテストとビルド
  • ローカル環境で検証後、素早くバグフィクス

CodeBuild Localを利用するには、事前に以下を実施しておく必要があります。

  1. 実際にビルド実行環境コンテナイメージ(デフォルトではUbuntu)を作成
  2. 環境コンテナを起動するためのエージェントコンテナイメージを取得

なお、CodeBuild Localを実行する際は2のエージェントコンテナイメージをDocker runするかたちになりますが、実行スクリプトが提供されているため、 この実行スクリプトに1のコンテナイメージ名やアーティファクトの出力先フォルダ、認証情報など情報を渡して実行することになります。

1の手順としては、CodeBuild LocalのGitHub README の手順にそって、Dockerがインストールされたローカル端末でターミナルやGitBash等を使って、 適当なディレクトリで、ビルド実行環境のコンテナイメージがあるaws-codebuild-docker-imagesのレポジトリをGit cloneします。そして、ビルド用のコンテナイメージ(2019年6月時点の正式サポートでは、starndard:1.0および2.0)を構築する Dockerfileがあるディレクトリへ移動し、docker buildコマンドを実行します。


$ git clone https://github.com/aws/aws-codebuild-docker-images.git
$ cd aws-codebuild-docker-images
$ cd ubuntu/standard/2.0
$ docker build -t aws/codebuild/standard:2.0 .


続いて、2.環境コンテナを起動するためのエージェントコンテナイメージをプルします。


$ docker pull amazon/aws-codebuild-local:latest --disable-content-trust=false


これでCodeBuild Localを実行する準備は整いましたが、buildspec.ymlを作成していく前に、ビルド中にSonarScannerを実行するために、 SonarQubeのURLやトークンを、buildspec.yml中に定義した環境変数から取得できるよう、AWS Systems Manager Parameter Storeの設定を行います。


AWS Systems Manager Parameter Storeの概要と設定


AWS Systems Manager Parameter Storeはパスワードやデータベース文字列、ライセンスコードなどアプリケーションに直接実装せずに環境変数を経由して設定するデータ等を一元的に管理するためのサービスです。 特に多くのEC2インスタンスやECSタスク上にデプロイされたアプリケーションなどで同一のデータを参照したい場合有効であり、データは階層構造をとることができます。 データ値はプレーンテキストまたはAWS KMSを使用して暗号化データとして使用でき、Parameter Store自体で、暗号化や復号化も同時に実行してくれます。データの参照はIAMにより細かくアクセス制御でき、以下のAWSサービスから利用可能です。

  • Amazon EC2
  • Amazon ECS
  • AWS Lambda
  • AWS CloudFormation
  • AWS CodeBuild
  • AWS CodeDeploy

また、暗号化や通知、モニタリング、監査を行うため、以下のサービスと連動して機能します。

  • AWS KMS
  • Amazon SNS
  • Amazon CloudWatch
  • AWS CloudTrail

注釈

以前はデータサイズが4KBまで、スループット上限は規定範囲内でしたが、2019年4月のアップデートでアドバンスドパラメータオプションが導入 され、10000を変えるパラメータの作成、最大8KB、スループットの上限緩和(デフォルト40tpsから最大1000tpsまで)等、拡張されました。


Parameter Storeを利用するには、AWSコンソールで 「Systems Manager」サービスから「Parameter Store」メニューを選択し、「パラメータの作成」ボタンを押下します。


../_images/management_console_ssm_create_parameter_store_1.png


パラメータの名前と値をそれぞれ入力し、「パラメータの作成」ボタンを押下します。

../_images/management_console_ssm_create_parameter_store_2.png


なお、CodeBuildなど各サービスからParameterStoreを参照するためには、Sysmtems Managerの権限を付与しておく必要があります。 今回のCodeBuild Localのようにローカル端末からアクセスする場合は、SSMアクセス許可ポリシーをアタッチして権限をしたユーザの認証情報をローカル端末のホームフォルダ配下の.aws/credentialsに置いておく必要があります。 第2回「 Amazon RDSを使ったSonarQubeServer用データベースの構築 」の際、RDSへアクセスするユーザを作成し、権限を付与している手順を紹介していますが、 これ同様の要領で、ユーザに「AmazonSSMFullAccess」ポリシーを付与して、認証情報をローカル端末へ設定しておきましょう。

注釈

ユーザや認証情報をIAMを使って作成する実際の手順は Amazon RDSにアクセスするSpringアプリケーション(1) ユーザ/認証情報の作成 に記載しています。


buildspec.ymlの作成とCodeBuild Localの実行


CodeBuild Localを使用する事前準備ができたところで、buildspec.ymlを作成します。buildspec.ymlの記載方法・仕様は AWSの公式 CodeBuildのビルド仕様に関するリファレンス に詳細な説明がありますが、 ここでは、前回までに実装したBFFアプリケーション向けに作成したbuildspec.ymlをサンプルに、ポイントとなる箇所の解説を進めます。なお、buildspec.ymlはBackendおよびBFFアプリケーションについては、プロジェクトの配下にbuild/devディレクトリを作成してその中に保存しておきます。


注釈

次回以降で、別の用途でbuildspec.ymlを作成する必要があるので、CodeBuildがデフォルトで読み込む対象であるソースコードルートディレクトリに保存せず、別途ディレクトリを分けて保存しておきます。


version: 0.2
env:
  parameter-store:
    SONAR_HOST_URL: "SONAR_HOST_URL"
    SONAR_LOGIN: "SONAR_LOGIN_BACKEND_FOR_FRONTEND"                  # …(A)
phases:
  install:
    runtime-versions:
      docker: 18                                                     # …(B)
    commands:
      - apt-get update -y
      - apt-get -y install language-pack-ja-base language-pack-ja    # …(C)
  pre_build:
    commands:
      - /usr/sbin/update-locale LANG=ja_JP.UTF-8 LANGUAGE="ja_JP:ja"
      - export LC_ALL="ja_JP.UTF-8"
      - locale-gen ja_JP.UTF-8
      - dpkg-reconfigure locales                                     # …(D)
  build:
    commands:
      - mvn -f common/pom.xml install                                # …(E)
      - mvn -f backend-for-frontend/pom.xml package sonar:sonar -Dspring.profiles.active="test" -Dsonar.host.url=${SONAR_HOST_URL} -Dsonar.login=${SONAR_LOGIN}
                                                                     # …(F)
artifacts:
  files:
    - backend-for-frontend/target/mynavi-sample-continuous-integration-backend-for-frontend-0.0.1-SNAPSHOT.jar


BFFアプリケーションのビルド・テストするbuildspec.yml
項番 説明
AWS Systems Manager Parameter Storeで設定したデータを環境変数に設定します。ダブルクォーテーションで囲まれた値がParamter Storeで定義した名称です。
Ubuntu Standard Image 2.0 以降を使用する場合は、buildspec ファイルで runtime-versions を指定する必要があります。詳細については、「 buildspec ファイルのランタイムバージョンの指定 」を参照してください。
日本語ロケールのランゲージパックを取得します。
OSのデフォルトロケールを「ja_JP」に変更します。もとの「en_US」だと、アプリケーション内でSpringのMessageSourceから取得したメッセージが英語となりアサーションに失敗するため、デフォルトロケールを日本語におきます。
BFFアプリケーションは共通のcommonライブラリに依存しているため、事前にMaven installで.m2配下にライブラリをインストールしておきます。
mvnコマンドでゴールをpackageに指定し、BFFアプリケーションをテスト・ビルドします。またsonar:sonarで、SonarScannerプラグインを実行することにより、ビルド後にSonarQubeServerへ静的チェック結果を送信します。なお、このソースコードプロジェクト内にあるBFFのE2Eテストコードはバックエンドアプリケーションの起動が必要なため、このビルドでは実行されないよう、pom.xml上で実行をスキップする設定になっています。E2Eテストの設定クラスを読み込むことがないよう、Springプロファイル設定をspring.profiles.active="test"としておきます。
ビルドが完了すると、アーティファクトとしてbackend-for-frontend/targetフォルダに作成したJARファイルを保存します。


上記のサンプルの要領で、同様に、backendプロジェクトやcommonプロジェクトにbuildspec.ymlを作成します。 また、前節でgit cloneしたhttps://github.com/aws/aws-codebuild-docker-images.git内には、local_builds/codebuild_build.shがあるので、以下のように、ソースコードのプロジェクトルートにコピーしてください。


[mynavi-sample-continuous-integration]
    │
    ├-[backend]
    │   ├- src
    │   │   ├-main .....
    │   │   └-test .....
    │   ├- build
    │   │   ├-dev
    │   │   │  └- buildspec.yml
    │   │  .....
    │   └- pom.xml
    │
    ├-[backend-for-frontend]
    │   ├- src
    │   │   ├-main .....
    │   │   └-test .....
    │   ├- build
    │   │   ├-dev
    │   │   │  └- buildspec.yml
    │   │  .....
    │   └- pom.xml
    │
    ├-[common]
    │   ├- src
    │   │   ├-main .....
    │   │   └-test .....
    │   ├- buildspec.yml
    │   │  .....
    │   └- pom.xml
    │
    ├- codebuild_build.sh
    └- pom.xml


上記で作成したbuildspec.ymlに対して、CodeBuild Localを実行するには、プロジェクトルートで、codebuild_build.shの引数に以下のようなオプションを与えて実行します。


$ ./codebuild_build.sh -i aws/codebuild/standard:2.0 -a backend-for-frontend/target/ -c -b backend-for-frontend/build/dev/buildspec.yml


各オプションの説明は以下の通りです。


codebuild_build.shのオプション
オプション 説明
iオプション(必須) 事前準備で作成したCodeBuildでビルドするコンテナイメージを指定する。
aオプション(必須) アーティファクトを出力するディレクトリを指定する。
cオプション AWS認証情報を指定する(デフォルトでは~/.aws/credentialsの認証情報が使用される)
bオプション buildspec.ymlを指定する。


スクリプトを実行すると、以下の通り、CodeBuildがローカルのDocker環境で実行されます。


Build Command:

docker run -it -v /var/run/docker.sock:/var/run/docker.sock -e "IMAGE_NAME=aws/codebuild/standard:2.0" -e "ARTIFACTS=/xxxxx/mynavi-sample-continuous-integration/backend-for-frontend/target/" -e "SOURCE=/xxxxxx/mynavi-sample-continuous-integration" -e "BUILDSPEC=/xxxxxx/mynavi-sample-continuous-integration/backend-for-frontend/build/dev/buildspec.yml" -e "AWS_CONFIGURATION=/yyyyyy/.aws" -e "INITIATOR=kawabatakouhei" amazon/aws-codebuild-local:latest


Removing agent-resources_build_1 ... done
Removing agent-resources_agent_1 ... done
Removing network agent-resources_default
Removing volume agent-resources_source_volume
Removing volume agent-resources_user_volume
Creating network "agent-resources_default" with the default driver
Creating volume "agent-resources_source_volume" with local driver
Creating volume "agent-resources_user_volume" with local driver
Creating agent-resources_agent_1 ... done
Creating agent-resources_build_1 ... done
Attaching to agent-resources_agent_1, agent-resources_build_1
agent_1  | [Container] 2019/06/27 19:25:01 Waiting for agent ping

// omit

agent_1  | [INFO] Analysis total time: 24.128 s
agent_1  | [INFO] ------------------------------------------------------------------------
agent_1  | [INFO] BUILD SUCCESS
agent_1  | [INFO] ------------------------------------------------------------------------
agent_1  | [INFO] Total time:  02:14 min
agent_1  | [INFO] Finished at: 2019-06-28T10:04:54Z
agent_1  | [INFO] ------------------------------------------------------------------------
agent_1  |
agent_1  | [Container] 2019/06/28 10:04:54 Phase complete: BUILD State: SUCCEEDED
agent_1  | [Container] 2019/06/28 10:04:54 Phase context status code:  Message:
agent_1  | [Container] 2019/06/28 10:04:54 Entering phase POST_BUILD
agent_1  | [Container] 2019/06/28 10:04:54 Phase complete: POST_BUILD State: SUCCEEDED
agent_1  | [Container] 2019/06/28 10:04:54 Phase context status code:  Message:
agent_1  | [Container] 2019/06/28 10:04:54 Expanding base directory path: .
agent_1  | [Container] 2019/06/28 10:04:54 Assembling file list
agent_1  | [Container] 2019/06/28 10:04:54 Expanding .
agent_1  | [Container] 2019/06/28 10:04:54 Expanding artifact file paths for base directory .
agent_1  | [Container] 2019/06/28 10:04:54 Assembling file list
agent_1  | [Container] 2019/06/28 10:04:54 Expanding backend-for-frontend/target/mynavi-sample-continuous-integration-backend-for-frontend-0.0.1-SNAPSHOT.jar
agent_1  | [Container] 2019/06/28 10:04:54 Found 1 file(s)
agent_1  | [Container] 2019/06/28 10:04:54 Preparing to copy secondary artifacts
agent_1  | [Container] 2019/06/28 10:04:54 No secondary artifacts defined in buildspec
agent_1  | [Container] 2019/06/28 10:04:54 Phase complete: UPLOAD_ARTIFACTS State: SUCCEEDED
agent_1  | [Container] 2019/06/28 10:04:54 Phase context status code:  Message:


上記の、buildspec.ymlをGitHub上のソースコードにコミット・プッシュすると、CodeBuildを使用できる準備が整います。次回は、AWSコンソール上からCodeBuildを設定し、GitHubへのコミットやプルリクエストに応じてビルド・テストを実行するよう設定してみます。


著者紹介

川畑 光平(KAWABATA Kohei) - NTTデータ 課長代理

../_images/pic_image01.jpg

金融機関システム業務アプリケーション開発・システム基盤担当を経て、現在はソフトウェア開発自動化関連の研究開発・推進に従事。

Red Hat Certified Engineer、Pivotal Certified Spring Professional、AWS Certified Solutions Architect Professional等の資格を持ち、アプリケーション基盤・クラウドなど様々な開発プロジェクト支援にも携わる。

2019 APN AWS Top Engineers & Ambassadors 選出。