【M1】devcontainerを使った、研究コード開発環境の構築
はじめに
これまで、Pythonをベースとした研究コードを作成する際に、pyenvでディレクトリごとにPythonのバージョンや必要なmoduleをinstallして作っていましたが、
共有計算機や別環境で計算を行うことも多く、pyenvがupdateされていて、いい感じに必要なライブラリなどが用意できてないとpyenv install x.x.x
といった単順なコマンドがコケることもあり、この際再現性などの観点からもDockerで研究コードを開発するようにしようと思ったのがきっかけです。
(ちなみに、M1のMackBook Proのまっさらな状況で、Homebrewでbrew install pyenv
でインストールしてPATH等設定したら挙動することは確認できてますが今回はDockerで環境を作ることに集中します。)
前提
- MacのM1チップ用のDocker Desktopを利用
- 他の多くの計算環境でも使えるようにデフォルトのARM64イメージではなく、x86-64(AMD64)のイメージで行う
- Visual Studio Code](https://code.visualstudio.com/)のdevcontainerを使う(完成物と開発過程のDockerイメージを基本同一で開発したい)
特に最後のdevcontainerの話は、開発物にはformatterやnotebookのモジュールはいらないけれど、開発時にはそのようなモジュールがあると嬉しいみたいなことがあったり、 最近Visual Studio CodeのupdateでRemote - SSHでもdevcontainerを使えるようになったので、ある程度開発が進んでマシーンスペックを上げて共有計算機やクラウドなどで並列計算させたりしつつリモート環境下での開発などの需要のときに、わざわざリモート側で開発物用の環境を作る必要がないということが大きいです。(これまでは、リモートでpyenvやvenvなどを使ってDockerfileのバージョンに合わせ、requirements.txtを使ってほぼ同じ環境っぽい状態にするといった工程が挟まっていたが、それがなくなった。)
【注】他の共有計算機環境があるならそこにRemote −SSH + devcontainerで開発すればいいのでは?って言うこともありますが、本題は新しいMacのM1チップでどこまでできるのかということを意図しています。共有計算機ないけど、M1 MacBook ProがあってDockerでゴニョゴニョしたいという状況が1番優先される前提です。今後利便性も含め、x86-64環境でビルドしたDocker imageをつかってM1のDockerでコンテナ作成、実行などができるかの検証も今後する予定です。
M1という障壁
その他の記事でも言われていますが、M1でなければそんな面倒でもないですが、ローカルの環境がM1のMacBook Proだとチップの違いによりARM64でビルドされていないPythonモジュールはpip install
できません。Dockerでもそれは同じで、Dockerで作られるimageがARM64だと同様の現象が起きます。 有名どころのモジュールは対応してくれていますがそんなたくさんないのでM1 用のDoker Desktopはマルチプラットフォームでimageが作成可能なため、AMD64でimageを作ることで、前述の問題を回避します。さらにそのビルドしたimageはx86-64の環境でそのまま使えるということにも繋がります。
対応策
最終的な形はこちらのGitHub レポジトリを見てもらえば良いと思いますが、要所をまとめます。
Dockerfile
- 適当にお好みのPython公式のimageを引っ張ってくる。マイナーバージョンまで指定すればビルドタイミングによるバージョン変更など起きない。
- requirements.txtに研究コードに必要なモジュールをバージョンを指定しておく。
WORKDIR
はdocker-compose.ymlのservicesに合わせる 。アプリケーション開発だとappとすることが多いので一旦app
としているが適宜変えて良い。
FROM python:3.7.9 COPY ./requirements.txt /requirements.txt RUN pip install -r /requirements.txt WORKDIR /app
まぁシンプルな感じ
docker-compose.yml
Dockerコマンドが開発ディレクトリをマウントしたり、imageのidを参照するなどすると面倒だし、コマンドが長くなるので、docker-compose
を使う。そのため、その設定ファイルであるdocker-compose.yml
が必要
- 最大のポイントはここで
platform: linux/amd64
としてプラットフォームを指定し、x86-64(AMD64)のimageが作られるようにする。 - あとは開発ディレクトリをマウントしておく(
.
とすることで、docker-compose.yml
がある場所をマウントするということになる。)
version: "3.8" services: app: build: . platform: linux/amd64 volumes: - ./:/app
とりあえずテスト
まずはDockerfileをビルドする。
docker-compose build
とりあえずビルドが終わったら、所定のバージョンがインストールされているか、ご所望のモジュールがインストールされているかなど確認してみる
docker-compose run app python -V # Python 3.7.9 (Dockerfileで指定したversion)
docker-compose run app pip list # Package Version # --------------- -------- # click 7.1.2 # numpy 1.21.5 # pandas 1.3.5 # Pillow 8.4.0 # pip 21.0.1 # python-dateutil 2.8.2 # pytz 2021.3 # QEPPI 0.1.11 # rdkit-pypi 2021.9.3 # setuptools 53.0.0 # six 1.16.0 # wheel 0.36.2
コンテナの中に遊んでもいい。
docker-compose exec app bash
とすれば、コンテナの中に入れるのであとは適宜必要に応じて確認などする。
pythonモジュールの作成
なんか適当に開発モジュールを適当に作る。要するにxxxx.py。
今回テスト的に、py4chemoinformaticsを参考にRDKitのMolToGridImage
でsildenafilとvardenafilの構造式をプロットしてくれるコードを書きました。(MolToGridImage.py)
あと、今回の本題とは、ずれますがargparserとしてclickを採用しました。シンプルで結構便利です。
モジュールを動かしてみる
docker-compose run app python -m rdkit_MolToGridImage --output ./data --name test --align True # 上記はargをあえて書いているが、defaultを指定しておけば、 docker-compose run app python -m rdkit_MolToGridImage でも実行可能
単にDockerでPython環境を固定して動かすだけであれば、これで十分だが、Visual Studio Codeのdevcontainerを使って開発したいので、こちらを準備する。
devcontainer
Visual Studio Codeを開いて、左下の><となっているところをクリックして、Add Development Container Configuration Files
っていうのをクリックして、From docker-compose
というのを選ぶと、.devcontainerディレクトリができて、devcontainer.jsonとdocker-compose.ymlが生成される。
devcontainer.json
基本自動生成されたままでよいが、使用するPythonは、devcontainerのpythonを指定。/usr/local/bin/python
で問題ないはず。
今回は、devcontainer上だけで使いたいフォーマッターなどのモジュールをinstallしたり、Visual Studio CodeのExtensionを毎回設定するのは面倒なので、devcontainerを使うときに最初から設定されるようにする。
"settings", "extensions", "postCreateCommand"らへんがそんなところ。
docker-compose.yml
自動生成されたままでOK
詳細はGitHubを参照してもらえばと思います。
devcontainerの起動
上記がおわったら、早速起動してみましょう。左下の><となっているところをクリックして、Open Fold in Container
をクリックすれば、devcontainerがビルドされます。コンテナ作成と追加のモジュールのインストール程度の時間なので、1-2分くらいです。そうすると以下のようになります。
あとは、よしなにこちらで開発をすすめていけばOKです。
Jupyter Notebookについて
Visual Studio Codeは.ipynb
ファイルを読みこんでノートブックとして動かすことが可能で固定された環境でとても便利ですが、上記の経緯で進めた結果、Notebookの実行でM1固有の問題に直面し、Cellを実行すると、
Failed to start the Kernel. qemu: uncaught target signal 6 (Aborted) - core dumped. View Jupyter log for further details.
となってしまって、実行できませんでした。いろいろ調べましたが現状Jupyter NotebookをVisual Studio Codeで動かすことは、まだ難しそうです。
おわりに
今回は、
- MacのM1チップ用のDocker Desktopを利用して、M1のMacでも特定のPython環境にお好みのモジュールをinstallし研究コードを開発する環境構築方法を示しました。
- また、
docker-compose
を使うことで長くなりがちのdockerコマンドを簡略化しました。 docker-compose
コマンドを使って、実際のpythonモジュールを動かす方法示しました。- Visual Studio Codeのdevcontainerを使って、完成品の環境と同一のimageを使った状態で開発をすすめる方法を示しました。
残念ながら、Jupyter NotebookをVisual Studio Codeで動かすことができなかったです。今後のupdateを期待したいところです。