Pulumiでマルチプラットフォーム対応のDocker Imageを作成する時はdocker-buildパッケージを使うのがオススメです。ただ注意点として、1年余りプレビュー状態が続いており、これまでのdockerパッケージから見ると挙動の違いに注意が必要です。
今回はPulumiでマルチプラットフォームなDocker Imageを作成する方法を紹介します。
PulumiでマルチプラットフォームDocker Imageを作成したい
Docker Imageを作成するにあたって長年課題だったのが、マルチプラットフォームなDocker Image作成ができないことでした。Docker BuildKitをベースにマルチプラットフォームイメージを簡単に扱えるようになっても、PulumiのDockerイメージ作成は長年ツギハギ対応で使いたくありませんでした。しかし、Docker Build Providerの登場でマルチプラットフォームイメージを安定して作成できるようになっています。
どうやるのか説明の前に、Dockerイメージ作成~利用を軽くイメージしてみましょう。
AWSを使っているならECRにイメージをプッシュすることでAWSサービスからIAM認証でプライベートなイメージを安全に取得できます。この挙動はサービスの安定的なコンテナイメージ取得において欠かせないもので、AzureやGoogle Cloudも同様の仕組みがあります。 例えば、PulumiでアプリケーションのイメージからECSでのタスク実行までやろうと思った場合、次のような流れになります。流れ自体は、amd64(x86_64)向けやarm64など単独プラットフォームなイメージであっても、マルチプラットフォームなイメージでも同じです。
- ローカル・CIでイメージをビルド&ECRにプッシュ
- TaskDefinitionでプッシュしたECRイメージを参照
- ECS Serviceで更新したTask Definitionを用いる
さて、この例でマルチプラットフォームの需要の鍵はECSです。
2024年12月にFargateがARM64 Spotに対応したこともあり、ECSをamd64で動かしていたがarm64にしたい。あるいは両アーキテクチャで動かしたい、というケースが現実を帯びるようになりました。こういった時、まずマルチプラットフォームなイメージに対応しておくことで、「Task Definitionのイメージタグ構成はかえず」に「ECS Service Providerをamd64/arm64で調整」するだけでスムーズなアーキテクチャ変更ができます。
ECSに限らずコンテナアプリケーションにおいて、マルチプラットフォーム対応したコンテナイメージは柔軟な運用を支えるため、マルチプラットフォームイメージは重要だと考えています。
マルチプラットフォームイメージとは
マルチプラットフォームなイメージ作成の基礎は、Dockerが出しているMulti-platform buildsがよくまとまっているので詳細はこちらに譲ります。ここではマルチプラットフォームイメージの概要だけかいつまみましょう。
単独プラットフォームイメージ
単独のマニフェストを持ち、それがコンフィグとレイヤーセットへのポインターとなっています。いわゆるDockerマニフェストという理解そのままではないでしょうか。
単独のマニフェストとはイメージのアーキテクチャやOSを指すため、amd64向けのイメージタグを取得したら中身はamd64向けのイメージだけです。 amd64とarm64の両方をサポートする単独タグは作れないので、「2つのイメージ」か「それぞれのタグ」を用意する必要があります。マルチプラットフォーム対応には、開発者・利用者共にイメージやタグを使い分ける必要があります。
マルチプラットフォームイメージ
マニフェストリストと複数のマニフェストを持ちます。マニフェストリストはOS・アーキテクチャに合わせて適切なマニフェストを指し示す役割を持っています。
マルチプラットフォームイメージをレジストリからダウンロードすると、まずマニフェストリストを取得し適切なアーキテクチャのイメージを選択、続いて対象アーキテクチャのマニフェストを取得します。 つまり、amd64とarm64の両方をサポートする「単独のイメージ・タグ」を作れます。マルチプラットフォーム対応には、開発者は「アーキテクチャごとにイメージビルド」「マニフェストリストの作成」を行う必要がありますが、利用者はamd64・arm64に関わらず同じイメージタグを利用できます。
まとめると次のようになります。複数アーキテクチャを単一イメージでシームレスに使ってほしい、という動機があるならマルチプラットフォームイメージを使うのがオススメです。
対象 | 単独プラットフォームイメージ | マルチプラットフォームイメージ | 備考 |
---|---|---|---|
単独アーキテクチャ (開発者) | ◎ | × | マルチプラットフォームに対応できない |
単独アーキテクチャ (利用者) | ◎ | × | マルチプラットフォームに対応できない |
複数アーキテクチャサポート (開発者) | 〇 | ◎ | 開発者はマニフェストリストの作成が必要 利用者は単一イメージタグでプラットフォーム問わず利用できる |
複数アーキテクチャサポート (利用者) | 〇 | ◎ | 開発者はマニフェストリストの作成が必要 利用者は単一イメージタグでプラットフォーム問わず利用できる |
PulumiでDockerイメージを作る
ECRにDockerイメージをプッシュする流れをPulumiで書いてみましょう。今回は3つの例を紹介します。
- 単独プラットフォームイメージを作る (Pulumi.Docker)
- 単独プラットフォームイメージを作る (Pulumi.DockerBuild)
- マルチプラットフォームイメージを作る (Pulumi.DockerBuild)
事前作業
マルチプラットフォームに対応している適当なイメージでDockefileを用意しておきます。今回はdocker/Dockerfile
に配置します。
FROM alpine:latest
単独プラットフォームイメージを作る (Pulumi.Docker)
以前から存在しているPulumi.Dockerパッケージで、単独プラットフォームイメージを作ってみましょう。
まずはパッケージを導入します。
dotnet add package Pulumi.Docker --version 4.5.5
続けてPulumi C#で、ECR作成&Dockerイメージを作成しつつプッシュするコードです。
var ecr = new Pulumi.Aws.Ecr.Repository("demo-image", new () { Name = "demo-image", ForceDelete = true, }, new CustomResourceOptions()); var credential = ecr.RegistryId.Apply(x => Pulumi.Aws.Ecr.GetAuthorizationToken.InvokeAsync(new() { RegistryId = x, })); var demoImage = new Pulumi.Docker.Image("demo-image", new() { Build = new Pulumi.Docker.Inputs.DockerBuildArgs() { Context = ".", Dockerfile = "docker/Dockerfile", Platform = "linux/amd64", }, ImageName = ecr.RepositoryUrl.Apply(x => $"{x}:tag1"), Registry = new Pulumi.Docker.Inputs.RegistryArgs { Server = ecr.RepositoryUrl, Username = credential.Apply(x => x.UserName), Password = credential.Apply(x => x.Password), }, SkipPush = false, });
適用してみましょう。
$ pulumi up + ├─ aws:ecr:Repository demo-image create + └─ docker:index:Image demo-image create Resources: + 2 created
できていますね。
APIを見てわかる通り、Pulumi.Docker.Image
は単独プラットフォームイメージのみサポートしています。マルチプラットフォームイメージ対応するために新設されたのが、Pulumi.DockerBuild
です。
単独プラットフォームイメージを作る (Pulumi.DockerBuild)
続けて、本記事で紹介したいPulumi.DockerBuildパッケージを使って、単独プラットフォームイメージを作ってみましょう。先ほどのECRを一度消してゼロベースで実行していると考えてください。1
まずはパッケージを導入します。
dotnet add package Pulumi.DockerBuild --version 0.0.9
Pulumi.DockerとはAPの差異はあるものの、おおむね同じようなコードです。
var demoImage = new Pulumi.DockerBuild.Image("demo-image", new() { BuildOnPreview = true, Context = new Pulumi.DockerBuild.Inputs.BuildContextArgs { Location = "docker/" }, Platforms = [Pulumi.DockerBuild.Platform.Linux_amd64], Tags = [ ecr.RepositoryUrl.Apply(x => $"{x}:tag1"), ], Registries = new Pulumi.DockerBuild.Inputs.RegistryArgs { Address = ecr.RepositoryUrl, Username = credential.Apply(x => x.UserName), Password = credential.Apply(x => x.Password), }, Push = !Pulumi.Deployment.Instance.IsDryRun, // previewとpushでboolが切り替わる });
適用してみましょう。
$ pulumi up + ├─ aws:ecr:Repository demo-image created (0.55s) + └─ docker-build:index:Image demo-image created (5s) Resources: + 2 created
できていますね。
DockerBuildパッケージの注意点
先ほどのコードは、Push
によってPulumi.Dockerと挙動が異なっています。
Pulumi.DockerBuildは、Push = true
にしているとECRの存在いかんにかかわらずpushするように変更されました。
このため、ECRリポジトリがない状態でPush = true
かつpulumi up
を実行すると次のエラーを出力します。
docker-build:index:Image (demo-image): error: docker-build:index:Image resource 'demo-image': property exports[0] value {<nil>} has a problem: at least one tag or export name is needed when pushing to a registry
Push = true
の代わりにPulumi.Deployment.Instance.IsDryRun
を使ってpreview時はプッシュしないように制御する必要があります。
マルチプラットフォームイメージを作る (Pulumi.DockerBuild)
APIを見てわかる通り、Pulumi.DockerBuild.Imageはマルチプラットフォームイメージをサポートしています。次のコードはPlatforms
にlinux/arm64
を追加しています。
var demoImage = new Pulumi.DockerBuild.Image("demo-image", new() { BuildOnPreview = true, Context = new Pulumi.DockerBuild.Inputs.BuildContextArgs { Location = "docker/" }, Platforms = [Pulumi.DockerBuild.Platform.Linux_amd64, Pulumi.DockerBuild.Platform.Linux_arm64], Tags = [ ecr.RepositoryUrl.Apply(x => $"{x}:tag1"), ], Registries = new Pulumi.DockerBuild.Inputs.RegistryArgs { Address = ecr.RepositoryUrl, Username = credential.Apply(x => x.UserName), Password = credential.Apply(x => x.Password), }, Push = !Pulumi.Deployment.Instance.IsDryRun, });
適用してみましょう。
$ pulumi up ~ └─ docker-build:index:Image demo-image updated (5s) [diff: ~platfor Resources: ~ 1 updated
ECRを見るとマルチプラットフォームなイメージ一覧に代わっています。特に、先ほどまではArtifact typeがImageでしたが、Image Indexへ変わったことに注目してください。
まとめ
Pulumiでマルチプラットフォームなイメージ作成をするには、Pulumi.DockerBuildパッケージを使うのがオススメです。
実運用を考えるとアプリケーションはPulumiとは独立してイメージビルド・プッシュすることが多いでしょう。 しかし、簡易なアプリケーション構成ではIaCでデプロイまでまとめてやって手間を極小にするケースがあります。本記事がそういったユースケースで参考になれば幸いです。
参考
- Docker Provider | Pulumi
- Docker Build Provider | Pulumi
- #7428 Using multi-arch Docker images with Pulumi? | pulumi/pulumi
- Introducing the new Docker Build provider | Pulumi Blog
- ECRをかえずイメージ部分だけマイグレートもできます↩