はじめに
勉強のため、AWSのCodeBuildを使ってみようと思ってドキュメントを見ていたところ、いちいちマネジメントコンソール画面からCodeBuildを実行しなくてもローカルマシンにビルド環境を構築することができるとのことなので、M2 Macのマシンでローカルビルド環境を構築してみました。
CodeBuildとは
アプリケーションを開発して動作させるようにするには、ソースコードをコンパイルしたり、必要なファイル等をまとめて資材を作成したりする必要がありますが、そのような面倒な作業をAWS側のサービスとして提供して、開発者の手間を減らしたり、CodeDeployやCodePipeline等の他サービスと連携することでアプリケーションのリリース作業を自動化したりするためのサービスです。
実際にどのファイルをどのようなコマンドオプションでコンパイルする等のCodeBuildで行う作業はbuildspec.ymlというファイルに記述して、その内容をCodeBuildで実行することで、指定のビルドが行えるようになっております。
CodeBuildをローカルで実行するということ
上述の通り、CodeBuildはAWSのマネージドサービスとなるため、buildspec.ymlで記述した設定が正しいかを確かめるためには通常AWSのマネジメントコンソール画面やAWS CLIから実際に実行してみるしかありませんが、作成中のbuildspec.ymlをいちいちCodeBuildの画面から実行してデバッグしていくのは一苦労ということで、ローカルマシン上でも環境を用意すればCodeBuildを実行できるようにしているそうです。
ローカルマシンで実行する場合とAWS上でCodeBuildを実行する場合の違い
CodeBuildはビルドプロジェクトと言うビルドの設計図を実行する際、Dockerコンテナを起動して、buildspec.ymlの内容を実行する仕組みとなっています。
ローカルで実行する場合もCodeBuildで実際に使用しているコンテナイメージを使用してローカルでbuildspec.ymlの内容を実行する仕組みとなるため、Dockerコンテナをどこで実行しているかという違いはありますが、環境の違い以外は同等のことが行えるようです。
ちなみに、CodeBuildはアプリケーションのビルドを実行することを目的としておりますが、そもそもがDockerコンテナなのでbuildspec.ymlの書き方次第でビルドとは関係ない処理も実行することができるそうなので、知っておくと設計の幅を広げることもできるかもしれません。
CodeBuildをローカルで実行する準備
導入方法は以下のAWS公式ドキュメントに記載されているため、こちらに沿って実行していきます。
M2のMacBook AirにAWS公式イメージを導入する場合の流れは以下となります。
- Gitクライアントインストール
- Dockerインストール
- Amazon Linux 2 のDockerイメージのダウンロード
- エージェントのDockerイメージのダウンロード
- CodeBuildエージェントのダウンロード
- buildspec.ymlの作成
- CodeBuildエージェントの実行
Gitクライアント、Dockerインストール
こちらは今回は紹介しませんが、それぞれダウンロードしてインストールしておきます。
Amazon Linux 2 のDockerイメージダウンロード
公式ドキュメント通り、docker pullコマンドでDockerイメージをダウンロードします。
バージョンについてはドキュメントのバージョンより新しいバージョンが出ているようなので、以下のように指定します。
docker pull public.ecr.aws/codebuild/amazonlinux2-x86_64-standard:4.0
エージェントのDockerイメージダウンロード
上記と同様docker pullコマンドでダウンロードします。
今回は実行マシンとしてM2 Macを使用するので、ARM版をダウンロードします。
docker pull public.ecr.aws/codebuild/local-builds:aarch64
CodeBuildエージェントのダウンロード
以下のように実行してエージェントをダウンロードします。
公式ドキュメントはwgetでダウンロードしておりますが、M2 Macにはデフォルトでwgetが含まれていないため、curlコマンドを以下のようにしてダウンロードします。
curl -OL https://raw.githubusercontent.com/aws/aws-codebuild-docker-images/master/local_builds/codebuild_build.sh
chmod +x codebuild_build.sh
buildspec.ymlの作成
テストのため以下のような内容のbuildspec.ymlファイルを作成して、先程ダウンロードしたCodeBuildエージェントのシェルと同じ場所に格納しておきます。
定番のHello Worldを表示するビルド定義ファイルです。
version: 0.2
phases:
install:
commands:
- echo 'Hello World'
CodeBuildエージェントの実行
最低限以下のようにして実行することでローカルで実行できます。
-aで指定するアーティファクト(生成物)は特に生成するような設定をしていなくても指定する必要があるので、適当な場所を指定しておきます。
./codebuild_build.sh -i public.ecr.aws/codebuild/amazonlinux2-x86_64-standard:4.0 -a artifact_output -l public.ecr.aws/codebuild/local-builds:aarch64
以下の結果のようにHello Worldの表示が出力され、exitコードが0になっていればOKです。
(省略)
agent_1 | [Container] 2022/09/01 12:54:01 Running command echo 'Hello World'
agent_1 | Hello World
agent_1 |
(省略)
agent-resources_agent_1 exited with code 0
Stopping agent-resources_build_1 ... done
Aborting on container exit...
codebuild_build.shのオプション
codebuild_build.shをオプション無しで実行した際のUSAGEでも内容が確認できますが、以下のオプションが指定可能です。
下記の書式の通り、-iと-aが必須オプションとなります。
codebuild_build.sh [-i image_name] [-a artifact_output_directory] [options]
| オプション | 説明 |
|---|---|
| -i [イメージ名] | 使用するコンテナイメージの指定(必須) |
| -a [出力先] | アーティファクト(生成物)の出力先の指定(必須) |
| -l [イメージ名] | デフォルトのローカルエージェントイメージを上書き |
| -r [出力先] | レポートの出力先の指定 |
| -s [ディレクトリパス] | ソースディレクトリの場所の指定 2つ目以降は、 <sourceIdentifier>:<sourceLocation>の書式で指定 |
| -c | ローカルのAWS認証情報を使用する場合に指定 |
| -p [プロファイル名] | デフォルト以外のプロファイルを使用する場合に指定 指定する場合は-cと一緒に使用 |
| -b [ファイルパス] | buildspec.ymlの名前以外でファイル名を使用する場合に指定 指定する場合、カレントディレクトリからの相対パスか絶対パスで指定 |
| -m | ソースディレクトリをコンテナに直接マウントする場合に指定 |
| -d | 特権モードでDockerコンテナを実行する場合に指定 |
| -e [環境変数ファイルパス] | 環境変数を記載したファイルを使用する場合に指定 |
それぞれCodeBuildのマネジメントコンソール画面の設定値に当てはまる内容となり、現状は上記のオプションが全てとなります。
以下でいくつかのオプションについて補足します。
-l デフォルトローカルエージェントイメージ上書き
特にオプションを指定しない場合、デフォルトのpublic.ecr.aws/codebuild/local-builds:latestが指定されますが、デフォルト以外のローカルエージェントイメージを使用したい場合に指定します。
例えばARM用のローカルエージェントイメージとなるpublic.ecr.aws/codebuild/local-builds:aarch64を使用したい場合は-lが必要となります。
-r テストレポートの出力先の指定
JUnitやJaCoCoといったテストフレームワークを使ったテストをbuildspec.ymlに盛り込んでいる場合、テストレポートを指定の場所に出力できるとのこと。
テストフレームワークについて私は詳しくないので、気になる方は以下のようなサイトをご確認ください。
-s ソースディレクトリの場所の指定
マネジメントコンソール上の設定で言うと、ソースの設定に当たります。
ローカルで実行する場合、CodeCommitやS3をソースに指定できないため、指定するディレクトリはローカルのパスとなり、何も指定しなかった場合はカレントディレクトリがソースディレクトリとなります。
また、1つ目のソースディレクトリにbuildspec.ymlの名前でビルド定義ファイルを格納している場合は、自動で認識するため、-bオプションは不要となります。
逆に1つ目のソースディレクトリ内にbuildspec.ymlが存在しなかったり、別のファイル名にしている場合は-bオプションで指定する必要があります。
2つ以上のソースディレクトリを指定する場合は、ディレクトリパス以外に<sourceIdentifier>という識別子の指定が必要となります。
これは、ソースディレクトリを呼び出す際、内部的に以下のような予約環境変数を使用しているため、識別子がないと識別できないからとなります。
| 変数 | 説明 |
|---|---|
| CODEBUILD_SRC_DIR | 1つ目のソースディレクトリパスを格納している変数 |
| CODEBUILD_SRC_DIR_[識別子] | 1つ目以降のソースディレクトリパスを格納している変数 |
予約されている変数は以下を参照してください。
-c、-p AWS認証情報の使用
-cはbuildspec.yml内でAWS CLIを使用したい場合にローカルマシンに設定しているAWS認証情報(~/.aws配下の情報)を使いたい場合に指定するオプションです。
また、-pはdefaultプロファイル以外のプロファイルを使用したい場合に指定するオプションです。
-m ソースディレクトリをコンテナに直接マウント
ローカルでCodeBuildを実行する場合、通常はDockerコンテナ起動時にソースディレクトリのファイルをコンテナ内にコピーしてから処理が実行されるそうですが、-mを使用すると、ソースディレクトリのファイルをコピーするのではなく、ソースディレクトリを直接マウントする動作となるようです。
試しにソースディレクトリ配下に1GBのファイルを作成し、起動してみたところ、-mをつけない場合はソースディレクトリ配下のファイルダウンロードに20秒かかっているのに対し、-mをつけてDockerコンテナに直接マウントさせたところ、1秒ですみました。
(抜粋)
agent_1 | [Container] 2022/09/02 11:58:20 Waiting for agent ping
agent_1 | [Container] 2022/09/02 11:58:21 Waiting for DOWNLOAD_SOURCE
agent_1 | [Container] 2022/09/02 11:58:41 Phase is DOWNLOAD_SOURCE
(抜粋)
agent_1 | [Container] 2022/09/02 11:59:01 Waiting for agent ping
agent_1 | [Container] 2022/09/02 11:59:02 Waiting for DOWNLOAD_SOURCE
agent_1 | [Container] 2022/09/02 11:59:03 Phase is DOWNLOAD_SOURCE
実際のCodeCommitのマネジメントコンソール画面上にはそのような項目は見当たらなかったので、ローカル実行する場合だけの設定なのかな?
-e 環境変数の指定
CodeBuildで環境変数を設定したい場合、buildspec.ymlのenvシーケンスに直接記載するか、マネジメントコンソールからCodeBuildの定義を行う際に設定するなどの方法で設定できますが、ローカル実行する場合、マネジメントコンソールから環境変数を設定する代わりに、環境変数を記載したファイルを作成して、そのファイルをCodeBuild実行時に読み込ませることでビルド定義ファイル内で変数として使用できます。
変数の書き方は一般的なシェルの環境変数の指定方法で記載すればOKです。
codebuild_build.shで作られるDockerコンテナ削除
codebuild_build.shの仕様というよりDockerの仕様ですが、codebuild_build.shでCodeBuildをローカル実行するとローカルビルド時に使用した終了済みのコンテナが残ってしまいリソースを圧迫してしまいます。(以下の場合NAMESがagent-resources_build_1とagent-resources_agent_1以外のコンテナが実行後の不要コンテナ)
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
50d3da581ad7 public.ecr.aws/codebuild/amazonlinux2-x86_64-standard:4.0 "sh -c 'while [ ! -f…" 3 minutes ago Exited (137) 3 minutes ago agent-resources_build_1
c9968c8c4740 public.ecr.aws/codebuild/local-builds:aarch64 "local_build.sh" 3 minutes ago Exited (0) 3 minutes ago agent-resources_agent_1
fe6fb6edb995 public.ecr.aws/codebuild/local-builds:aarch64 "local_build.sh" 3 minutes ago Exited (0) 3 minutes ago stoic_cartwright
1f3947824af4 public.ecr.aws/codebuild/local-builds:aarch64 "local_build.sh" 3 minutes ago Exited (0) 3 minutes ago sad_wilbur
5a2aefe94637 public.ecr.aws/codebuild/local-builds:aarch64 "local_build.sh" 4 minutes ago Exited (0) 3 minutes ago jovial_lichterman
4f95a2ce88f4 public.ecr.aws/codebuild/local-builds:aarch64 "local_build.sh" 4 minutes ago Exited (0) 4 minutes ago infallible_hellman
795095d32638 public.ecr.aws/codebuild/local-builds:aarch64 "local_build.sh" 4 minutes ago Exited (0) 4 minutes ago sleepy_cori
6d6acece1148 public.ecr.aws/codebuild/local-builds:aarch64 "local_build.sh" 4 minutes ago Exited (0) 4 minutes ago intelligent_williamson
そもそもcodebuild_build.shを使う目的としてCodeBuildの動作を確認するためであり、再利用する必要も無いことからcodebuild_build.shの中身を以下のように少し編集して、実行完了後に不要となるコンテナが削除されるようにしたほうが良いでしょう。
docker_command="docker run -it "
↓↓以下に変更↓↓
docker_command="docker run --rm -it "
(おまけ)オプションもりもりの実行
オプション動作が分かりづらいものもあるため、実行例かねてオプションもりもりで実行してみようと思います。
以下のような想定で実施します。
| 設定 | 説明 |
|---|---|
| アーティファクトディレクトリ | codebuild/artifact |
| 1つ目のソースディレクトリ | codebuild/sourcedir1 |
| 2つ目のソースディレクトリ | codebuild/sourcedir2 |
| 識別子 | IDENTIFIER |
| AWS認証情報プロファイル | codebuild |
| ソースディレクトリマウント | する |
| ビルド定義ファイル名 | codebuild.yml |
| 環境変数ファイル名 | variable.env |
VAR="variable_test"
version: 0.2
phases:
install:
commands:
- echo ${VAR}
- ls -l ${CODEBUILD_SRC_DIR}/
- ls -l ${CODEBUILD_SRC_DIR_IDENTIFIER}/
- aws s3 ls
artifacts:
files:
- '**/*'
name: codebuild/artifact
~/
├── codebuild/
│ ├── artifact/
│ ├── sourcedir1/
│ │ ├── codebuild.yml
│ │ └── testfile1
│ └── sourcedir2/
│ └── testfile2
├── codebuild_build.sh
└── variable.env
ビルド定義ファイルはDockerでの動作を確認するため、以下のコマンドを実行します。
| コマンド | 用途 |
|---|---|
| echo ${VAR} | 環境変数呼び出し確認 |
| ls -l ${CODEBUILD_SRC_DIR}/ | 1つ目のソースディレクトリ変数が呼び出せるかの確認 |
| ls -l ${CODEBUILD_SRC_DIR_IDENTIFIER}/ | 2つ目のソースディレクトリ変数が呼び出せるかの確認 |
| aws s3 ls | aws-cliでAWS環境にアクセスできるかの確認 |
2つ目のソースディレクトリを指定している変数は実行時に指定する識別子によって変わるので環境によって変更してください。
また、aws-cliは、実行マシン側のIAMユーザ情報を使ってアクセスするため、もしアクセスできなかった場合は権限周りを確認しましょう。
./codebuild_build.sh -c -m \
-i public.ecr.aws/codebuild/amazonlinux2-x86_64-standard:4.0 \
-a codebuild/artifact \
-l public.ecr.aws/codebuild/local-builds:aarch64 \
-e variable.env \
-p codebuild \
-s codebuild/sourcedir1 \
-s IDENTIFIER:codebuild/sourcedir2 \
-b codebuild/sourcedir1/codebuild.yml
オプションもりもりのコマンド実行例(展開してください)
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] 2022/09/02 13:05:05 Waiting for agent ping
agent_1 | [Container] 2022/09/02 13:05:06 Waiting for DOWNLOAD_SOURCE
agent_1 | [Container] 2022/09/02 13:05:07 Phase is DOWNLOAD_SOURCE
agent_1 | [Container] 2022/09/02 13:05:07 CODEBUILD_SRC_DIR=/codebuild/output/src041/src
agent_1 | [Container] 2022/09/02 13:05:07 CODEBUILD_SRC_DIR_IDENTIFIER=/codebuild/output/src041/secSrc/IDENTIFIER
agent_1 | [Container] 2022/09/02 13:05:07 YAML location is /codebuild/output/srcDownload/src/codebuild.yml
agent_1 | [Container] 2022/09/02 13:05:07 Processing environment variables
agent_1 | [Container] 2022/09/02 13:05:08 Moving to directory /codebuild/output/src041/src
agent_1 | [Container] 2022/09/02 13:05:08 Registering with agent
agent_1 | [Container] 2022/09/02 13:05:08 Phases found in YAML: 1
agent_1 | [Container] 2022/09/02 13:05:08 INSTALL: 4 commands
agent_1 | [Container] 2022/09/02 13:05:08 Phase complete: DOWNLOAD_SOURCE State: SUCCEEDED
agent_1 | [Container] 2022/09/02 13:05:08 Phase context status code: Message:
agent_1 | [Container] 2022/09/02 13:05:08 Entering phase INSTALL
agent_1 | [Container] 2022/09/02 13:05:08 Running command echo ${VAR}
agent_1 | "variable_test"
agent_1 |
agent_1 | [Container] 2022/09/02 13:05:08 Running command ls -l ${CODEBUILD_SRC_DIR}/
agent_1 | total 4
agent_1 | -rw-r--r-- 1 root root 228 Sep 2 12:59 codebuild.yml
agent_1 | -rw-r--r-- 1 root root 0 Sep 1 13:19 testfile1
agent_1 |
agent_1 | [Container] 2022/09/02 13:05:08 Running command ls -l ${CODEBUILD_SRC_DIR_IDENTIFIER}/
agent_1 | total 0
agent_1 | -rw-r--r-- 1 root root 0 Sep 1 13:19 testfile2
agent_1 |
agent_1 | [Container] 2022/09/02 13:05:08 Running command aws s3 ls
agent_1 | 2022-08-26 12:31:35 [s3バケット名]
agent_1 | 2022-07-23 04:28:16 [s3バケット名]
agent_1 | 2022-07-24 23:58:28 [s3バケット名]
agent_1 |
agent_1 | [Container] 2022/09/02 13:05:12 Phase complete: INSTALL State: SUCCEEDED
agent_1 | [Container] 2022/09/02 13:05:12 Phase context status code: Message:
agent_1 | [Container] 2022/09/02 13:05:12 Entering phase PRE_BUILD
agent_1 | [Container] 2022/09/02 13:05:12 Phase complete: PRE_BUILD State: SUCCEEDED
agent_1 | [Container] 2022/09/02 13:05:12 Phase context status code: Message:
agent_1 | [Container] 2022/09/02 13:05:12 Entering phase BUILD
agent_1 | [Container] 2022/09/02 13:05:12 Phase complete: BUILD State: SUCCEEDED
agent_1 | [Container] 2022/09/02 13:05:12 Phase context status code: Message:
agent_1 | [Container] 2022/09/02 13:05:12 Entering phase POST_BUILD
agent_1 | [Container] 2022/09/02 13:05:12 Phase complete: POST_BUILD State: SUCCEEDED
agent_1 | [Container] 2022/09/02 13:05:12 Phase context status code: Message:
agent_1 | [Container] 2022/09/02 13:05:12 Expanding base directory path: .
agent_1 | [Container] 2022/09/02 13:05:12 Assembling file list
agent_1 | [Container] 2022/09/02 13:05:12 Expanding .
agent_1 | [Container] 2022/09/02 13:05:12 Expanding file paths for base directory .
agent_1 | [Container] 2022/09/02 13:05:12 Assembling file list
agent_1 | [Container] 2022/09/02 13:05:12 Expanding **/*
agent_1 | [Container] 2022/09/02 13:05:13 Found 2 file(s)
agent_1 | [Container] 2022/09/02 13:05:13 Preparing to copy secondary artifacts
agent_1 | [Container] 2022/09/02 13:05:13 No secondary artifacts defined in buildspec
agent_1 | [Container] 2022/09/02 13:05:13 Phase complete: UPLOAD_ARTIFACTS State: SUCCEEDED
agent_1 | [Container] 2022/09/02 13:05:13 Phase context status code: Message:
agent-resources_build_1 exited with code 0
Aborting on container exit...
S3バケット名だけ一部マスク。
コマンド実行完了後、codebuild/artifactにソースディレクトリが固められたZIPファイルも存在確認できました。
おわりに
CodeBuildをマネジメントコンソール上で実行する場合、単純な内容のビルドでも数分かかりますが、ローカルで実行した場合、いくつか制約はあるにせよ、マネジメントコンソールから実行するより早く確認できるので、buildspec.yml自体の動作チェックを行うのに最適です。
また、codebuild_build.shはオプションさえ覚えてしまえば非常に簡単にローカルでCodeBuildが試せることがわかりました。
CodeBuildはあまり触ったことがなかったのですが、実際にローカルで動かしてみて、CodeBuild自体、かなり自由度が高いサービスなのだと感じました。