TTYF ~earlgrey の雑記~

主に自分用メモとか

CodeBuild をローカルで動作確認する(Windows/Mac 対応版)

AWS CodeBuild では buildspec.yml という YAML ファイル(詳細はこちら)にビルドの詳細を定義しますが、これを用いて、 AWS 上でなくてもローカル環境でビルドを行うことができます。そのため、ローカルで十分に動作確認を行ってから AWS 上で本稼働させることができます。今回はこの方法を解説していきます。

Docker のインストール

CodeBuild は Docker コンテナ上でビルドを行いますので、ローカルで動かすのにも Docker を使います。なので、Docker が入っていなければインストールします。

WindowsMac では、無料で使用できる Docker の動作環境として、Docker Desktop と Docker Toolbox の 2 種類が存在しています。ダウンロードはこちら。

Docker Toolbox

Releases · docker/toolbox · GitHub

Desktop の方が新しく、Hyper-V や Hypervisor.framework といった OS 標準のハイパーバイザを使用しておりパフォーマンスもおそらく高いですが(一方 Toolbox は VirtualBox ベースです)、動作環境も少し厳しめになっています。一例を挙げると、Windows 版は Windows 10 64bit Pro 以上、Mac 版は macOS Sierra 以上、といった感じです。詳しくはそれぞれ以下の「System Requirements」をご覧ください。

Install Docker Desktop on Windows | Docker Documentation
Install Docker Desktop on Mac | Docker Documentation

もうひとつの注意点として、Docker Desktop は無償ではありますが、メールアドレスによるアカウント取得とそれによるログインが必要です。これについては、かつて GitHub の Issue にログインなしで使わせてほしい、という要望が挙がったものの却下されています。ちなみに以下がその Issue ですが、めちゃ炎上してます。Thumbs Down 4,600 超なんてそうそうお目にかかれない...!

Download Docker CE without logging in · Issue #6910 · docker/docker.github.io · GitHub

話が逸れましたが、Windows/Mac で Desktop の動作環境を満たしていなかったり、アカウント取得が面倒だったりする場合は Toolbox を使うことになります。詳しくはこちら。必要な動作環境はそれぞれ以下の「Step 1: Check your version」をご覧ください。Desktop よりは緩くなっています。

Install Docker Toolbox on Windows | Docker Documentation
Install Docker Toolbox on macOS | Docker Documentation

Toolbox を使う場合、いくつか注意点があります。

Windows の場合は、Hyper-V をオフにした方が無難です。VirtualBoxHyper-V と競合するためで、VirtualBox 6.0 からは一応共存可能になったようですが、Docker Toolbox にバンドルされているのは現時点で 5.2.20 です。Docker Toolbox よりも先に 6.0 をインストール、もしくは後から 6.0 に上げることももちろんできますが、少し手間がかかるのと、あとは環境によっては 6.0 でも結局 Hyper-V と共存できないことがあるようです。以下参考です。

VirtualBox 6.0 から Hyper-V と共存できるはず - Qiita

Mac の場合、Sierra/High Sierra/Mojave では、標準のインストーラが動作しません。代わりに Homebrew Cask でインストールします。詳細は省略しますが、このあたりを参考にしてください。

macOSのSierraではDocker Toolboxのインストールが出来ない - Qiita
macOS Sierra Docker Toolbox のインストーラがエラるので Brew cask でインストールした - Qiita

最後に Linux ですが、Linux は Desktop/Toolbox のような区別はありません。基本的にディストリビューション標準のパッケージマネージャでインストールします。詳しくはこちらを。

Get Docker Engine - Community for CentOS | Docker Documentation
Get Docker Engine - Community for Debian | Docker Documentation
Get Docker Engine - Community for Fedora | Docker Documentation
Get Docker Engine - Community for Ubuntu | Docker Documentation

AWS CodeBuild の Docker イメージ

CodeBuild をローカル実行するために必要なものは GitHub で公開されています。

github.com

上のリポジトリを次のコマンドでクローンする(GitHub アカウントが必要)か、ZIP でダウンロードして展開します。

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

このリポジトリには次のものが含まれています。

  • ビルドを実行する Docker コンテナのイメージをビルドするための定義ファイル(Dockerfile)
  • Docker コンテナを起動し、ビルドを実行するためのサポートスクリプト

