AWS再入門ブログリレー2022 AWS CodeBuild編

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。枡川です。
当エントリは弊社コンサルティング部による『AWS 再入門ブログリレー 2022』の8日目のエントリです。
このブログリレーは、普段AWSサービスについて最新のネタ・深い/細かいテーマを主に書き連ねてきたメンバーの手によって、今一度初心に返って基本的な部分を見つめ直してみよう、解説してみようというコンセプトで実施しております。

AWSをこれから学ぼう!という方にとっては文字通りの入門記事として、またすでにAWSを活用されている方にとってもAWSサービスの再発見や2022年のサービスアップデートのキャッチアップの場となればと考えておりますので、ぜひ最後までお付合い頂ければ幸いです。

8日目のテーマはCodeBuildです。

はじめに

さっそく身も蓋もない話から始まるのですが、CodeBuildに入門する際は下記AWS BlackBeltが非常にわかりやすくまとまっています。

2020年11月の資料ですが、マネジメントコンソールの表示もそれほど変わってなかったのでとりあえずこれを見とけば間違いないと思っています。
とはいえ、手を動かしながら少しでも気づきがあるようにと書いていきます。

CodeBuildとは?

ソフトウェア開発に関連するAWSサービスの一つで、AWSマネージドのビルドサービスという位置づけです。
[AWS Black Belt Online Seminar AWS CodeBuild]より引用

フルマネージドなサービスとなるので利用者がサーバを管理する必要がなく、簡単にコンテナを立ち上げてビルドやテストなどを行うことができます。
また、CodeBuild用のAWSマネージドイメージにはAWS CLIやDockerなどがあらかじめインストールされています。
もちろん、カスタムイメージを用意して柔軟に環境を定義することも可能です。
AWSのサービスらしく、料金も利用した分のみの支払いとなります。

CodeBuildの使い方について

CodeBuildは下記のリポジトリプロバイダをサポートしています。

  • CodeCommit
  • GitHub(GitHub Enterpriseも含む)
  • Bitbucket
  • S3

これらのリポジトリにbuildspec.ymlとしてビルド仕様を定義するファイルを含めておき、その定義ファイルに沿ってビルドを行うのが一般的な使い方となります。
マネジメントコンソールやCLIで手動実行することも、CodePipelineに組み込んでpush時等に自動で実行させることも可能です。

buildspec.ymlについて

ビルドの仕様を決めるファイルになります。
項目は全部で9つありますが、とりあえずはversionphasesだけ記載すれば問題ないです。
versionではbuildspec.ymlのバージョンを指定するのですが、新しく作成する場合は0.2を選んでおけば良いのでphasesに実行するコマンドを記載すればCodeBuildを利用開始できます。
[AWS Black Belt Online Seminar AWS CodeBuild]より引用

実行するコマンドを記載する部分であるphasesだけもう少し触れておきます。
installpre_buildbuildpost_buildと4つのフェーズが存在し、各フェーズに実行するコマンドを書き込んでいきます。
この通りになっていなくても動いてしまいますが、可読性やメンテナンス性を考えた時に可能な限りルールに従って記載するべきです。
[AWS Black Belt Online Seminar AWS CodeBuild]より引用

Node.js+Jestで試してみる

簡単にCodeBuildを使ってみます。
今回はNode.jsで簡単なFizzbuzzのプログラムを作成し、CodeBuildでJestを使用したテストのみ行うこととします。
テストに通ったらデプロイといったパイプラインを組むことが多いと思いますが、今回はCodeBuildに着目しているため、テスト結果を見て満足して終わることにします。

ビルドプロジェクトを作成していきます。

ビルドの名前を決めます。

ソースを指定します。
今回はGithubを使用します。
CodeBuildでGithubと連携する際は、OAuthまたは個人用アクセストークンを使用することができます。

環境について設定します。
今回はAmazon Linux2のイメージとしてaws/codebuild/amazonlinux2-x86_64-standard:3.0を使用します。
現在東京リージョンで使用可能なマネージドイメージは下記になります。

  • aws/codebuild/amazonlinux2-x86_64-standard:2.0
  • aws/codebuild/amazonlinux2-x86_64-standard:3.0
  • aws/codebuild/amazonlinux2-aarch64-standard:1.0
  • aws/codebuild/amazonlinux2-aarch64-standard:2.0
  • aws/codebuild/standard:4.0
  • aws/codebuild/standard:5.0

マネコンからは確認できませんが、aws/codebuild/standard:4.0はUbuntu 18.04、aws/codebuild/standard:5.0はUbuntu 20.04を使用することになります。
また、バージニア北部やオハイオなどの一部リージョンではWindows Serverのイメージも提供されています。
使えるイメージについては下記を参照して下さい。

使用可能なランタイムについては下記にまとまっております。
例えば、使用可能な最新のNode.jsバージョンは、Amazon Linux2なら12、Ubuntuであれば14となります。

