M1 MacのDockerでどうにかこうにかProphetを動かせた話
結論
- Docker for Macの代わりにlimaを使う
- limaでUbuntuのVMを立ち上げ、VM内でDockerを動かし、Mac側からそれを利用する
- limaのインストールには「パッチを当てる…」系の記事が多く出ているが、2022/03/16現在のlimaであれば、
brew install lima
で入れたものが普通に使える-
ただし、下記に注意!
-
uname -m
→ 出力がarm64
であることを確認-
X86_64
と出る場合、ターミナルがX86_64版で起動している
-
-
which brew
→ 出力が/opt/homebrew/bin/brew
であることを確認-
/usr/local/bin/brew
と出る場合、X86_64版のhomebrewになっている - homebrewにはX86_64版とARM64版があり、X86_64版の方になっていると、limaのインストールには成功するが後段で失敗する
-
- 参考: lima公式のissue
-
-
ただし、下記に注意!
経緯
普段はPoetryを利用していて、あまりDockerは使わずにAWSへの展開はできるだけLambda等で済ませる形で開発しているが、先日Prophetを扱うことになった。
とりあえずローカルの検証ではPoetryでProphetが入ったが、Lambdaだとサイズオーバーなので、AWSに展開するためにはEC2に展開するか、ECSを使わないといけない。
(マネージドのAmazon Forecastとかも迷いましたが、今回は用途に合わなかったので不採用。)
EC2は嫌だったので、ECSを使う…となるとDocker化が必須に。
M1チップ出始めの頃にDockerを使おうとして全然うまく行かず、そのとき以来苦手意識がある状態ではあったが、今後もDockerは使っていくことになるだろうということで、Docker化することに決定。
やろうとしたらやっぱりハマりまくったので、今思い出しながら記事を書いている。
docker build
中、prophetの依存パッケージであるpystanのインストールで止まる
ハマりポイント1: DockerFileを書いていざdocker build
すると、pystanのinstallが全然終わらない。
謎だったのは「Poetryで入れたローカルのprophetはM1 Mac上で動いているのに、なぜDocker上では動かないのか?」というところ。
Dockerイメージの選択が悪いのか?と思いUbuntuベースにしたり、動いているPoetry環境をコピーして利用(参考: 仕事でPythonコンテナをデプロイする人向けのDockerfile (1): オールマイティ編)したりしようとしたが、変わらず…
調べているとこちらの記事を発見
M1 Pro + Docker for Macが遅い
ではやってみよう、ということで、limaを試すことに。
(この時点で brew uninstall docker --cask
した。)
ハマりポイント2: limaでVMが立ち上がらない
※注: このポイントは、M1 Macを買ってイチから環境構築をした人はハマらずに済むはずです。(ARM64版のhomebrewでlimaをinstallできればOK)
先程の記事や、Docker on Limaで脱Docker Desktop for Macなどにある手順に従ってlimaをインストール。
limactl start [設定ファイル名].yml
でVMを立ち上げようとすると
FATA[00xx] exiting, status={Running:false Degraded:false Exiting:true Errors:[] SSHLocalPort:0} (hint: see "/Users/xxx/.lima/xxx/ha.stderr.log")
と出る。
先程の記事の例ではポートが既に専有されていたということだったが、どうもそうでは無かったので、調べていくと「limaが利用しているqemuというプロフェッサエミュレータに、HVFというHypervisor.frameworkを使ったアクセラレータが入っていないとダメ」らしい。(ヨコモジワカラナイ)
qemu-system-aarch64 -accel help
というコマンドでチェックできるということで、確認。
$ qemu-system-aarch64 -accel help
Accelerators supported in QEMU binary:
tcg
hvf
と出ないので、これが原因っぽい。
「パッチを当ててインストールする」という記事がいくつか(こちら1やこちら2やこちら3やこちら4)あったので、「最新版ならパッチ不要」という情報も見かけていたが、一応それぞれトライ。が、やっぱりダメ。
(今よく読んだら、こちら3に「If you already installed x86_64 Homebrew in /usr/local, please uninstall it. It’s not possible to build QEMU with x86_64 Homebrew」って書いてますね…気づかなかった…)
しんどい…と思いながら調べていると行き当たったのがこちらのissue。
qemu-system-x86_64: Unknown Error (Was: 60022: Connection refused SSH)
#543
toshitanianさんの気になるコメントを発見。
I didn't know I need to install Homebrew for two different environment of x86 and arm.
これか…?と思い、調べると、この記事に行き当たった。
https://docs.brew.sh/Installation によれば、このスクリプトを動作させると、
macOS Intel では /usr/local に、 macOS Apple Silicon では /opt/homebrew にインストールされる。
チェック。
$ which brew
/usr/local/bin/brew
/usr/local/bin/brew
ということは、どうやらX86_64版が入ってしまっている!
ハマりポイント3: ターミナルがなぜX86_64で起動しているかわからない
私の場合、Intel版のMacのBackupからM1のMacへ移行していたので、HomebrewがX86_64版のままだった。そのため、X86_64版のlimaがインストールされてしまい、うまく動いていなかったということらしい。
先程のbrewのインストールに関する記事に「インストーラがCPUを識別してうまく動作するようだ。」とあったので、とりあえずbrewを再インストールしてみるが、/usr/local/bin/brew
のまま。
なぜ…と思って一応確認すると、
$ uname -m
X86_64
ターミナルがX86_64で起動していた。
Rosetta 2が働いてしまっているのかなー、と思いながら、iTerm2の「情報を見る」も、Rosetta 2を使う設定にはなっていない。
ではなぜ…?と調べていると、発見。
Rosettaを解除したのにarm64にならない原因
原因は、親プロセスがx86_64で作動しているからである。親プロセスがx86_64で作動していた場合、そこから発生するコマンドの全てがx86_64で作動する。
なるほど…!
私の場合、X86_64版のHomebrewでインストールしたfishシェルを使っていたので、fishもX86_64版が入っていた。結果、ターミナルがX86_64扱いになってしまっていた。
やっと解決へ…
というわけで、brew uninstall fish
してから公式のインストーラからfishを再インストール。
iTerm2を再起動すると、
$ uname -m
arm64
OK!
homebrewも再インストール。(fish環境でbrew(Homebrew)をインストールする)
$ which brew
/opt/homebrew/bin/brew
OK!
あとは先程のこの記事や、この記事などにある手順に従って、dockerを利用する環境が整った。
私の場合はPoetryをそのまま使いたかったので、動いているPoetry環境をコピーして利用する方向でDockerFileを作成。
# 参考:https://future-architect.github.io/articles/20200513/
# ここはビルド用のコンテナ
FROM python:3.9-buster as builder
WORKDIR /opt/app
RUN pip install -U pip poetry
COPY pyproject.toml .
COPY poetry.lock .
RUN poetry config virtualenvs.in-project true && \
poetry install --no-dev --no-interaction -vvv
# ここからは実行用コンテナの準備
FROM python:3.9-slim-buster as runner
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
COPY --from=builder /opt/app/.venv /opt/app/.venv
ENV PATH=/opt/app/.venv/bin:$PATH
WORKDIR /opt/app
# 以降はやりたいように
docker build ...
したら、docker run -it ...
で中に入り、Prophetが入ったかどうかチェック。
$ python
Python 3.9.10 (main, Mar 1 2022, 21:02:54)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import prophet
>>> prophet.__version__
'1.0'
>>>
OK!!!
これでやっと、使える状態に…!疲れた…
感想
- CPUアーキテクチャまわりもDockerまわりもVMまわりも何もわからんだったので、しんどみがヤバすぎた
- 新しくPCを買ったら、開発環境は再構築したほうがいい
- これやってればハマりポイント2,3がほぼ無かったはず
- 今後はDockerも使って開発していく…!
- Docker for Macからの卒業…!
この記事が少しでも皆様のハマりポイント解消の手助けになれば幸いです。
追加の補足: DockerイメージをAWS CDK等でECRにプッシュして利用する場合の注意点
- docker-credential-desktop not installed or not available in PATH... 的なエラーが出る場合は、
~/.docker/config.json
を削除する必要がある- 参考: エラーの回避方法について
- もしかしたら認証に関わっている部分だけ消せばいいかもしれない。未検証。
- ECSのタスク定義でCPUアーキテクチャを指定できる。Dockerイメージをビルドしたアーキテクチャと同じかどうかを要確認
- CDKの場合、
runtimePlatform: { cpuArchitecture: ecs.CpuArchitecture.ARM64 },
みたいな感じで指定できる。- Fargateなら
FargateTaskDefinition
内。(公式doc: class FargateTaskDefinition (construct))
- Fargateなら
- CDKの場合、
Discussion