ちょっと話題の記事

CodeBuild をローカル環境で実行(テスト、デバッグ)できるようになりました

CodeBuild の処理内容を記載した buildspec ファイルをローカルで動作確認できるようになりました。簡単な説明と手順をまとめてみました。
2018.05.09

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

CodeBuild の処理をローカルで動作確認できるようになりました。今まで buildspec.yml を CodeBuild の実環境でトライ&エラーで修正した経験がある方もいらっしゃるのではないでしょうか。ローカルで実行できることでテスト実行や、記述に問題があった時のデバッグを簡単に行えます。

概要

AWS CodeBuild はビルド実行サービスです。アプリケーションのテストやビルド、必要に応じてビルドしたアプリケーションを S3 へアップロードすることができます。CodeBuild の処理内容は YAML ファイル(デフォルトはbuildspec.yml)に記述した内容に従って実行されます。(以降、このファイルをbuildspec.ymlを記載します)

今まで buildspec.yml は CodeBuild で実際に動かしてみるまで想定した動作をするのか分かりませんでした。また想定した動作をしない時に CloudWatch Logs にある程度のログ出力はされますが、状況によってはなぜ失敗したのかわからず、トライ&エラーを繰り返して解決するようなケースがありました。

今回のアップデートは CodeBuild をローカル環境で動作させることができる Docker Image が公開されました。DockerHub の Amazon のリポジトリで公開されています。

この Image を利用することでローカルで起動したコンテナ内で開発中のアプリケーションを buildspec.yml の内容に従って、テスト、ビルド、成果物の作成などを実行できます。つまり buildspec.yml の初回作成時や修正を実際に CodeBuild で動かす前にローカル環境でテスト、デバッグできることになります。

それでは早速試してみましょう。

試してみた

Python のウェブアプリケーションフレームワークの Django を使ったウェブアプリケーションを例に試してみます。

Django アプリケーションを本番環境(今回は EC2 とします)にデプロイする前に CodeBuild でテストして、テストに成功したら S3 にアーカイブファイルをアップロードするとします。もう少し具体的に書くと、依存ライブラリをインストール、テスト、必要ファイルのみを成果物としてアーカイブ、アップロードします。buildspec.yml を書くと以下のような感じでしょうか。

buildspec.yml
version: 0.2

phases:
  install:
    commands:
      - pip install -r requirements.txt
  build:
    commands:
      - python manage.py test
