NeetEch

DockerとPythonと環境構築

こんにちは,ぎいとです.

今更Dockerについてさらっと勉強してみたところ,コンテナの中に開発環境を構築したらどうなんだろう,という疑問が湧いたので調べてみまして.

Pythonを例にいくつかの記事を見つけ,筆者には難しいところがいくつかあったのでそれについて考えてみました.前置きがゴチャゴチャなのでいきなり本題もどうぞ.

なお,筆者はDockerに関してもPythonに関しても素人です.粗が目立つかもしれません.また筆者は頭も強くないです.誤りなどがございましたら,早急に訂正しますので改善点,ご意見など頂けると助かります.感想もドシドシどうぞ.

開発環境って人によって?

突然ですが筆者は「Dockerコンテナは,何かのプロセスを実行するために作られ,プロセスを実行し,実行後消滅するべき」という宗教を信仰することにしています.
この思想をどれくらいの人が信じているのかわかりませんが,環境構築の記事を読んでいると混乱してくる理由がこれにありました.「Docker(イメージ上)にPythonの開発環境を構築する」がしたかったわけですが,「Dockerは特定のプロセスを実行する」となんとなく噛み合わないんですよね.“開発”は人間が行うプロセスであって,コンテナのプロセスとして”開発”を扱っていいのかと.

さらに,そもそもDockerに構築する予定の「Pythonの開発環境」とはなんなのだろうか,というところもわからなくなりました.
これまでの筆者の想像するPythonの(モダンな)開発環境は,実行環境の構築,gitによるバージョン管理,PyPIパッケージの依存関係の処理(pipenv install,poetry add など),エディタを用いたコーディングが可能,など全てを満たす環境のことと認識していました.
しかしgitやエディタも同一のコンテナに詰め込むのはやりすぎな印象も受けます(これはただの感想ですが…).エディタに関しては使えるかも微妙なところです.コンテナに入れていいの?って感じです.

一方で,Pythonでのプロジェクト開発では,テストフレームワークとしてpytestを利用する方も多いかと思います.あらかじめテストケースを書いておいて,pytestを実行するとテストケースを検証してくれるやつです.
このpytestの実行環境としてDockerを使うのはしっくりきます.「pytestを実行する」ためのコンテナですね.しかしこれは実行環境であってこれだけでは開発環境と言えるのかよくわからないです.pythonコマンドでも同様に,です.

混乱した頭で考えた結果,構築したい環境がなんとなく見えてきました.すごく丁寧にいうと,「 “ホストOSでgit,dokcerコマンド,エディタによるソースファイルなどの編集を行い,Python絡みのこと(pyenv,pipenv,pythonコマンド)はDockerコンテナで実行できる” 開発環境を構築する」のがこの記事の目的です.

長々と書きましたが,巷のDockerを用いたPythonの開発環境の構築と基本的な指針は同じだと思います(一部例外はあるかも).ただ,どこまでを仮想化(隔離)していて,Dockerコンテナでなんの処理をさせているのかがわかりづらい印象だったのでツラツラ書いてみました.混乱させてしまったらすいません.

仮想開発環境を作ってみる

ということで実際に作ってみた非常に単純なDockerfileを基に仮想開発環境を構築します.まぁイメージを作成するだけなんですが.
Pipenvで作成していますが,執筆時点でPipenvは使わない方が…みたいな風潮があるような.poetryの方がいいかも.

FROM python

# pipenvのインストール
RUN pip install --upgrade pip && pip install pipenv

# 環境を設定
ENV PIPENV_SHELL /bin/bash  # これがないと [$ pipenv shell] が実行できない
ENV PIPENV_VENV_IN_PROJECT 1  # 開発プロジェクトのディレクトリにpipenv用の環境を用意する

# 開発プロジェクトのディレクトリに移動
WORKDIR /root/project

# コマンド( /bin/bashなどは基本的に起動しない )
ENTRYPOINT ["pipenv"]

このDockerfileがあるディレクトリで以下を実行します.

$ docker build -t pipenv .  # Dockerイメージの作成
$ docker run pipenv --version  # コンテナの起動を確認

詳しくは本題から逸れそうなので割愛です.とりあえずこれでやりたいことは終わり.

Dockerコンテナ内での変更は残らない

ここまでで作業は完了ですが使い方に注意が必要です.そもそもコンテナは,コンテナ内での変更を記録しません.コンテナ内部でのあらゆる作業は,ホスト側に反映されないんですね.Pipfile やPipenvによる仮想環境( .venv/ )も例外ではありません.

$ docker run pipenv --three  # コンテナ上にPythonの仮想環境を作成
...
$ docker run pipenv --venv  # コンテナ上のPythonの仮想環境の配置先を出力
No virtualenv has been created for this project yet!
Aborted!

1行目は .venv/ の作成(というプロセス)です.プロセスが終了すればコンテナは停止しますから,せっかく作った.venvは,保持されずにコンテナと一緒に破棄されます.
2行目を実行すると,コンテナ上に.venv が存在しないと怒られます.

これを避けるために,ホストOS上にプロジェクトディレクトリを作成しコンテナからの参照と編集を許可するという方針をとります.マウントしましょう
マウントすると,あたかも外部(ホストOS側)のディレクトリが内部(コンテナ側)に存在するかのように扱えます

マウント自体はすごく簡単です.Dockerのrunコマンドにオプションを指定すればOK.カレントディレクトリをマウントするなら,次のようにします.

$ docker run -v ${PWD}:/root/project pipenv --virsion

-v がマウントオプションです.${PWD}がカレントディレクトリ(絶対パス),/root/project がコンテナ上のディレクトリです.コンテナでの変更がカレントディレクトリに反映されるようになります.次の例を見て見ましょう.
なお,/root/project という名前が気に食わない場合には,DockerfileのWORKDIRも同じように変更してください.

動作確認

# サンプルプロジェクトを用意
$ mkdir sample_project/src/ 
$ cd sample_project/ 
$ touch src/__init__.py 
$ echo "print('Hello, World! ')" > src/__main__.py 

# 仮想環境をコンテナを用いて作成
$ docker run -v ${PWD}:/root/project pipenv --three
# numpyをインストールしてみる
$ docker run -v ${PWD}:/root/project pipenv install numpy
# 実行してみる
$ docker run -v ${PWD}:/root/project pipenv run python src
Hello, World!

sample_project/ に,Pipfile,.venvなどが作成されていることが確認できると思います..venv/ もプロジェクト内に作成されていますから,プロジェクトを破棄したければsample_project/ を削除すればOKです.
イメージを削除すればpipenv環境ともさよならができます.ホスト環境はできるだけ汚したくない.
ソースファイルの編集はホストOSから行えるので,使い慣れたエディタで編集できます.

しかしコンテナに作業させるので,コマンドがいちいち長いという欠点があります.シェルスクリプトはまだまだ勉強が足りないので解決案があったら教えていただけると助かります.

同じようにやっていけばpyenvなんかでも構築できるんじゃなかろうか

Follow me!