Apple Silicon でビルドした Docker イメージを Cloud Run にデプロイする

Apple Silicon でビルドした Docker イメージを Cloud Run にデプロイする

June 8, 2023

私はまだ Intel Mac を使っているのですが、別の Apple Silicon Mac で作業したときに遭遇したことです。

ローカルでビルドした Docker イメージを Cloud Run にあげて動かしたところ、次のエラーがでて起動できませんでした。これは Cloud Run のログに出力されたメッセージです。

terminated: Application failed to start: Failed to create init process: failed to load /usr/local/bin/docker-entrypoint.sh: exec format error

原因と解決策はこの Stack Overflow に書いてありました。

結論だけ書くと、イメージをビルドする際のコマンドを以下のようにすれば解消するはずです。

docker buildx build ./ --platform linux/amd64 -t TAG_NAME

以降では、原因とその解決策について詳しく見ていきたいと思います。

説明 #

エラーとなる原因は、Apple Silicon で ビルドした Docker イメージの形式を Cloud Run がサポートしていないためです。

Cloud Run のドキュメントによると #

Cloud Run のドキュメントには次のように書いてあります。

Executables in the container image must be compiled for Linux 64-bit. Cloud Run specifically supports the Linux x86_64 ABI format.

https://cloud.google.com/run/docs/container-contract

Cloud Run で動かすためには Linux 64 ビット用にコンパイルする必要があります、と説明されています。

CPU の違い #

CPU に何かを命令するときは、その CPU の「命令セット」に従って命令を実行します。つまり命令セットは機械語(マシン語)の仕様のことです。

当然、命令セットが異なる CPU に対してはその命令セットに基づいて命令を実行する必要があります。

Intel Mac の CPU は Intel のアーキテクチャ、Apple Silicon Mac の CPU は ARM のアーキテクチャです。そして両者の「命令セットアーキテクチャ(ISA, instruction set architecture)」は異なっています。

あなたの Mac でターミナルを開き、次のコマンドを実行してみてください。

uname -m

私の Intel Mac では x86_64 と表示されます。Apple Silicon Mac の方であれば arm64 と表示されたのではないでしょうか。

docker build について #

docker build デフォルトでは実行する端末の CPU アーキテクチャでビルドします。

次のコマンドを実行したとき、あなたの Mac が Apple Silicon であれば arm64 向けの Docker イメージを作成します。

docker build ./ TAG_NAME

そしてこれを Cloud Run にデプロイすると冒頭のエラーになります。

もう理由はお分かりかと思いますが、Cloud Run では arm64 向けにビルドされた Docker コンテナは実行できないからです。

x86_64 向けに Docker イメージをビルドする #

あなたの Mac が Apple Silicon だとしても Cloud Run で実行するためには x86_64 向けに Docker イメージをビルドする必要があります。

そのためには、docker build のコマンドを次のように実行しましょう。

docker buildx build ./ --platform linux/amd64 -t TAG_NAME

上記は linux/amd64 のアーキテクチャ向けにビルドしています。

x86_64 でなくて amd64 なの?と思うかもしれませんが、両者は同じものを指しています。これは次の記事がわかりやすいです。

ということで、上記でビルドしたイメージを Cloud Run にデプロイしてみてください。エラーが発生せず上手く動いたのではないでしょうか?

あるいは Dockerfile 内で指定することもできる #

冒頭にリンクを貼った Stack Overflow の記事で紹介されている方法は、docker build コマンドに --platform オプションを付与するものでしたが、それを Dockerfile に記述することもできます。

Dockerfile の FROM にて –platform を指定します。(なおここでは例として node のベースイメージを使用しました。)

# 変更前
FROM node:18-bullseye-slim

# 変更後
FROM --platform=linux/amd64 node:18-bullseye-slim