特定のDockerイメージをAmazon ECSやApp Runnerで実行すべく、デプロイを試みたところ、以下のようなエラーが発生しました。
exec format error
真面目にハマったので、何が起こったのかを整理しておきます。
前提としてのMulti-platform builds
まず前提として、DockerにはMulti-platform buildsという機能があります。これは、複数のアーキテクチャ向けのイメージを1つのDockerfileでビルドできる機能で、linux/amd64
やlinux/arm64
などの異なるアーキテクチャ向けのイメージを同時にビルドできます。
これがなぜ必要なのかというと、Dockerイメージの可搬性を高めるためですね。コンテナはホストのカーネルを使うからこそ軽量なのであり、これは結局コンテナの稼働はホストのアーキテクチャに依存することを意味します。そのため、異なるアーキテクチャ向けのイメージをビルドしておくことで、異なるアーキテクチャのホストに対しても同じイメージを使いまわすことができるというわけですね。
何が起こったのか
ECSやApp Runnerへのデプロイを行うにあたり、僕はECRのリポジトリにコンテナイメージをPUSHし、そのリポジトリからECSやApp Runnerにデプロイするという流れを取りました。
このオペレーションはMacを用いて、次のように行なったわけです。
aws ecr get-login-password --region ap-northeast-1 \ | docker login --username AWS --password-stdin xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com docker pull grafana/grafana:latest docker tag grafana/grafana:latest xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/grafana/grafana:latest docker push xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/grafana/grafana:latest
ここで問題を明らかにするにあたり、2つのポイントがあります。
AWSサービスにおけるCPUアーキテクチャサポート
ECSにおいては、EC2・Fargateともに、AMD64、ARM64アーキテクチャをサポートしています。
- Prerequisites for Amazon EC2 instance support
- Prerequisites for AWS Fargate (Amazon ECS only) support
ただし、ARM64のイメージを使うためには、タスク定義にその旨を記述しておかねばなりません。以下は Specifying the ARM architecture in an Amazon ECS task definitionからの引用です。
{ "runtimePlatform": { "operatingSystemFamily": "LINUX", "cpuArchitecture": "ARM64" }, ... }
一方でApp RunnerはARM64アーキテクチャをサポートしていません。公式のマニュアルにはその記述は見つからなかったのですが、やはり利用できないようです。
記事執筆時点ではARM64のコンテナイメージには対応していないため、x86_64アーキテクチャのイメージを使用する必要があります。
Multi-platform buildsされたイメージに対するdocker pullの挙動
そして、docker pull
の対象イメージがMulti-platform buildsされたイメージである場合、Dockerはホストのアーキテクチャに応じて適切なイメージを選択します。例えばMacでdocker pull
を行うと、その時にpullされるのはlinux/arm64
向けのイメージです。
When you pull the image, the registry returns the manifest list, and Docker automatically selects the correct variant based on the host's architecture. For example, if you run a multi-platform image on an ARM-based Raspberry Pi, Docker selects the linux/arm64 variant. If you run the same image on an x86-64 laptop, Docker selects the linux/amd64 variant (if you're using Linux containers).
起こったこと
Macでdocker pull
やECRへのPUSHを行った結果、linux/arm64
向けのイメージがECRにPUSHされました。そして、ECSやApp Runnerにデプロイする際にそのイメージを使おうとしたところ、それらサービスはAMD64アーキテクチャを想定している状況であったため、exec format error
が発生しました。
解決策
解決策は簡単です。ECSやApp Runnerにデプロイする際には、対象のアーキテクチャに合わせたイメージをPUSHするようにしましょう(もちろん、ECSの場合はARM64に対応しているので、タスク定義側で対応するのが望ましいケースもあります)。
言うは易しですが、どうすれば良いのか。docker pull
するときに、--platform
オプションで対象のアーキテクチャを明示的に指定しましょう。
docker pull --platform linux/amd64 grafana/grafana:latest
これで、対象のアーキテクチャ向けのイメージをPULLすることができます。
なお、イメージがどのアーキテクチャ向けであるかは、docker inspect
で確認できます。
docker inspect grafana/grafana --format='{{.Os}}/{{.Architecture}}' linux/amd64