🫗

poetry × Docker(マルチステージ)におけるPOETRY_HOMEの固定設定とパス引き継ぎ問題について

2025/02/19に公開

Dockerを用いたマルチステージビルドでは、各ステージが独立した環境となるため、インストール済みのpoetryや依存パッケージのパスが正しく引き継がれない場合があります。特に、curlでpoetryをインストールする際、環境変数で固定のインストール先を設定していないと、実行時に「poetry not found」エラーや依存パッケージの認識エラーが発生する可能性があります。
そこで、環境変数POETRY_HOMEを明示的に固定し、各ステージで同一のパス設定を利用することが重要になります。

POETRY_HOMEを固定するメリット

固定のインストールパスを設定することで、以下のメリットが得られます。

  • 一貫性の確保:
    各ステージで同じ環境変数を利用するため、poetryのインストール先や依存パッケージの配置先が統一され、ステージ間のファイルコピーが容易になります。

  • 将来的な変更への対応:
    Poetryのバージョンアップや内部仕様の変更により、デフォルトのインストール先が変わる可能性がありますが、固定パスを用いることでその影響を回避できます。

グローバルインストール vs ローカル(仮想環境)インストール

グローバルインストールの場合

  • メリット:

    • Dockerコンテナは単一のアプリケーションを実行する専用環境であるため、システム全体に依存パッケージをインストールしても問題が少ない。
    • 仮想環境の管理やアクティベーションが不要となり、Dockerfileがシンプルになる。
  • デメリット:

    • 他のツールとの依存関係の衝突リスクはゼロではないが、コンテナ内で単一アプリケーションを運用する場合は大きな問題にはならない。

    グローバルインストールの場合、必要なのは

    • 依存パッケージがインストールされる /usr/local/lib/python3.9/site-packages
    • 実行可能ファイルが配置される /usr/local/bin
      をランタイムステージに正しくコピーすることです。

ローカル(仮想環境)インストールの場合

  • メリット:

    • プロジェクトごとに依存関係が隔離されるため、環境間での衝突を防げる。
    • 開発環境と本番環境の整合性が高くなる。
  • デメリット:

    • 仮想環境(例: .venv)のディレクトリをランタイムステージにコピーする必要があり、Dockerfileが複雑になる。
    • イメージサイズが大きくなる可能性がある。

解決策の方向性と正しいDockerfile例

以下は、グローバルインストールを前提とした場合の正しいDockerfile例です。

# --- ビルドステージ ---
FROM python:3.9 as builder

# POETRY_HOMEを固定し、PATHに追加する
ENV POETRY_HOME=/opt/poetry

# poetryをcurlでインストール(グローバルインストールのため、仮想環境は作成しない)
RUN curl -sSL https://install.python-poetry.org | python - && \
    poetry config virtualenvs.create false

# PATH に追加
ENV PATH="${POETRY_HOME}/bin:${PATH}"

WORKDIR /app
COPY pyproject.toml poetry.lock ./

# 依存パッケージをグローバル環境にインストール(開発用パッケージを除外)
RUN poetry install --no-dev --no-interaction --no-ansi

# アプリケーションソースコードのコピー
COPY . .

# --- ランタイムステージ ---
FROM python:3.9-slim as runtime

# 同一の環境変数を設定する
ENV POETRY_HOME=/opt/poetry
ENV PATH="${POETRY_HOME}/bin:${PATH}"

WORKDIR /app

# ビルドステージでグローバルにインストールされた依存パッケージと実行ファイルをコピー(POETRY_HOME全体でなくて良い。)
COPY --from=builder /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin

# アプリケーションのソースコードをコピー
COPY --from=builder /app /app

# エントリーポイント(例:uvicornでASGIアプリを起動)
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]

ポイント

  1. 環境変数の統一:
    ビルドステージとランタイムステージの両方で同じPOETRY_HOMEとPATHを設定し、インストール先の一貫性を保ちます。

  2. グローバルインストールの場合:
    Poetryのインストール先として/opt/poetryを固定しても、実際に依存パッケージや実行ファイルは/usr/local/lib/python3.9/site-packagesや/usr/local/binに配置されるため、これらを正しくコピーすることが重要です。
    COPY --from=builder ${POETRY_HOME} ${POETRY_HOME} といった記述は不要です。

  3. ローカル(仮想環境)インストールの場合:
    仮想環境(例: /app/.venv)を利用するなら、そちらをランタイムステージにコピーする必要があります。

まとめ

Dockerのマルチステージビルドにおいては、環境変数POETRY_HOMEを固定することで、将来的な変更への対応やステージ間の一貫性を確保できます。
グローバルインストールを採用する場合、実行ファイルや依存パッケージは通常/usr/local/binや/usr/local/lib/python3.9/site-packagesに配置されるため、これらを正しくランタイムステージに引き継げば、$POETRY_HOME全体をコピーする必要はありません。
環境や要件に応じて、最適なインストール方法とファイルの引き継ぎ方法を選択してください。

詳しくは、GitHubディスカッション
をご参照ください。


https://github.com/orgs/python-poetry/discussions/1879?sort=top

Discussion