Dockerでマルチステージビルドという機能を知ったので検証がてらCodeBuildで試してみました。
マルチステージビルドとは、例えばjavaアプリケーションにおいて、ビルドについてはjdkが入ったイメージを利用してビルドを行い、ビルドされたバイナリだけをjreが入ったイメージにコピーしてDockerイメージを作成することをDockerイメージのビルド時にできる機能となります。こうすることで簡単に実行するDockerイメージを小さくすることが可能となります。この機能はDokcerの17.05以降で利用可能となっています。
検証内容
Javaアプリをmavenがインストールされているコンテナでビルドして、jreがインストールされているコンテナをベースにイメージを作成しECRにプッシュします。
構成イメージは以下になります。
ECRの作成
イメージプッシュ用のリポジトリを作成します。
AWSコンソールの「Elastic Container Service」→「リポジトリ」から「リポジトリの作成」をクリックし、リポジトリ名を入力して リポジトリを作成します。ここでは「multistage-ecr」という名前で作成しました。
CodeCommitの作成
AWSコンソールの「CodeCommit」→「リポジトリの作成」をクリックし、リポジトリ名を入力してリポジトリを作成ます。ここでは「multistage-test」という名前で作成しました。
ビルド用リソースの作成
以下のリソースを作成し、作成したCodeCommitのリポジトリにコミットしてください。
├── ./Dockerfile ├── ./buildspec.yml ├── ./pom.xml └── ./src └── ./src/hoge ├── ./src/hoge/Main.class └── ./src/hoge/Main.java
GitHub – cloudfish7/multi-stage-build-for-codebuildに一式配置していますのでここからもDLできます。
Dockerfile
# ビルド用コンテナでjavaをコンパイル。build1と名前を付けて後続で利用 FROM maven:3.3.9-jdk-8 AS build1 RUN mkdir -p /opt/java/src ADD ./pom.xml /opt/java/ ADD ./src /opt/java/src RUN cd /opt/java && mvn install # jreがインストールされたイメージにビルド用コンテナから作成したjarファイルをコピー FROM openjdk:8u131-jre-alpine RUN mkdir -p /opt/app/ COPY --from=build1 /opt/java/target/ /opt/app/ RUN java -jar /opt/app/HelloWorld-1.0.jar
buildspec.yml
dockerイメージをビルドしてECRにプッシュします。
以下をセットして
{REPO_NAME}にはECRのリポジトリ名
{tag}にはイメージタグをセット(何もなければlatest)
{account_id}にはAWSアカウントID
version: 0.1 phases: pre_build: commands: - $(aws ecr get-login --region ap-northeast-1 --no-include-email) build: commands: - docker build -t {REPO_NAME}:{tag} . - docker tag {REPO_NAME}:{tag} {account_id}.dkr.ecr.ap-northeast-1.amazonaws.com/{REPO_NAME}:{tag} - docker push {account_id}.dkr.ecr.ap-northeast-1.amazonaws.com/{REPO_NAME}:{tag} post_build: commands:
pom.xml
コンパイルしてjarファイルを作成します。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>hoge</groupId> <artifactId>HelloWorld</artifactId> <version>1.0</version> <name>Java Sample App</name> <build> <outputDirectory>target/classes</outputDirectory> <sourceDirectory>src</sourceDirectory> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <mainClass>hoge.Main</mainClass> </manifest> </archive> </configuration> </plugin> </plugins> </build> </project>
Main.java
package hoge; class Main{ public static void main(String args[]){ System.out.println("Hello Docker!!"); } }
CodeBuildの作成
AWSコンソールの「CodeBuild」→「プロジェクトの作成」をクリックします。
プロジェクト名:multistage-build
ソースプロバイダ:AWS CodeCommit
リポジトリ:multistage-test ← CodeCommitのリポジトリ
環境イメージ:AWS CodeBuildによって管理されたイメージの使用をチェック
オペレーティングシステム:Ubuntu
ランタイム:Docker
バージョン:aws/codebuild/docker:17.09.0
ビルド仕様:ソースコードのルートディレクトリのbuildspec.ymlを使用をチェック
上記以外はデフォルトのままとしてプロジェクトを作成します。
プロジェクト作成後に、AWSコンソールの「IAM」→「ロール」から作成されたCodeBuild用のサービスロールを選択し、以下のポリシーを追加します。
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "ecr:BatchCheckLayerAvailability", "ecr:CompleteLayerUpload", "ecr:GetAuthorizationToken", "ecr:InitiateLayerUpload", "ecr:PutImage", "ecr:UploadLayerPart" ], "Resource": "*", "Effect": "Allow" } ] }
実行確認
準備が整ったのでビルド実行してECRにプッシュされることを確認します。
AWSコンソールの「CodeBuild」→作成したプロジェクトを選択し、「ビルドの開始」をクリックします。
プロジェクト名:作成したプロジェクト名
ブランチ:master
他はデフォルとのままで「ビルドの開始」をクリックします。
ビルドが正常終了した場合、ステータスが「Succeeded」となります。
失敗した場合はステータスが「Failed」となりますので、詳細画面からビルドログを確認しエラー内容を確認してください。
正常にビルドが完了しjavaのビルドも正しく完了していると、ビルドログに以下のように出力されていると思います。ビルドイメージ作成時にJavaのアプリが正常にコンパイルされているかチェックするため実行しています。
まとめ
今回利用したjreのみのイメージだと約50MBとなり、openjdkのalpineイメージ(約100MB)と比べてもサイズを大幅に削減することが簡単にできました。
また、多段での実行が可能なので、アプリをビルド後、テスト用のイメージでテストを実行し、その後に実行用のイメージを作成するようなこともできそうです。