FFRIエンジニアブログ

株式会社FFRIセキュリティのエンジニアが執筆する技術者向けブログです

Poetry と Docker を併用する試み

はじめに

基礎技術研究室リサーチエンジニアの茂木です。今回は、あまりこれまでの記事にはなかった、開発環境の話をします。

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 を併用する記事は、検索すると複数でてきます。 しかし、それらでは解決しない問題がありました。

  1. プロジェクトの初期から使いたい

    多くの記事が pyproject.toml と poetry.lock を Dockerfile で COPY していました。これは誰かが環境を作成・配布し、他の人がそれを元にコンテナを作成する想定だと考えられます。 しかし、pyproject.toml や poetry.lock のない状態から、つまり poetry init を打つところから Docker 内で行いたいです。

  2. ライブラリの追加や削除も行いたい

    上に関連しますが、pyproject.toml や poetry.lock は変更が想定されます。

  3. サーバー開発以外にも使いたい

    多くの記事が 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 にも適用して、開発に参加しやすく・使いやすくしていきます。


  1. パッカー検知ツールの PEiD を Python で再実装したツール。

  2. FFRI データセット(2019 年版の紹介記事2020 版の紹介スライドの PDF リンク)と同形式のデータセットを作成できるツール。