はじめに
今宵はfantastic night🌟1 nikkieです。
半年くらい前に書いた、uv init --appしたプロジェクトでDockerイメージを作る記事の改訂版です。
目次
- はじめに
- 目次
- uv init --appしたプロジェクトでDockerイメージを作る
- Dockerイメージでuvicornコマンドが動かない
- 依存ライブラリのコマンドが使えるように注意を払った改訂版
- 終わりに
uv init --appしたプロジェクトでDockerイメージを作る
Pythonプロジェクトの雛形を作るコマンドuv initは、--appを指定するとApplicationとしてプロジェクトを作ります。
https://docs.astral.sh/uv/concepts/projects/init/#applications
Application projects are suitable for web servers, scripts, and command-line interfaces.
uv init --appの例は、FastAPIのアプリケーションを作るときです。
過去の記事では、uv init --appしたプロジェクトで、マルチステージビルドでuvを残さずにDockerイメージを作りました。
- マルチステージビルドで成果物イメージからuvは除く
- 依存ライブラリのコピーと、アプリケーションの配置をした
PythonのDockerイメージのマルチステージビルドはこちらをどうぞ
uv init --appしたFastAPIアプリケーションをDockerイメージにする中で、改訂のきっかけがありました。
Dockerイメージでuvicornコマンドが動かない
% docker --version Docker version 27.5.1-rd, build 0c97515
Pythonのマルチステージビルドにならって、最初は以下のようにDockerfileを書きました。
ビルドはできます。
% docker build -t uv-practice-fastapi:0.0.1 .
しかし、docker runでコンテナを起動できません。
% docker run --rm -p 8000:8000 uv-practice-fastapi:0.0.1 exec /usr/local/bin/uvicorn: no such file or directory
このメッセージの理由は、シバンによると理解しました。
% docker run -it --rm -p 8000:8000 uv-practice-fastapi:0.0.1 bash
root@e37442a7b681:/app# cat /usr/local/bin/uvicorn
#!/app/.venv/bin/python
# -*- coding: utf-8 -*-
import sys
from uvicorn.main import main
if __name__ == "__main__":
if sys.argv[0].endswith("-script.pyw"):
sys.argv[0] = sys.argv[0][:-11]
elif sys.argv[0].endswith(".exe"):
sys.argv[0] = sys.argv[0][:-4]
sys.exit(main())
# ls /app/.venv/bin/python
ls: cannot access '/app/.venv/bin/python': No such file or directory
(PATHに配置した)uvicornコマンドを/app/.venv/bin/pythonで実行しようとしますが、そこにPython処理系はないので実行できず落ちています。
以前の記事では、依存ライブラリのコマンドを使う要素がなかったため、これを見落としていました。
依存ライブラリのコマンドが使えるように注意を払った改訂版
※私の中ではめちゃめちゃ美しく解決できてはいないので、アイデアをお持ちの方がいたらぜひ話しましょう
uvicornコマンドのファイルに書かれたシバンを書き変えることを諦め、/app/.venv/bin/pythonを用意するアプローチを取りました2。
FROM python:3.12-slim-bookworm +WORKDIR /app +RUN python -m venv .venv --without-pip -COPY --from=builder /app/.venv/bin/uvicorn /usr/local/bin/uvicorn +COPY --from=builder /app/.venv/bin/uvicorn /app/.venv/bin/uvicorn -COPY --from=builder /app/.venv/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages +COPY --from=builder /app/.venv/lib/python3.12/site-packages /app/.venv/lib/python3.12/site-packages -WORKDIR /app COPY main.py . EXPOSE 8000 -CMD ["uvicorn", "main:app", "--host", "0.0.0.0"] +CMD ["/app/.venv/bin/uvicorn", "main:app", "--host", "0.0.0.0"]
WORKDIR(/app)の下に仮想環境を作り、そこにbuilderイメージから一式コピーしました。
全容はこちらで確認できます3。
% docker build -t uv-practice-fastapi:0.1.0 . % docker run --rm -p 8000:8000 uv-practice-fastapi:0.1.0
% curl http://127.0.0.1:8000/ | jq '.' { "kokoro": "Sunday", "aki": "Thursday", "fuka": "Thursday", "rion": "Sunday", "subaru": "Tuesday", "masamune": "Tuesday", "ureshino": "Friday" }
ここで作る仮想環境にpipは不要なので、pipなしの仮想環境を作る知識が役に立ちましたね。
uvは含みませんが、uvが作ったディレクトリ構造が残っているのが、個人的にきれいに解決できていないと感じるポイントです。
手を動かす中で、Pythonを始めたときに知った「仮想環境はPC内を移動できない」というのはなぜかが腹落ちしていきました。
終わりに
uvで環境構築したPythonアプリケーションのDockerイメージについて、依存ライブラリのコマンドが使えるように改訂しました。
- builderステージにてuvでインストールするときに、依存ライブラリのコマンドにuvの仮想環境でシバンが書き込まれる
- マルチステージビルドで依存ライブラリや依存ライブラリのコマンドをコピーする際、書かれたシバンに注意して配置する
シバンは私には盲点でしたが、体験して仮想環境がmvで動かせない理由を納得できました