CodeBuildが他のAWSサービスにアクセスするためのIAMロールも新規作成します。
CodeBuildを実行したり、ログを出力するための権限が自動で付与されます。

環境に関しては他にも多くの設定項目があります。
CodeBuildのデフォルトのタイムアウトは1時間ですが、8時間まで延長することができます。
また、コンピューティングについても下記から選択することが可能です。
ラジオボタンを変更するだけでサクッとパフォーマンスを増強できるのは魅力的ですね。

  • 3GBメモリ、2vCPU
  • 7GBメモリ、4vCPU
  • 15GBメモリ、8vCPU
  • 145GBメモリ、72vCPU

Buildspecは今回ソースコードにbuildspec.ymlを含めているので設定しません。
マネジメントコンソールからもbuildspec.ymlをソースコードに含めた場合と同様に設定することも可能です。
buildspec.ymlはデフォルトで探しにいくビルド設定ファイルとなりますが、他のファイル名を指定することも可能です。
buildspec.yamlと書いても今回私が試した際は拾ってくれましたが、ドキュメントにもbuildspec.ymlとちゃんと書いてあるのでymlにした方が良いと思います。
バッチ設定と、処理の結果をS3に出力するためのアーティファクトはデフォルトで進めます。
テストレポート等の出力はあるのですが、テストレポートはレポートとして出力可能なのでそちらで設定します。
ログ設定は下記にします。
特に設定しなくても/aws/codebuild/ビルドプロジェクト名でロググループを作成してくれますが、名前を設定することも可能です。

buildspec.ymlは下記のように記載します。
jestでテストを行って、ビルドレポートとカバレッジレポートを出力するのみです。

version: 0.2

phases:
  install:
    runtime-versions:
      nodejs: 12
  pre_build:
    commands:
      - node -v
      - npm ci
  build:
    commands:
      - npm test

reports:
  jest-reports:
    files:
      - test-result.xml
    file-format: JUNITXML
    base-directory: reports
  coverage-report:
    files:
      - 'coverage/clover.xml'
    file-format: 'CLOVERXML'

package.jsonは下記のように設定しており、npm testでjest --coverageが実行されるように設定しています。

{
  "scripts": {
    "test": "jest --coverage"
  },
  "devDependencies": {
    "jest": "^27.4.7",
    "jest-junit": "^13.0.0"
  }
}

今回、versionphasesだけでなく、reportsも設定してしています。
出力したテストレポートやカバレッジレポートをマネジメントコンソール上に可視化するためです。
テストレポートとしては、下記ファイル形式がサポートされています。

  • Cucumber JSON (.json)
  • JUnit XML (.xml)
  • NUnit XML (.xml)
  • NUnit3 XML (.xml)
  • TestNG XML (.xml)
  • Visual Studio TRX (.trx)

S3にエクスポートしない場合、テストレポートは30日で削除されます。
また、テストのカバレッジレポートもマネジメントコンソール上で可視化できます。
ここでカバレッジは、ラインカバレッジとブランチカバレッジがサポートされています。
[AWS Black Belt Online Seminar AWS CodeBuild]より引用

テストする対象のプログラムは下記となります。

module.exports = function fizzbuzz (num) {
  if (num % 15 === 0) {
    return 'FizzBuzz'
  }
  if (num % 3 === 0) {
    return 'Fizz'
  }
  if (num % 5 === 0) {
    return 'Buzz'
  }
  return String(num)
}

テストプログラムには下記のように書いたので、最後の行(3でも5でも割り切れない場合)はテストされないように書いています。

const fizzbuzz = require('./index')

test('returns FizzBuzz when num is divisible by both 3 and 5', () => {
  expect(fizzbuzz(30)).toBe('FizzBuzz')
})

test('returns Fizz when num is divisible by 3', () => {
  expect(fizzbuzz(6)).toBe('Fizz')
})

test('returns Buzz when num is divisible by 5', () => {
  expect(fizzbuzz(10)).toBe('Buzz')
})

最後の文を含めて、命令文は8行存在して、その内の1行を実行しません。
同様にif分岐が3回ありますが、3個目のif文がfalseになるテストは書いていません。
よって、ラインカバレッジでは7/8となり、ブランチカバレッジでは5/6となる予定です。

では、ビルドを実行してみます。
こんな感じでビルドのログが流れてステータスが成功になったら終了です。

まずはテストレポートの方から見ます。
全テストの合格率を可視化してくれています。
今回は3つしかテストがありませんが、ステータスで絞ったりフィルタを使って検索したりも可能です。

カバレッジレポートについても確認します。
ラインカバレッジとブランチカバレッジを両方意図した通りに表示してくれています。

通知機能について

Codeシリーズには専用の通知機能が存在します。

いろいろ作り込む必要がなくビルドの状態に応じて通知を設定することが可能です。
また、ターゲットとしてChatbotを使用することが可能なため、簡単にSlackへの通知を実現することができます。
通知はビルドプロジェクトの通知 > 通知ルールの作成から設定します。
Slackへの通知は事前にChatbotのページでSlackとの連携を済ませておく必要があります。
通知ルールの作成画面で通知をトリガーするイベントを選択可能です。
ターゲットとして、設定済みのChatbotを選択しました。
簡単にSlack通知を実現できました。

