M1 MacでHeroku containerにデプロイしたら `Exec format error` と出て困った話

2 min read読了の目安(約2600字

環境

  • date: 2021/04/26 (事象が起きたのは2月後半ですが、本記事執筆日に再現することを確認)
  • heroku cli: 7.52.0
  • docker desktop: 3.3.1 (63152)
  • docker engine: 20.10.5 build 55c4c88

先に結論

自分の場合は、armアーキテクチャ向けのDocker ImageをBuild/Pushしていたため、失敗していました。(Heroku側がx86なのでしょう、多分)
x86アーキテクチャ向けのDocker ImageをBuild/Pushすることで、正常に動作するようになりました。
方法は後述します。

はじめに

友人たちとオンラインでボードゲームをしている時に使うツールの開発[1]で、バックエンドをHerokuにしようとしていました。[2]
Herokuを使うのは約4年ぶりだったのですが、知らない間にDockerサポートが来ていたので、こりゃ楽だな、と高をくくっていました。

エラーとの遭遇

ローカルのDockerコンテナで動作することを確認して、いざデプロイといきます。
基本的に上記のページ通り、ただHerokuアプリ名は指定する、という形で、以下のコマンドを打っていました。(以下アプリ名はexampleとします)

$ heroku login
$ heroku container:login
$ heroku container:push web -a example
=> Your image has been successfully pushed. You can now release it with the 'container:release' command.
$ heroku container:release web -a example
=> Releasing images web to example... done

楽勝だったなぁと思いつつ、アプリのAPIを叩いても結果が帰ってこない。
久しぶりだったからポートの扱いでも間違えたかな、など思いつつ heroku logs -a example でログを確認しました。

2021-04-26T13:08:01.635688+00:00 heroku[web.1]: Starting process with command `java -jar /work/app.jar`
2021-04-26T13:08:04.757975+00:00 app[web.1]: Error: Exec format error
2021-04-26T13:08:04.832667+00:00 heroku[web.1]: Process exited with status 126
2021-04-26T13:08:04.929320+00:00 heroku[web.1]: State changed from starting to crashed

見慣れないExec format errorで調べると、アーキテクチャが云々出てきたので、試行錯誤しているうちに、以下の解決策1で解決しました。

解決方法1. M1 Macでx86向けのDocker Imageをビルドする

色々試しているうち[3]に、x86向けのイメージじゃないからだと当てがついたので、x86向けイメージをどうにか作れないかと調べていたところ、マルチプラットフォームのイメージビルドなるものがありました。
それに従い以下のような形で、コマンドを打ち、無事成功しました。(以下タグに使うユーザ名をusernameとしています)

$ docker buildx build . --platform linux/amd64 -t username/example:latest
$ docker tag username/example registry.heroku.com/example/web
$ docker push registry.heroku.com/example/web
$ heroku container:release web -a example

解決方法2. x86のマシンを使う(CIなど)

自分では試していないのですが、本記事執筆にあたって再度調べてみると、別の方法でやっている方がおり、自分より賢いやり方だと思ったので、ここで紹介しておきます。
GitHub Actionsで行われていますね。

終わりに

自分はM1 Macでハマることは無いだろうと思っていたので、こんなこともあるんだなぁと思いました。
また、改めて調べてみて別の解決法を知ると視野が広がって、とても良い気持ちになります。
こういうのもアウトプットの副次的な効果かと思うと、ますますアウトプットしないとな、と思います。
最後に、本題とズレるのですが、尊敬する先輩の言葉で、「まずエラーログを読む」というのを身に染みるまで教わってよかったと思います。[4]

脚注
  1. こういうのもアウトプットしていきたい ↩︎

  2. 理由はもちろんお金です ↩︎

  3. AWS-EC2にDockerインストールして再現確認しました ↩︎

  4. この章はideaの領域ですね ↩︎