Docker でビルドを実行するコンテナの元になるイメージファイルは含まれておらず、それは Dockerfile から自前でビルドする必要があります。
ここでビルドという言葉が2回出てきてややこしいですが、それぞれが指すビルドの成果物は、前者のビルドがアプリケーション、後者のビルドが Docker のイメージファイルです。この先は便宜上前者をビルド、後者を構築というふうに使い分けていきます。

さらに、CodeBuild をローカルで動かすには、ビルドを実行するコンテナのほかに、CodeBuild エージェントというコンテナが必要です。
ホスト OS の Docker 上でいきなりビルド用のコンテナを実行するわけでなく、まずは CodeBuild エージェントのコンテナを実行、CodeBuild エージェントはさらにビルド用のコンテナを起動し、それをリモート操作する、という多段かつ並列的な構成で動作します(このような手法を Docker outside of Docker というらしいです)。こうしている理由や利点は不勉強なのでよくわかりませんが、CI 環境ではわりとよく使われるらしいです。

というわけで、必要な Docker イメージは以下の 2 つになります。

  • 実際にビルドを行うコンテナのイメージ
  • CodeBuild エージェントのイメージ

それぞれ、準備する方法を説明していきます。

ビルドを行うコンテナのイメージ構築

ビルドを実行するコンテナのイメージは、前述のように Dockerfile の定義に従って構築します。

構築対象のイメージ選択

まずはどのような Docker イメージを構築するのか決める必要があります。既に作成されている CodeBuild のプロジェクトがあって、それをローカルでビルドしたいのであれば、プロジェクトで使用しているイメージをコンソールなどで確認して、それと同じものにします。以下の例だと、「aws/codebuild/standard:2.0」がイメージの種類を表しています。

f:id:s_earlgrey:20190812201136p:plain

これから新たに CodeBuild プロジェクトを作成するのであれば、基本的に「aws/codebuild/standard:2.0」または「aws/codebuild/amazonlinux2-x86_64-standard:1.0」にしておけば良いでしょう(長いので、以降は適宜 aws/codebuild/ を略します)。これらには .NET Core 2.2、Python 3.7 など複数の処理系のビルド/実行環境が予め含まれています。処理系の完全なリストはこちらにあります。(※言語が日本語で開いた場合はリストが表示されませんので、English に切り替えてください。)

Docker Images Provided by CodeBuild - AWS CodeBuild

「standard:2.0」」も「amazonlinux2-x86_64-standard:1.0」も含まれる処理系はほぼ同じなのですが、唯一 Java だけが、前者は オリジナルの OpenJDK、後者は Amazon Corret というように異なっています。あとは OS が前者は Ubuntu 18.04、後者は Amazon Linux 2 です。
このあたりは要件に合わせてイメージを選択してください。あくまでビルド環境ですので、OS の違いには特にシビアにならなくても良いと思います。

構築するイメージを決めたら、先ほどクローン or ダウンロードしたファイルから、目的のイメージの Dockerfile があるディレクトリを探します。「standard:2.0」の場合は ubuntu/standard/2.0、「amazonlinux2-x86_64-standard:1.0」の場合は al2/x86_64/standard/1.0 です。
それ以外で、「aws/codebuild/dot-net:core-2.1」などのように名前に処理系が含まれているイメージのものは、ubuntu/unsupported_images 配下にあります。もし既存の CodeBuild プロジェクトで使用しているのとまったく同じバージョンが見当たらない場合は、用意されている中でひとつ次のバージョンを使うか、いっそ既存プロジェクトの buildspec.yml を「standard:2.0」や「amazonlinux2-x86_64-standard:1.0」用に移行できないか検討してみるのも良いでしょう。

イメージの構築

この先はいくつか docker コマンドを使用します。それにあたって、環境に応じて以下の前提事項がありますので、これに従ってください。

  • Docker Toolbox を使用している場合、Docker Quickstart Terminal で起動したターミナルを用いる
  • Linux を使用している場合、sudo で実行する

イメージの構築は、次のコマンドで行います。

$ (sudo) docker build -t [任意の名称:タグ] [Dockerfile のあるディレクトリのパス]

-t オプションに続ける名称は何でも良いですが、CodeBuild 用イメージ種類の識別子、つまり「aws/codebuild/standard:2.0」などをそのまま使うのがわかりやすいでしょう。