artifacts:
  files:
    - app/*
    - project/*
    - manage.py
    - requirements.txt

ローカル実行するには以下のような手順が必要です。

  • ビルド用の Docker イメージ作成
  • ビルド実行用の イメージのダウンロード
  • ローカルでのビルド実行

実行環境

  • OS : macOS High Sierra (v10.13.3)
  • Docker : Docker E18.04.0-ce-mac62

ビルド用の Docker イメージ作成

buildspec.yml に従って処理を実行するコンテナのイメージを作成します。CodeBuild と同じように OS / Runtime / Version の組み合わせのイメージを作成する必要があります。イメージはパブリックなレジストリで公開されていませんが、イメージを作成する Dockerfile は GitHub リポジトリで提供されています。Dockerfile を取得します。

$ git clone https://github.com/aws/aws-codebuild-docker-images.git

Dockerfile は CodeBuild と同じように OS / Runtime / Version の組み合わせの種類があります。

$ find aws-codebuild-docker-images -name Dockerfile
aws-codebuild-docker-images/ubuntu/nodejs/6.3.1/Dockerfile
aws-codebuild-docker-images/ubuntu/nodejs/7.0.0/Dockerfile
aws-codebuild-docker-images/ubuntu/nodejs/4.4.7/Dockerfile
aws-codebuild-docker-images/ubuntu/nodejs/4.3.2/Dockerfile
aws-codebuild-docker-images/ubuntu/nodejs/8.11.0/Dockerfile
aws-codebuild-docker-images/ubuntu/nodejs/5.12.0/Dockerfile
aws-codebuild-docker-images/ubuntu/docker/1.12.1/Dockerfile
aws-codebuild-docker-images/ubuntu/docker/17.09.0/Dockerfile
aws-codebuild-docker-images/ubuntu/ubuntu-base/14.04/Dockerfile
aws-codebuild-docker-images/ubuntu/python/3.3.6/Dockerfile
aws-codebuild-docker-images/ubuntu/python/3.5.2/Dockerfile
aws-codebuild-docker-images/ubuntu/python/2.7.12/Dockerfile
aws-codebuild-docker-images/ubuntu/python/3.4.5/Dockerfile
aws-codebuild-docker-images/ubuntu/golang/1.5.4/Dockerfile
aws-codebuild-docker-images/ubuntu/golang/1.6.3/Dockerfile
aws-codebuild-docker-images/ubuntu/golang/1.7.3/Dockerfile
aws-codebuild-docker-images/ubuntu/golang/1.10/Dockerfile
aws-codebuild-docker-images/ubuntu/java/openjdk-8/Dockerfile
aws-codebuild-docker-images/ubuntu/java/openjdk-6/Dockerfile
aws-codebuild-docker-images/ubuntu/java/openjdk-7/Dockerfile
aws-codebuild-docker-images/ubuntu/java/openjdk-9/Dockerfile
aws-codebuild-docker-images/ubuntu/php/5.6/Dockerfile
aws-codebuild-docker-images/ubuntu/php/7.0/Dockerfile
aws-codebuild-docker-images/ubuntu/dot-net/core-1/Dockerfile
aws-codebuild-docker-images/ubuntu/dot-net/core-2/Dockerfile
aws-codebuild-docker-images/ubuntu/android-java-7/24.4.1/Dockerfile
aws-codebuild-docker-images/ubuntu/ruby/2.3.1/Dockerfile
aws-codebuild-docker-images/ubuntu/ruby/2.2.5/Dockerfile
aws-codebuild-docker-images/ubuntu/ruby/2.5.1/Dockerfile
aws-codebuild-docker-images/ubuntu/ruby/2.1.10/Dockerfile
aws-codebuild-docker-images/ubuntu/android-java-8/24.4.1/Dockerfile
aws-codebuild-docker-images/ubuntu/android-java-8/26.1.1/Dockerfile
aws-codebuild-docker-images/ubuntu/android-java-6/24.4.1/Dockerfile

CodeBuild に設定したものと同じものを選びましょう。今回は Python 3 の最新版、Python 3.5.2 のイメージを作成します。なお、イメージを作成する前に Dockerfile の ENTRYPOINT を削除、もしくはコメントアウトする必要があります。

$ cd aws-codebuild-docker-images/ubuntu/python/3.5.2/
$ sed -ie 's/^ENTRYPOINT/#ENTRYPOINT/g' Dockerfile
$ docker build -t aws/codebuild/python:3.5.2 .

ビルドは結構時間かかります。これでイメージの作成は完了です。

ビルド実行用のイメージのダウンロード

ビルド用のコンテナを操作するエージェントがインストールされたイメージをダウンロードします。

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

ローカルでのビルド実行

ローカルでビルドを実行します。docker runコマンドを実行するだけです。実行する際は三つの環境変数の指定が必要です。

  • IMAGE_NAME : ビルドしたイメージの名前
  • ARTIFACTS : アーカイブファイルの出力先ディレクトリ
  • SOURCE : ビルドするアプリケーションのディレクトリ
docker run \
  -it -v /var/run/docker.sock:/var/run/docker.sock \
  -e "IMAGE_NAME=aws/codebuild/python:3.5.2" \
  -e "ARTIFACTS=/tmp" \
  -e "SOURCE=/Users/fujimoto.shinji/python/django-rest" \
  amazon/aws-codebuild-local

Removing agentresources_build_1 ... done
Removing agentresources_agent_1 ... done
Removing network agentresources_default
Removing volume agentresources_user_volume
Removing volume agentresources_source_volume
Creating network "agentresources_default" with the default driver
Creating volume "agentresources_user_volume" with local driver
Creating volume "agentresources_source_volume" with local driver
Creating agentresources_agent_1 ...
Creating agentresources_agent_1 ... done
Creating agentresources_build_1 ...
Creating agentresources_build_1 ... done
Attaching to agentresources_agent_1, agentresources_build_1
build_1  | [Container] 2018/05/09 06:53:05 Waiting for agent ping
build_1  | [Container] 2018/05/09 06:53:05 Waiting for DOWNLOAD_SOURCE
build_1  | [Container] 2018/05/09 06:53:32 Phase is DOWNLOAD_SOURCE
build_1  | [Container] 2018/05/09 06:53:34 CODEBUILD_SRC_DIR=/codebuild/output/src221564627/src
build_1  | [Container] 2018/05/09 06:53:34 YAML location is /codebuild/output/src221564627/src/buildspec.yml
build_1  | [Container] 2018/05/09 06:53:34 Processing environment variables
build_1  | [Container] 2018/05/09 06:53:34 Moving to directory /codebuild/output/src221564627/src
build_1  | [Container] 2018/05/09 06:53:34 Registering with agent
build_1  | [Container] 2018/05/09 06:53:34 Phases found in YAML: 2
build_1  | [Container] 2018/05/09 06:53:34  INSTALL: 1 commands
build_1  | [Container] 2018/05/09 06:53:34  BUILD: 1 commands
build_1  | [Container] 2018/05/09 06:53:34 [INSTALL BUILD UPLOAD_ARTIFACTS]
build_1  | [Container] 2018/05/09 06:53:34 Phase complete: DOWNLOAD_SOURCE Success: true
build_1  | [Container] 2018/05/09 06:53:34 Phase context status code:  Message:
build_1  | [Container] 2018/05/09 06:53:34 Entering phase INSTALL
build_1  | [Container] 2018/05/09 06:53:34 Running command pip install -r requirements.txt
build_1  | [Container] 2018/05/09 06:54:45 Phase complete: INSTALL Success: true
build_1  | [Container] 2018/05/09 06:54:45 Phase context status code:  Message:
build_1  | [Container] 2018/05/09 06:54:46 Entering phase PRE_BUILD
build_1  | [Container] 2018/05/09 06:54:46 Phase complete: PRE_BUILD Success: true
build_1  | [Container] 2018/05/09 06:54:46 Phase context status code:  Message:
build_1  | [Container] 2018/05/09 06:54:46 Entering phase BUILD
build_1  | [Container] 2018/05/09 06:54:46 Running command python manage.py test
build_1  | [Container] 2018/05/09 06:54:48 Phase complete: BUILD Success: true
build_1  | [Container] 2018/05/09 06:54:48 Phase context status code:  Message:
build_1  | [Container] 2018/05/09 06:54:48 Entering phase POST_BUILD
build_1  | [Container] 2018/05/09 06:54:48 Phase complete: POST_BUILD Success: true
build_1  | [Container] 2018/05/09 06:54:48 Phase context status code:  Message:
build_1  | [Container] 2018/05/09 06:54:48 Preparing to copy artifacts
build_1  | [Container] 2018/05/09 06:54:48 Expanding base directory path: .
build_1  | [Container] 2018/05/09 06:54:48 Assembling file list
build_1  | [Container] 2018/05/09 06:54:48 Expanding .
build_1  | [Container] 2018/05/09 06:54:48 Expanding artifact file paths for base directory .
build_1  | [Container] 2018/05/09 06:54:48 Assembling file list
build_1  | [Container] 2018/05/09 06:54:48 Expanding app/*
build_1  | [Container] 2018/05/09 06:54:48 Expanding project/*
build_1  | [Container] 2018/05/09 06:54:48 Expanding manage.py
build_1  | [Container] 2018/05/09 06:54:48 Expanding requiments.txt
build_1  | [Container] 2018/05/09 06:54:48 Skipping invalid artifact path requiments.txt
build_1  | [Container] 2018/05/09 06:54:48 Found 14 file(s)
build_1  | [Container] 2018/05/09 06:54:49 Phase complete: UPLOAD_ARTIFACTS Success: true
build_1  | [Container] 2018/05/09 06:54:49 Phase context status code:  Message:
agentresources_build_1 exited with code 0

ビルド用コンテナがステータスコード 0 で終了していれば問題なく処理に成功しています。成功しても実行用コンテナは停止しないので Ctrl+c で終了します。アーカイブファイルも確認してみましょう。

$ ls /tmp/
artifacts.zip
$ unzip -q /tmp/artifacts.zip
$ ls
app              manage.py        project          requirements.txt

指定されたディレクトリ、ファイルだけがアーカイブされていますね。

まとめ

いかがでしたでしょうか? ローカルに Docker 実行環境が整っていれば簡単に実行できました。