はじめに
基礎技術研究室リサーチエンジニアの茂木です。今回は、あまりこれまでの記事にはなかった、開発環境の話をします。
Poetry と Docker をいい感じに併用する方法を探った話です。
Poetry
Poetry は、Python のパッケージ・仮想環境の管理ができます。 Poetry を用いてライブラリをインストールすることで依存関係が解決されて lockfile が生成されます。 また、今回はあまり触れませんが、setup.py を書かずにプロジェクトの build や PyPI への publish もできます。
Docker と Poetry の併用
Poetry を使用することでモダンに Python での開発ができるわけですが、一方で開発環境といえば、外せない話題があります。
Docker です。
Docker は、production 環境でも使われますし、開発環境でも使われる例が多くあります。
では Docker を使えば、Poetry は不要になるかというと、必ずしもそうではありません。
Dockerfile に pip install hogehoge
を直接書いたり、requirements.txt を使ったりすると、Poetry が解決する問題が再び表れてしまいます。
つまり Python のパッケージを Poetry で管理することと、Docker で開発環境を作ることは、両立したいわけです。
実は、Poetry と Docker を併用する記事は、検索すると複数でてきます。 しかし、それらでは解決しない問題がありました。
プロジェクトの初期から使いたい
多くの記事が pyproject.toml と poetry.lock を Dockerfile で
COPY
していました。これは誰かが環境を作成・配布し、他の人がそれを元にコンテナを作成する想定だと考えられます。 しかし、pyproject.toml や poetry.lock のない状態から、つまりpoetry init
を打つところから Docker 内で行いたいです。ライブラリの追加や削除も行いたい
上に関連しますが、pyproject.toml や poetry.lock は変更が想定されます。
サーバー開発以外にも使いたい
多くの記事が Flask を用いたアプリケーションなど、サーバーを開発するケースでした。 この場合、Dockerfile の最後でサーバーを起動してアタッチすればよいですが、CLI だとそうは行きません。
そこで GitHub で公開している FEXRD を例として、こうした問題を解決しつつ、マルチステージビルドにより production まで統一的な Docker の使用ができないかを探りました。
Poetry、Docker、Docker-Compose、そして Visual Studio Code による開発
まず、実際に FEXRD で使用している Dockerfile を載せます。 この Python 製ライブラリは、2020 版以降の FFRI Dataset (2020 版の紹介スライドの PDF リンク)から特徴量ベクトルを作成するもので、CLI 機能もあります。
FROM python:3.8.8-slim as python-base ENV PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ \ PIP_NO_CACHE_DIR=off \ PIP_DISABLE_PIP_VERSION_CHECK=on \ PIP_DEFAULT_TIMEOUT=100 \ \ POETRY_VERSION=1.1.5 \ POETRY_HOME="/opt/poetry" \ POETRY_VIRTUALENVS_CREATE=false \ \ PYSETUP_PATH="/opt/pysetup" ENV PATH="$POETRY_HOME/bin:$PATH" FROM python-base as initial RUN apt-get update \ && apt-get install --no-install-recommends -y \ curl \ build-essential \ git \ cmake RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python WORKDIR $PYSETUP_PATH FROM initial as development-base ENV POETRY_NO_INTERACTION=1 COPY poetry.lock pyproject.toml ./ FROM development-base as development RUN poetry install WORKDIR /app FROM development-base as builder-base RUN poetry install --no-dev FROM python-base as production COPY --from=builder-base /usr/local/lib/python3.8/site-packages /usr/local/lib/python3.8/site-packages COPY ./src /app/ WORKDIR /app
これは Poetry の Discussion のコメント を参考にしていますが、異なる点も多くあります。
まず、仮想環境は作らないようにしています。これは Poetry の場合は必ずしも推奨されませんが、ベースイメージが Python の Slim イメージであることと後述の理由から今回は作らないようにしています。
poetry init
をやるために、initial ステージを用意しています。これにより問題 1 が解決されます。cmake
をインストールしているのはライブラリの都合です。他に必要なパッケージや、Poetry をインストールしています。
次に開発用の development ステージを作成します。その前段で COPY poetry.lock pyproject.toml ./
を行い、その後 poetry install
をすることで、キャッシュしています。ここで、poetry.lock と pyproject.toml は最終的なワーキングディレクトリ(/app
)とは別のディレクトリに COPY
され、またライブラリはグローバルにインストールされます。
development コンテナで開発をするために、次の docker-compose.development.yml ファイルを使用します。
version: "3.8" services: app: build: context: ./ dockerfile: Dockerfile target: development volumes: - ./:/app tty: true
Visual Studio Code で開発する場合、.devcontainer ディレクトリを作成し、その下に devcontainer.json ファイルを作成します。
{ "dockerComposeFile": ["../docker-compose.development.yml"], "service": "app", "workspaceFolder": "/app", "extensions": ["ms-python.python"] }
ここで、extensions
には必要な拡張機能を書いてください。
これを用意することで、Visual Studio Code Remote - Containers 拡張機能からコンテナを立ち上げ、開発できます。この拡張機能の使用方法は公式の記事をご参照ください。
development コンテナを立ち上げてアタッチを行い、Visual Studio Code とコンテナによる開発環境が完成します。これで問題 3 が解決されました。
ライブラリを追加する場合、ワーキングディレクトリにおいて poetry add <package>
すれば、グローバルにライブラリが入ります。このために仮想環境は作成しませんでした。このときワーキングディレクトリにある poetry.lock と pyproject.toml が更新されます。これにより問題 2 も解決されます。
production 用には、docker-compose.production.yml を作成しています。
version: "3.8" services: app: build: context: ./ dockerfile: Dockerfile target: production
マルチステージビルドにより、最低限のもので構成されます。
おわりに
今回は Poetry と Docker を併用する方法を探り、FEXRD に実装しました。 今後も開発環境の改善をしていき、他に公開している Python 製 OSS の pypeid1 やデータセット作成スクリプト2 にも適用して、開発に参加しやすく・使いやすくしていきます。
-
FFRI データセット(2019 年版の紹介記事、2020 版の紹介スライドの PDF リンク)と同形式のデータセットを作成できるツール。↩