Session Managerでビルド環境に入ってみる

CodeBuildでは2020年7月からSession Mangerを使用したビルド環境へのアクセスをサポートしています。

普段は使わない機能ですが、ビルドに問題が生じた時にさくっとアクセスして調査できるのはとても嬉しいですよね。
Session Managerを使用する場合は、CodeBuildのサービスロールに下記権限を付与する必要があります。

{
  "Effect": "Allow",
  "Action": [
    "ssmmessages:CreateControlChannel",
    "ssmmessages:CreateDataChannel",
    "ssmmessages:OpenControlChannel",
    "ssmmessages:OpenDataChannel"
  ],
  "Resource": "*"
}

その上で、codebuild-breakpointという専用コマンドをbuildspec.ymlに組み込むことで、ビルドを一時停止してビルドコンテナにアクセスすることが可能です。
今回はbuildspec.ymlを修正して下記にします。

phases:
  install:
    runtime-versions:
      nodejs: 12
  pre_build:
    commands:
      - node -v
      - npm ci
  build:
    commands:
      - echo "Hello World" > /tmp/hello-world
      - codebuild-breakpoint
      - npm test

buildspec.ymlを修正しただけだとセッションマネージャーでのアクセスはできず、ビルドプロジェクト側での設定を変更する必要があります。
CodeBuildは設定を変更して上書きで開始することができるのでその機能を使います。
上書きで開始した場合、設定を変えることが可能ですが、元のプロジェクトの設定自体は変更されません。
高度なビルドの上書きへ移動します。
環境欄にセッション接続の有効化というチェックボックスがあるのでチェックしてビルドを開始します。
codebuild-breakpointの所でビルドが止まり、AWSセッションマネージャーという表記が存在するのでクリックします。
すると、見慣れたセッションマネージャの画面が立ち上がります。
直前に差し込んだコマンドが反映された後にビルドが止まっていることも確認できました。
なお、ビルドを再開するためにはコンソールでcodebuild-resumeを実行する必要があります。

ECRへプッシュする場合

Dockerを使用している場合も多いと思いますので、CodeBuildでDockerイメージをビルドしてECRにプッシュする場合についても触れておきます。
下記の公式サンプルページを参考にすれば設定可能です。

ECRへプッシュする際は下記権限を追加する必要があります。
Resouceは適宜絞って下さい。

{
    "Effect": "Allow",
    "Action": [
        "ecr:BatchCheckLayerAvailability",
        "ecr:CompleteLayerUpload",
        "ecr:GetAuthorizationToken",
        "ecr:InitiateLayerUpload",
        "ecr:PutImage",
        "ecr:UploadLayerPart"
    ],
    "Resource": "*"
}

今回は環境変数も設定してみます。
環境 > 環境変数で設定すればすぐに使えるようになります。
buildspec.ymlは下記です。

version: 0.2

phases:
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
  build:
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...          
      - docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
      - docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG      
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker image...
      - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG

このように簡単にECRへpushすることが可能です。
一方でサンプルアプリをそのまま実行した場合、下記エラーに直面する可能性があります。(そこそこ頻繁にあります)

toomanyrequests: You have reached your pull rate limit. You may increase the limit by authenticating and upgrading: https://www.docker.com/increase-rate-limit

これはDockerHubのDownload Rate Limitに関するものであり、ビルド内でDocker Hubにログインするなどの方法で回避可能です。
詳細については下記エントリにまとまっております。

CodeBuildでのキャッシュ使用について

CodeBuildではキャッシュを使用することができ、ビルドにかかる時間を短縮可能です。
キャッシュを保持する場所でS3キャッシュとローカルキャッシュの2通りが存在します。
S3キャッシュは複数のビルドホスト間で利用することができますが、ネットワーク経由で転送することによりパフォーマンス影響が発生する可能性があります。
ローカルキャッシュはそのビルドホストのみが利用できるキャッシュをそのビルドホストに保存します。
具体的なキャッシュの削除期間については記載がありませんが、頻繁にビルドしているプロジェクトでないとキャッシュが効いてくれません。
ローカルキャッシュの1機能として下記のようにDocker Layerキャッシュ等も実現可能です。

CodeBuildの無料枠について

CodeBuildではbuild.general1.smallでのビルドについて月あたり100分の無料枠が存在しています。
この無料枠については12ヶ月の制限が無く、どのアカウントでも無料で使用することが可能です。
是非使ってみましょう!

最後に

以上、『AWS 再入門ブログリレー 2022』の8日目のエントリ『CodeBuild』編でした。
CodeBuildをこれから始める人の参考になると非常に嬉しいです。
来週の2/14(月)はアシさんの「SQS編」の予定です。
楽しみにしておいて下さい!