イメージ構築にはそれなりに時間がかかります。「standard:2.0」であれば、CPU パワーやネットワークの帯域にもよりますが 30 分~ 1 時間半程度です。
出力は大量にあるので省略しますが、完了するとこのようなメッセージが表示されます。

Successfully built ffcd726be2dd
Successfully tagged aws/codebuild/standard:2.0
SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.

最後の SECURITY WARNING は、Windows で実行した場合に表示されますが、結論から言うと無視して良いです。
Dockerfile の中で、COPY や ADD という、Docker のホスト OS から構築中の Docker イメージにファイルをコピーするコマンドが使えるのですが、これでコピーされたファイルのパーミッションが Docker イメージの中では一律 '-rwxr-xr-x' にセットされているので、これが過剰であれば訂正するように、ということを言っています。
システムの本番環境として使うイメージであればそのあたりは厳密に取り扱うべきですが、これはあくまでローカルでアプリケーションのビルドを行うためのイメージですので、特に気にする必要もないでしょう。
余談ですが、Dockerfile で COPY や ADD を使っていなくても、構築に使用した環境が Windows であればこのメッセージが出ます。

さて、実際に構築されたイメージを見てみましょう。

$ (sudo) docker images
REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
aws/codebuild/standard   2.0                 ffcd726be2dd        4 minutes ago       7.36GB
ubuntu                   18.04               4c108a37151f        2 weeks ago         64.2MB

aws/codebuild/standard 2.0」というのが今回構築したイメージです。その下にある「ubuntu 18.04」は、今回構築したもののベースになっているイメージで、構築の最初のステップで自動的にダウンロードされたものです。

CodeBuild エージェントのダウンロード

CodeBuild エージェントは、出来合いのものをダウンロードしてくるだけです。次のコマンドでダウンロードします。

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

もう一度イメージのリストを確認してみましょう。「amazon/aws-codebuild-local latest」が追加されていますね。

$ (sudo) docker images
REPOSITORY                   TAG                 IMAGE ID            CREATED             SIZE
aws/codebuild/standard       2.0                 ffcd726be2dd        8 minutes ago       7.36GB
amazon/aws-codebuild-local   latest              b0bdf3d66f0e        7 weeks ago         563MB
ubuntu                       18.04               4c108a37151f        2 weeks ago         64.2MB

ビルドの実行

ここまでで準備が整いました。CodeBuild をローカルで実行するためには、GitHub からクローン or ダウンロードしてきたリポジトリの local_builds/codebuild_build.sh を使います。このスクリプト内部で docker コマンドを使用しているので、ここでも Toolbox の場合は Docker Quickstart Terminal を使い、Linux の場合は sudo してください。
基本的なコマンドは以下の通りです。

$ (sudo) codebuild_build.sh -i [docker イメージの名称:タグ] -a [ビルド結果出力先のディレクトリ] -s [ソースコードのあるディレクトリ] (-b [buidspec のパス])

-b オプションは省略可能で、その場合は -s オプションで指定したディレクトリ直下の buildspec.yml が使用されます。ほかにもいろいろオプションがありますが、詳しくはリポジトリの local_builds/README.md をご覧ください。

WindowsMac の場合、-a, -s, -b の各オプションには、以下の表にあるディレクトリ配下を指定してください。これ以外はデフォルト状態ではビルドコンテナがディレクトリやファイルを認識できません。然るべき設定をすればその他のディレクトリも使えますが、デフォルトでも特に困ることはないと思いますので、その方法は省略します。

OS Docker 使用可能ディレクト
Windows Desktop C:\ 配下
Windows Toolbox C:\Users 配下
OS X/macOS Desktop /Users または /Volumes 配下
macOS Toolbox /Users 配下

Windows の場合、さらに注意点があります。Desktop と Toolbox で実行方法が異なりますので、別々に書いていきます。

Windows + Docker Desktop の場合

codebuild_build.sh は Bash スクリプトなので、Windows では別途 Bash の実行環境を導入する必要があります。Git for Windows に付属の Git Bash を使うのがお手軽で良いと思いますので、その前提で書きます。WSL や Cygwin など他の Bash 環境の場合は未検証です。

  • ディレクトリのパス指定方法が、通常の Windows の記述と異なります。例えば C:\Users\username\source であれば、/c/Users/username/source になります。
  • コマンドを実行すると、いきなりエラーになります。ですが、最終的に実行される docker コマンドが出力されるので、その先頭に "winpty" を付けて実行してください。

