poetry × Docker(マルチステージ)におけるPOETRY_HOMEの固定設定とパス引き継ぎ問題について
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 /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages
COPY /usr/local/bin /usr/local/bin
# アプリケーションのソースコードをコピー
COPY /app /app
# エントリーポイント(例:uvicornでASGIアプリを起動)
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
ポイント
-
環境変数の統一:
ビルドステージとランタイムステージの両方で同じPOETRY_HOMEとPATHを設定し、インストール先の一貫性を保ちます。 -
グローバルインストールの場合:
Poetryのインストール先として/opt/poetryを固定しても、実際に依存パッケージや実行ファイルは/usr/local/lib/python3.9/site-packagesや/usr/local/binに配置されるため、これらを正しくコピーすることが重要です。
COPY --from=builder ${POETRY_HOME} ${POETRY_HOME} といった記述は不要です。 -
ローカル(仮想環境)インストールの場合:
仮想環境(例: /app/.venv)を利用するなら、そちらをランタイムステージにコピーする必要があります。
まとめ
Dockerのマルチステージビルドにおいては、環境変数POETRY_HOMEを固定することで、将来的な変更への対応やステージ間の一貫性を確保できます。
グローバルインストールを採用する場合、実行ファイルや依存パッケージは通常/usr/local/binや/usr/local/lib/python3.9/site-packagesに配置されるため、これらを正しくランタイムステージに引き継げば、$POETRY_HOME全体をコピーする必要はありません。
環境や要件に応じて、最適なインストール方法とファイルの引き継ぎ方法を選択してください。
詳しくは、GitHubディスカッション
をご参照ください。
Discussion