hero_picture
Cover Image for CodeBuildでマルチアーキテクチャなDockerイメージを高速にビルドする

CodeBuildでマルチアーキテクチャなDockerイメージを高速にビルドする

前置き

クラウドソリューション事業部の西村です

唐突ですが、Docker Buildxでマルチアーキテクチャのイメージをビルドする際遅いなぁと思われたことはありますでしょうか?

特にコンパイル処理はかなり遅いと感じるかと思います

今回はそのような悩みをCodeBuildのバッチビルドを利用して解決したいと思います

注意事項

今回の記事ではDockerの実験的機能を利用します

ですのでこの記事を閲覧されたタイミングによっては動作しなくなる恐れもあります

予めご了承ください

バッチビルドって?

簡単に言えば複数のビルドタスクを逐次実行できたり、並列に実行させたりできます

詳しい解説は以下のドキュメントをご覧ください

AWS CodeBuild でのバッチビルドhttps://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/batch-build.html

実装

今回はコンテナイメージをビルドし、プライベートECRへプッシュするような実装をします

各ファイルの作成

以下の4つのファイルを作成します

今回の記事ではこれらのファイルをCodeCommitに入れています

Dockerfile

まずビルドに利用する簡単なDockerfileを用意します

※このDockerfileに深い意味はありません

1FROM public.ecr.aws/docker/library/php:8.1-cli
2RUN docker-php-ext-install pdo_mysql

コンテナイメージビルドを行うbuildspec

続いてコンテナイメージのビルドを行うbuildspecを作成します

ファイル名はbuild_container.ymlとしています

こちらで実行する内容はECRへのログイン、コンテナイメージのビルドとプッシュとなります

1version: 0.2
2env:
3  variables:
4    IMAGE_HOST: ''
5    IMAGE_NAME: example
6    TAG_VERSION: latest
7    TAG_SUFFIX: ''
8    DOCKER_BUILDKIT: 1
9phases:
10  pre_build:
11    commands:
12      - aws ecr get-login-password | docker login --username AWS --password-stdin $IMAGE_HOST
13      - export IMAGE_TAG="${IMAGE_HOST}${IMAGE_HOST:+/}${IMAGE_NAME}:${TAG_VERSION:-latest}${TAG_SUFFIX:+-}${TAG_SUFFIX}"
14  build:
15    commands:
16      - docker build -t $IMAGE_TAG .
17
18  post_build:
19    commands:
20      - docker push $IMAGE_TAG

マニフェストリストを作成するbuildspec

マニフェストリストを作成するbuildspecを作成します

ファイル名はcreate_manifest.ymlとしています

こちらの実行にDockerの実験的機能を利用しています

docker manifesthttps://docs.docker.com/engine/reference/commandline/manifest/

1version: 0.2
2env:
3  variables:
4    IMAGE_HOST: ''
5    IMAGE_NAME: example
6    TAG_VERSION: latest
7phases:
8  pre_build:
9    commands:
10      - aws ecr get-login-password | docker login --username AWS --password-stdin $IMAGE_HOST
11      - export IMAGE_TAG="${IMAGE_HOST}${IMAGE_HOST:+/}${IMAGE_NAME}:${TAG_VERSION:-latest}"
12      - export IMAGE_TAG_AMD64="${IMAGE_HOST}${IMAGE_HOST:+/}${IMAGE_NAME}:${TAG_VERSION:-latest}-amd64"
13      - export IMAGE_TAG_ARM64="${IMAGE_HOST}${IMAGE_HOST:+/}${IMAGE_NAME}:${TAG_VERSION:-latest}-arm64"
14  build:
15    commands:
16      - docker manifest create $IMAGE_TAG $IMAGE_TAG_AMD64 $IMAGE_TAG_ARM64
17
18  post_build:
19    commands:
20      - docker manifest push $IMAGE_TAG

メインのbuildspecを作成する

メインのbuildspecを作成します

ファイル名はbuildspec.ymlとしています

コンテナビルドに利用するイメージはそれぞれのアーキテクチャのイメージを指定しておきます

1version: 0.2
2batch:
3  fast-fail: true
4  build-graph:
5    - identifier: build_amd64
6      buildspec: build_container.yml
7      env:
8        image: aws/codebuild/amazonlinux2-x86_64-standard:4.0
9        privileged-mode: true
10        type: LINUX_CONTAINER
11        variables:
12          TAG_SUFFIX: amd64
13    - identifier: build_arm64
14      buildspec: build_container.yml
15      env:
16        image: aws/codebuild/amazonlinux2-aarch64-standard:2.0
17        privileged-mode: true
18        type: ARM_CONTAINER
19        variables:
20          TAG_SUFFIX: arm64
21    - identifier: create_manifest
22      buildspec: create_manifest.yml
23      depend-on:
24        - build_amd64
25        - build_arm64

