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)と比べてもサイズを大幅に削減することが簡単にできました。
また、多段での実行が可能なので、アプリをビルド後、テスト用のイメージでテストを実行し、その後に実行用のイメージを作成するようなこともできそうです。

元記事はこちら

CodeBuildでDockerイメージのマルチステージビルド