はじめに
最近は Python での環境構築に uv を気に入って使っている。
そこで、Streamlit で作成した Web サービスを Docker に囲って出す際に、uv をベースとしたバージョン管理をしつつ公開する方法はどうすればいいかを実際に手を動かしながらやるということをまとめてみる。
uv プロジェクトの作成
まずは uv プロジェクトを作成するところから。
プロジェクト作成
uv init streamlit-uv-docker
streamlit の追加
Streamlit を依存関係に追加する。
uv add streamlit
開発用のモジュールの追加
こちらは、Docker でリリース環境として扱う際には含まないもの。
例えば、フォーマッタやリンタ、テストツールなどをこちらの方にインストールする。
uv add --dev ruff
結果の確認
pyprpoject.toml
pyproject.toml
の中は次のようになっていると思う。
[project]
name = "streamlit-uv-docker"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"streamlit>=1.40.2",
]
[dependency-groups]
dev = [
"ruff>=0.8.1",
]
Streamlit の起動確認
uv run streamlit hello
アプリの作成
それではベースとなるアプリを作成していく。
uv のデフォルト環境は hello.py
がおかれているだけ。
ここに実際に記述していってもいいが、ファイルの増加やファイル名がそもそも適切でないことから階層を作り直す。
root
├─ .venv
├─ .python-version
├─ app
│ └─ app.py
├─ pyproject.toml
├─ README.md
└─ uv.lock
from . import main
main.main()
import streamlit as st
def main():
st.title("Python + uv + Streamlit + Docker!")
さて、ではこの構成で起動してみる。
uv run streamlit run app\app.py
Docker 環境の構築
uv 側は、uv インストール済みの Docker イメージを公開してくれている。
まずはこれを利用することを考える。
Dockerfile の作成
FROM python:3.12-slim-bookworm
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
ADD . /app
WORKDIR /app
RUN uv sync --frozen
CMD ["uv", "run", "streamlit", "run", "app.py"]
uv を地でインストールする
通常のインストールプロセスと同様に uv をインストールする方法もある。
通常の Python 環境等の Dcoker イメージを使用した場合は、以下を上記の COPY 部分の代わりに記述する。
FROM python:3.12-slim-bookworm
# レポジトリ情報を更新 && curl をインストールして有効にしておく
RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates
# 最新のインストーラーをダウンロード
ADD https://astral.sh/uv/install.sh /uv-installer.sh
# インストーラーを使ってインストールし、インストーラーを削除
RUN sh /uv-installer.sh && rm /uv-installer.sh
# PATH 環境変数にパスが通ってるかを確認
ENV PATH="/root/.local/bin/:$PATH"
.dockerignore で同期不要なものに対する対応
上記までローカルで作業していたため 別環境の .venv 環境 が作成されてしまっているので、Docker 処理でそれごとコピーされてしまうのを .dockerignore
ファイルを作成することによって防ぐ。
# Ignore the virtual environment
.venv
TROUBLESHOOT: .venv を ignore しないと?
次のようなエラーが発生する。.venv は、プラットフォームの依存も含めた仮想環境なのを忘れずに対処をしよう。
=> ERROR [stage-0 5/5] RUN uv sync --frozen 0.3s
------
> [stage-0 5/5] RUN uv sync --frozen:
0.247 error: Project virtual environment directory `/app/.venv` cannot be used because it is not a valid Python environment (no Python executable was found)
------
Dockerfile:9
--------------------
7 | WORKDIR /app
8 |
9 | >>> RUN uv sync --frozen
10 |
11 | CMD ["uv", "run", "streamlit", "run", "main.py"]
--------------------
ERROR: failed to solve: process "/bin/sh -c uv sync --frozen" did not complete successfully: exit code: 2
Docker イメージのビルド
Docker イメージをビルドする。
今回はわかりやすい様に、タグ (-t
) を streamlit-uv
として打った。
docker build . -t streamlit-uv
実際に動かしてみる
Streamlit はデフォルトポートは、8501
となっているので、必要に応じてポートフォワーディングする。Ex) -p 80:8501
起動するコンテナ名 (--name
) は st-uv
として名付けて起動。
docker run -p 80:8501 --name "st-uv" streamlit-uv
実行すると、次の様に Docker コンテナが動作する。
localhost
や localhost:80
等でアクセスする事で、次の様にアクセスできる事が確認できる。
ついでに Docker Compose も用意
実際に環境をデプロイする場合は Docker Compose もまとめて用意する事も多い。
さらには、開発環境もデプロイ環境の Docker 内を直接書き換えながら開発したいと思うので、次のように項目を追加しておく。
services:
example:
build: .
ports:
- 80:8501
開発環境の Docker Compose 設定も追加
# 省略
develop:
watch:
- action: sync
path: .
target: /app
ignore:
- .venv/
- action: rebuild
path: ./pyproject.toml
すると、
docker compose watch
コマンドを実行する事で、ローカル環境のファイルの変更を同期しながら開発できる。
さいごに
意外と、uv ってどう入れるんやろうな?uv 周りとか自分で Docker イメージ作る時の配慮とか必要なのかな?という疑問だけがあったが、それもこうして一通りやってみたら、めちゃくちゃちゃんとサポートされてる!という気付きがあった。
とりあえず、uv で管理しているプロジェクトも、問題なくデプロイ環境への導入はいけそうだ。
動作も警戒なので、今後も更新を注視したい。