リポジトリの作成

続いてリポジトリを作成します

Amazon Elastic Container Registryの画面(https://console.aws.amazon.com/ecr/)を開き、”Create repository” を選択します

任意のリポジトリ名を設定し、 “Create repository” を押し作成します

作成後のリポジトリ名を覚えておくか、コピーしておいてください

CodeBuild プロジェクト作成

続いてCodeBuildのプロジェクトを作成します

CodeBuildの画面(https://console.aws.amazon.com/codesuite/codebuild/home)を開き、”Create build project” を選択します

Project configuration

Project nameに任意の名前を設定します

Source

ソースは先ほど作成したファイルを置いた場所を指定します

当記事ではCodeCommitを指定しています

Environment

イメージ関連はそれぞれ以下を選択していきます

  • Operating system: Amazon Linux 2
  • Image: aws/codebuld/amazonlinux2-aarch64-standard:2.0
  • Image version: Always use the latest image for this runtime version

サービスロールは新規で作成します

ロール名は自動的に入っているかと思います

ここでのロール名は後ほど利用するので覚えておいてください

また追加のオプションとして環境変数を指定しておきます

  • IMAGE_HOST: リポジトリ名のスラッシュより前の文字列
  • IMAGE_NAME: リポジトリ名のスラッシュより後の文字列

Buildspec

そのままで問題ありません

Batch configuration

こちらの設定を行う必要があるためチェックボックスにチェックを入れます

バッチサービスロールは新規で作成します

ロール名は何かしらを入力してください

Allowed compute type(s) for batchは無くても問題ないですが念のため “3 GB memory, 2 vCPUs” を指定しておきます

※この項目はバッチで実行される処理で利用可能なコンピューティングタイプを制限できる機能です

Artifacts / Logs

そのままで問題ありません

ここまで設定できましたら “Create build project” を押し作成を行います

サービスロールにポリシーを追加する

この状態では権限がないためECRへのプッシュができません

そこで、先ほどEnvironmentで指定したロールに対してポリシーを追加します

IAMロールの画面(https://console.aws.amazon.com/iamv2#/roles)を開き、先ほど作成したロールをクリックし開きます

Add permission > Attach policies の順番に押しポリシー選択画面を開きます

“AmazonEC2ContainerRegistryPowerUser” を選択し “Attach policies” を押します

CodeBuildを実行し動作を確認する

これで準備が整ったため実行します

CodeBuildの画面へ戻り “Start build” を押し完了するまで待ちます

完了後ECRの画面へ行き、作成したリポジトリに

  • latest
  • latest-amd64
  • latest-arm64

の3つのイメージができていれば成功です

イメージを利用する際は “latest” を利用すればOKです

比較

Buildxで実行するのとどちらが早いか気になっている方もいるかと思います

そこで比較を行ってみました

比較に利用したBuildx用のbuildspec.ymlは以下になります

1version: 0.2
2env:
3  variables:
4    IMAGE_HOST: ''
5    IMAGE_NAME: example
6    TAG_VERSION: latest
7    DOCKER_BUILDKIT: 1
8phases:
9  install:
10    commands:
11      - mkdir -p $HOME/.docker/cli-plugins
12      - curl -L "https://github.com/docker/buildx/releases/download/v0.9.1/buildx-v0.9.1.linux-arm64" -o "$HOME/.docker/cli-plugins/docker-buildx"
13      - chmod +x "$HOME/.docker/cli-plugins/docker-buildx"
14      - docker buildx create --use
15      - docker run --privileged --rm tonistiigi/binfmt --install amd64,arm64
16  pre_build:
17    commands:
18      - aws ecr get-login-password | docker login --username AWS --password-stdin $IMAGE_HOST
19      - export IMAGE_TAG="${IMAGE_HOST}${IMAGE_HOST:+/}${IMAGE_NAME}:${TAG_VERSION:-latest}"
20  build:
21    commands:
22      - docker buildx build -t $IMAGE_TAG --platform linux/arm64,linux/amd64 --push .

それ以外のファイルはそのままにしています

CodeBuildに関してはそのままでは実行できないため、バッチビルドの設定を削除したものを用意し実行しました

それぞれ5回実行した結果が以下になります

バッチビルド

  • 3:44
  • 3:37
  • 3:50
  • 4:59
  • 3:32

Buildx

  • 4:38
  • 4:36
  • 4:38
  • 4:32
  • 4:41

今回Dockerfileの中身がほぼなかったため、差は大きく生まれませんでした

ですがバッチビルドの方が基本的には早かったです

実際はもっと内容があるためさらに差が生まれると思います

最後に

Dockerイメージはlinux/arm64を利用することが増えてきましたが、linux/amd64もまだまだ現役で、どちらも使えないと困ることもあり作ってみました

この記事が少しでも参考になればうれしいです

最後までお読みいただきありがとうございました