実際に Git Bash で実行すると、こんなふうに出力されます。

$ ./codebuild_build.sh -i aws/codebuild/standard:2.0 -a /c/Users/username/artifacts -s /c/Users/username/source
Build Command:

docker run -it -v //var/run/docker.sock:/var/run/docker.sock -e "IMAGE_NAME=aws/codebuild/standard:2.0" -e "ARTIFACTS=//C/Users/username/artifacts" -e "SOURCE=//C/Users/username/source" -e "INITIATOR=username" amazon/aws-codebuild-local:latest

the input device is not a TTY.  If you are using mintty, try prefixing the command with 'winpty'

ここから "docker run ..." をコピーして、改めてこのように入力します。これでビルドが実行できます。

$ winpty docker run -it -v //var/run/docker.sock:/var/run/docker.sock -e "IMAGE_NAME=aws/codebuild/standard:2.0" -e "ARTIFACTS=//C/Users/username/artifacts" -e "SOURCE=//C/Users/username/source" -e "INITIATOR=username" amazon/aws-codebuild-local:latest

Windows + Docker Toolbox の場合

Windows + Docker Toolbox だと、私が試した限りではビルドコンテナに Windows 側のディレクトリを認識させること自体ができません。仕方ないので、VirtualBox 上で動いている、Docker コンテナをホストしている Linux にログインして、そこでスクリプトを実行します。まずは Docker Quickstart Terminal で以下のコマンドを実行してください。

$ docker-machine ssh default

こんな風に表示がされればOKです。コンテナホストの Linux に入れました。

   ( '>')
  /) TC (\   Core is distributed with ABSOLUTELY NO WARRANTY.
 (/-_--_-\)           www.tinycorelinux.net

docker@default:~$

続けて codebuild_build.sh を実行してください。Linux では、Windows の C:\Users が /C/Users にマウントされていますので、その前提で各オプションのディレクトリを指定してください(codebuild_build.sh 自体も C:\Users 配下においておく必要があります)。

ビルドのサンプル

では実際にビルドしてみます。サンプルとしては、.NET Core で new しただけのコンソールアプリケーションを使います。

using System;

namespace hello
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.2</TargetFramework>
  </PropertyGroup>

</Project>

buildspec はこんな感じで。

version: 0.2
phases:
  install:
    runtime-versions:
      dotnet: 2.2
  build:
    commands:
      - dotnet publish -c Release
artifacts:
  files:
    - '**/*'
  base-directory: bin/Release/netcoreapp2.2/publish

これらをビルドします。ちなみに Windows + Docker Desktop で実行しています。

$ ./codebuild_build.sh -i aws/codebuild/standard:2.0 -a /C/codebuildtest/artifacts -s /C/codebuildtest 

Build Command:

docker run -it -v //var/run/docker.sock:/var/run/docker.sock -e "IMAGE_NAME=aws/codebuild/standard:2.0" -e "ARTIFACTS=//Ccodebuildtestartifacts" -e "SOURCE=//Ccodebuildtest" -e "INITIATOR=username" amazon/aws-codebuild-local:latest

the input device is not a TTY.  If you are using mintty, try prefixing the command with 'winpty'

$ winpty docker run -it -v //var/run/docker.sock:/var/run/docker.sock -e "IMAGE_NAME=aws/codebuild/standard:2.0" -e "ARTIFACTS=//C/codebuildtest/artifacts" -e "SOURCE=//C/codebuildtest" -e "INITIATOR=username" amazon/aws-codebuild-local:latest

...
agent_1  | [Container] 2019/09/01 13:55:33 Phase context status code:  Message:
agent-resources_build_1 exited with code 0
Aborting on container exit...

最後に Aborting とか出ていますが、その前に exited with code 0 と出ていれば問題ありません。出力先に指定したディレクトリを見てみましょう。

f:id:s_earlgrey:20190901231410p:plain


それらしいファイルができていますね。これを展開してみます。

f:id:s_earlgrey:20190901231446p:plain


確かにビルドされています。さらに実行できるか確認します。

C:\codebuildtest\artifacts\artifacts>dotnet hello.dll
Hello World!

ちゃんと実行できますね!ビルドは問題なく成功しました。