理系学生日記

おまえはいつまで学生気分なのか

App RunnerやECSで"exec format error"が発生する / Multi-platform buildsでハマる

特定のDockerイメージをAmazon ECSApp Runnerで実行すべく、デプロイを試みたところ、以下のようなエラーが発生しました。

exec format error

真面目にハマったので、何が起こったのかを整理しておきます。

前提としてのMulti-platform builds

まず前提として、DockerにはMulti-platform buildsという機能があります。これは、複数のアーキテクチャ向けのイメージを1つのDockerfileでビルドできる機能で、linux/amd64linux/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アーキテクチャをサポートしています。

ただし、ARM64のイメージを使うためには、タスク定義にその旨を記述しておかねばなりません。以下は Specifying the ARM architecture in an Amazon ECS task definitionからの引用です。

{
    "runtimePlatform": {
        "operatingSystemFamily": "LINUX",
        "cpuArchitecture": "ARM64"
    },
...
}

一方でApp RunnerはARM64アーキテクチャをサポートしていません。公式のマニュアルにはその記述は見つからなかったのですが、やはり利用できないようです。

記事執筆時点ではARM64のコンテナイメージには対応していないため、x86_64アーキテクチャのイメージを使用する必要があります。

App Runnerの可能性 - NIFTY engineering

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).

Multi-platform | Docker Docs

起こったこと

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