はじめに
七尾百合子さん、お誕生日 113日目 おめでとうございます! nikkieです。
よりよい解法がないか気になっていたトピックがあるのですが、着想を得たのでここに記します。
目次
再改訂に至る流れ
私は「小さいは正義」を信奉しているので、これをDockerイメージにも適用したいです。
小さなDockerイメージは取り回しやすい点を好んでいます。
Pythonではマルチステージビルドを採用して、インストーラ向けのPythonパッケージのキャッシュが残らない小さなDockerイメージを得ます。
Python開発で人気のuvについてもマルチステージビルドする方法を考えてきました。
私の思想として、成果物Dockerイメージ1の中にuvは不要なため、uvのドキュメントとは別案にあたるuvを残さないマルチステージビルドを提案しています。
マルチステージビルドの前段でuvを使うため、uvicornのようなコマンドはシバンに#!/app/.venv/bin/pythonと書かれます2。
そのためやむなく成果物イメージにも/app/.venvを用意していました。
このたび成果物イメージに/app/.venvを残さない方法に気づきました。
改訂二版:uvicornコマンドの代わりにuvicorn.run()するPythonスクリプト
要はuvicornコマンドを使わなければよいのです。
uvicornコマンドの代わりにpython main.pyでFastAPIアプリを起動します。
main.pyに追加します。
+if __name__ == "__main__": + import uvicorn + + uvicorn.run(app, host="0.0.0.0", port=8000)
Dockerイメージのマルチステージビルドでは、成果物イメージの中に仮想環境を作る必要はありません。
uvicornモジュールは(シバンにPythonパスが書かれるのと違って)Python処理系がsys.pathからインポートする方法もあるのです。
FROM python:3.12-slim-bookworm WORKDIR /app -RUN python -m venv .venv --without-pip -COPY --from=builder /app/.venv/bin/uvicorn /app/.venv/bin/uvicorn -COPY --from=builder /app/.venv/lib/python3.12/site-packages /app/.venv/lib/python3.12/site-packages +COPY --from=builder /app/.venv/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages COPY main.py . EXPOSE 8000 -CMD ["/app/.venv/bin/uvicorn", "main:app", "--host", "0.0.0.0"] +CMD ["python", "main.py"]
Dockerfile全容
% docker build -t uv-practice-fastapi:0.1.1 . % docker run --rm -p 8000:8000 uv-practice-fastapi:0.1.1
% curl http://127.0.0.1:8000/ | jq '.'
{
"kokoro": "Thursday",
"aki": "Sunday",
"fuka": "Sunday",
"rion": "Thursday",
"subaru": "Saturday",
"masamune": "Saturday",
"ureshino": "Tuesday"
}
成果物イメージから仮想環境を消せたので、私としてはより満足しました。
さらに改善する案が浮かんだ方はご共有いただけましたら幸いです。
別案:python -m uvicorn
main.pyは変更せず、以下でもよいと気づきました。
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
(シバンにPythonパスが書かれたuvicornコマンドではなく、)DockerイメージのPython環境にインストールしたuvicornパッケージを実行しています。
終わりに
uvで環境を管理するFastAPIアプリのDockerイメージ、小さく改善できました!
uvicornコマンド以外にも、uvicornモジュールをimportしたスクリプトや、uvicornパッケージの実行でもASGIサーバが起動できると気づいたのが突破口でした!
全容はこちら:
- Dev containersのように開発中にもDockerイメージを使う場合は、仮想環境が不要なので、仮想環境を取り回しやすくするuvも不要という考えです↩
- 当初の方法はシバンを見落としていました。uv init --appで環境構築して開発中の自作Pythonアプリケーションを含んだDockerイメージをビルドする - nikkie-ftnextの日記↩