Linux 使いになりたい人のための入門版 Python 開発コンテナーの使い方 - 2024年度版
はじめに
ここでは Linux 使いになりたい人向けに入門版 Python 開発コンテナーの使い方について説明します。入門版の開発コンテナーを対象とするので、devcontainer.json
を使った本格的な開発コンテナーについては説明していませんので注意してください。
ここで、「Linux 使いになりたい人」とタイトルにつけているのは、この記事では、簡単なシェルスクリプトやコマンドを使う方法で説明している部分が多いからです。最近は Infrastructure as Code (IaC) という用語があることからわかるように、「コードを使用して IT インフラの管理やプロビジョニングを自動化すること」が重要視されています。こういったことを意識からすると、できるだけ GUI に頼らずに環境構築したり、開発作業ができるようになりたいところでしょう。
自分は、このスキルを身につけるのには、「Linux 使いになる」というのが近道だと考えています。IaC 以外でも Linux を使えること自体が開発者としてアドバンテージがあるはずです。ということで、今は Linux 使いでなくても、将来 Linux 使いになりたいと考えている人に、実用的なシェルスクリプトを含むドキュメントを提供することで役に立てればと思い、「Linux 使いになりたい人のため」シリーズの記事を執筆しています。
本題に戻ります。動作確認は Linux 環境を使っているため、Windows や macOS とは違っている場合があります。とはいえ、基本的には Docker が動作する環境であれば動くはずです。
この記事の対象者の前提は下記となります。
- VS Code を使った開発コンテナーに興味があること
- Visual Studio Code(VS Code)が使えること
- Docker が使えること
- Python の開発について基本的なことを知っていること
- Docker Compose の基本的な使い方を知っていること
- Linux の基本的な使い方を知っていること
使用する環境は下記です。
-
Visual Studio Code
- Docker 拡張機能: ms-azuretools.vscode-docker
- Dev Containers 拡張機能: ms-vscode-remote.remote-containers
- Docker Desktop
Docker Desktop については、これに同梱されている Docker Engine と Docker Compose があれば大丈夫です。
動作の確認については Ubuntu 22.04 で行っています。おそらく WSL2 Ubuntu 22.04 でも動作します。
説明にあたっては、Docker ホストで実行するスクリプトも用意してあり、それは Ubuntu 22.04 を前提としています。ただし、単純なものなので、Windows や macOS でも手動で似たような処理を実行することは容易なはずです。
開発コンテナーについて
この記事では、開発コンテナーといったら Developing inside a Container using Visual Studio Code Remote Development で説明されているものを指します。コンテナーの中で VS Code Server を動かして、ユーザーは VS Code の画面経由でコンテナーのコンピュータリソースを使って開発作業をします。
開発コンテナーを使うメリットのひとつには、開発者が使う環境の差が基本的になくなることがあります。開発者が、使用する Docker イメージの作り方、VS Code での開発コンテナーの使い方を知っていれば、開発環境の構築時間は非常に少なくて済むようになります。
本格的な開発コンテナーは devcontainer.json
を使ったものになりますが、そこまで高機能なものを使わなくても開発コンテナーは便利なものです。VS Code の Dev Container 拡張機能をインストールすると、Visual Studio Code をアタッチする
というメニューが使えるようになり、これを使うとコンテナーの中で VS Code Server が自動で稼働して、ユーザーは VS Code の画面経由でコンテナーのコンピュータリソースを使って開発作業をすることができるようになります。
ということで、ここでは、Visual Studio Code をアタッチする
というメニューで使う開発コンテナーのことを「入門版の開発コンテナー」と呼んでいます。そして、これを使って開発をするときは、どんな感じで作業をすることになるのかを、この記事では説明しています。
自分で開発コンテナーを使いながら、開発をしてみるとわかるのですが、開発をしていると、最初に用意した環境を改良したくなります。アプリの開発と、開発環境のベースとなる開発コンテナーの両方について、開発を進めていくことになるので、効率よく作業をするには、いろいろとノウハウがあります。そういったことは、経験してみないとわからないことが多いかと思います。
本記事では、次のサイクルをまわしながら作業します。それぞれのタイミングで何が必要になるかは、大体同じになるはずなので、参考になることが多いはずです。
- 開発コンテナーの用意
- アプリの開発
- 開発コンテナー用 Docker イメージの作成(1. へ戻る)
開発コンテナーでの Python 開発
本記事では、具体的に使ってみるプログラミング言語として、Python を選択しています。人気のあるプログラミング言語で、開発コンテナーでの使用方法に興味がある人も多いことでしょう。ここでは、Web サービス用のアプリをプログラミング言語 Python で開発することになって、そこで使う技術選定をしていると想定します。
この場合、パッケージ管理に何を使うか、Web フレームワークはどれにするか、DB 関連はどうするか、といったことを順番に検討することになるでしょう。今回、この記事を執筆するに当たり、筆者の場合は次のような検討をしました。
パッケージ管理について、普段は pip を使っていますが、Poetry、や Rye にも興味があり、機会があれば使ってみたいと考えていました。
次に、Python の Web フレームワークとして有名なのものとして、FastAPI、Flask、Django などがあります。この中で FastAPI はきちんと使ったことがなく、機会があれば使ってみたいと考えていました。
DB 関連では SQLAlchemy、Prisma Client Python などがあります。Django を使うなら、Django Models を使うことになります。Django Models は、いわゆる Django ORM の機能を提供するものです。SQLAlchemy は使ったことがあり、Prisma は Node.js のアプリ開発時に使ったことがありますが、Prisma Client Python は使ったことがありません。Prisma と Python の組み合わせには興味があります。
ということで、このような選択肢があるなかで、今回は Poetry、FastAPI、Prisma を使ってみることにしました。ここでは、これらが使える開発環境を開発コンテナーで用意します。
開発コンテナーを用意するにあたっては、実際の開発作業について考慮しながらとなります。必要になった時点で環境も用意するのが効率が良いはずなので、大体、次のような順番で作業を進めます。
- プロジェクト用ファイルの作成
- FastAPI アプリの雛形の用意
- 最初の FastAPI アプリの動作確認
- バージョン管理への対応
- 開発で使う Docker イメージのビルドと起動
- FastAPI 用プログラムの開発
- ターミナルのプロンプト改善
- 開発したプログラムを Dcoker イメージへ反映
- Prisma を使うアプリの作成
- 開発した Prisma 対応版プログラムを Dcoker イメージへ反映
実際に動くプログラムを用意するので、FastAPI と Prisma の知識があった方が理解はしやすいと思いますが、ここでは「Python の開発コンテナーをどのようにして使うか」の紹介を主目的とするので、FastAPI と Prisma についての説明は最低限とします。
なお、使用するコードは https://github.com/hiro345g/dcfwd のリポジトリに用意してあります。ただし、用意してあるコードは、説明用のものなので、できるだけ単純なコードにしてあります。また、機能も限られているので、複雑な処理はできません。説明用途以外では使いにくいものもあるので、承知しておいてください。
ここでは、https://github.com/hiro345g/dcfwd のリポジトリに含まれる devcon-python-fastapi-prisma
ディレクトリーを ${PROJ_DIR}
と表現します。
たとえば、${PROJ_DIR}
として ${HOME}/workspace/devcon-python-fastapi-prisma
ディレクトリーを用意する場合は次のようにします。
cd ${HOME}/workspace
curl -sLO https://github.com/hiro345g/dcfwd/archive/refs/heads/main.zip
unzip main.zip
mv dcfwd-main/devcon-python-fastapi-prisma .
rm -fr main.zip dcfwd-main/
ちなみに、環境変数 ${PROJ_DIR}
を次のように設定しておくと、この後のコマンド入力が楽になります。
cd ${HOME}/workspace/devcon-python-fastapi-prisma
PROJ_DIR=$(pwd)
環境変数 ${PROJ_DIR}
の値を確認するには、echo
コマンドを使います。${PROJ_DIR}
は $PROJ_DIR
と {}
を省略して参照することもできます。
echo ${PROJ_DIR}
実際の実行例は次のようになります。この例では ${HOME}
が /home/user001
の場合です。
$ echo ${PROJ_DIR}
/home/user001/workspace/devcon-python-fastapi-prisma
$ echo $PROJ_DIR
/home/user001/devcon-python-fastapi-prisma
ディレクトリーの構成
ここで、用意するディレクトリーの構成について説明しておきます。開発にあたっては、次のようなディレクトリー構成でファイルを用意することにします。ここで例として作成するアプリの名前は app001
とすることにします。
devcon-python-fastapi-prisma/
├── build-image/devcon-python-fastapi-prisma/ ... Python アプリ学習用の開発コンテナービルド用
│ ├── compose.yaml ... 開発コンテナー用 compose.yaml
│ └── resource/ ... 初期化時に使うリソースファイルを置く
├── dev/devcon-python-fastapi-prisma/ ... 開発環境用
│ ├── app001/ ... Python アプリのプロジェクト
│ ├── compose.yaml ... 開発コンテナー用 compose.yaml
│ └── script/ ... Docker ホスト側で使用するスクリプト
└── share/ ... Python アプリ学習用の開発コンテナーと Docker ホスト側とで共有
また、開発コンテナー用の Docker イメージは、開発対象のアプリのバージョンに合わせてバージョンをつけることにします。つまり、アプリ app001
のバージョン 0.1 を開発するときの Docker イメージは devcon-python-fastapi-prisma:0.1
とします。また、このイメージの完成形では、Docker コンテナーを起動したら app001
のバージョン 0.1 が実行されるようにします。
Docker イメージについては build-image
ディレクトリーに compose.yaml
ファイルを用意してビルドすることにして、開発時に使用する compose.yaml
ファイルは dev
ディレクトリーに置いて、分けておきます。これは、開発コンテナー起動中に Dockerfile
や compose.yaml
ファイルを編集したくなることがあるので、事前に分けておいたほうが管理しやすいからです。
なお、事前に用意してある devcon-python-fastapi-prisma
ディレクトリーは次のようになっています。サンプルとして sample
ディレクトリーに各バージョンのコードを入れてあります。一番最初に用意する開発コンテナー用 compose.yaml
については、特殊なので v0.0_init
としてあります。
devcon-python-fastapi-prisma/
├── README.md ... 説明ファイル
├── build-image/devcon-python-fastapi-prisma/ ... Python アプリ学習用の開発コンテナービルド用
│ └── resource/ ... 初期化時に使うリソースファイルを置く
├── dev/devcon-python-fastapi-prisma/ ... ここに compose.yaml を用意して Python アプリ学習用の開発コンテナーを使用
│ └── script/ ... Docker ホスト側で使用するスクリプト
├── sample/ ... サンプル
│ ├── build-image/ ... Python アプリ学習用の開発コンテナービルド用
│ │ ├── v0.0_init/devcon-python-fastapi-prisma ... 最初のビルド用
│ │ ├── v0.1/devcon-python-fastapi-prisma ... v0.1 ビルド用
│ │ ├── v0.2/devcon-python-fastapi-prisma ... v0.2 ビルド用
│ │ └── v0.3/devcon-python-fastapi-prisma ... v0.3 ビルド用
│ ├── python_src/ ... Python プログラム
│ │ ├── v0.0/app001 ... v0.0 版 Python プログラム
│ │ ├── v0.1/app001 ... v0.1 版 Python プログラム
│ │ ├── v0.2/app001 ... v0.2 版 Python プログラム
│ │ └── v0.3/app001 ... v0.3 版 Python プログラム
│ ├── compose/ ... Python アプリ学習用の開発コンテナー起動用サンプル
│ │ ├── v0.1/devcon-python-fastapi-prisma/compose.yaml
│ │ ├── v0.2/devcon-python-fastapi-prisma/compose.yaml
│ │ ├── v0.2-demo/devcon-python-fastapi-prisma/compose.yaml
│ │ └── v0.3/devcon-python-fastapi-prisma/compose.yaml
│ └── sample.env ... .env のサンプル
└── share/ ... Python アプリ学習用の開発コンテナーと Docker ホスト側とで共有
プロジェクトの用意
最初に開発用プロジェクトを用意します。作業にあたっては開発コンテナーを使います。
プロジェクト用ファイルの作成
Python 用アプリの開発を始めるにあたり、最初に Poetry のプロジェクトファイルを作成します。Poetry のプロジェクトを作成するにあたっては、開発時に使う Docker コンテナーに近いものを使って作業するのが確実です。
ここでは、${PROJ_DIR}/build-image/devcon-python-fastapi-prisma/
に Dockerfile
と compose.yaml
を用意します。
Dockerfile
については次のようにします。Docker Hub の公式の Python イメージを使い、poetry
パッケージをインストール済みの Docker イメージを用意することにしました。
FROM python:3.10.14-bookworm
ENV PYTHONUNBUFFERED=1
# poetry
RUN curl -sSL https://install.python-poetry.org | python -
ENV PATH /root/.local/bin:$PATH
これをビルドするための compose.yaml
も用意します。このファイルを用意しておくと VS Code の Docker 拡張機能からビルドもできますし、ビルドするイメージ名の指定なども、このファイルに書いておくことができます。こうしておくことで、Docker イメージ作成についての情報をバージョン管理して Git リポジトリへ記録できます。
name: devcon-python-fastapi-prisma
services:
devcon-python-fastapi-prisma:
build: .
image: devcon-python-fastapi-prisma:0.1
container_name: devcon-python-fastapi-prisma
hostname: devcon-python-fastapi-prisma
tty: true
作成した Docker イメージのクイック動作確認にも使えるのと、この後に使う compose.yaml
の雛形にもなっているので、ビルド時にはなくても良い container_name:
や hostname:
を指定してます。
Dockerfile
と compose.yaml
ファイルを用意したらビルドします。
DC_DIR_PATH=${PROJ_DIR}/build-image/devcon-python-fastapi-prisma
DC_FILE_PATH=${DC_DIR_PATH}/compose.yaml
docker compose -f ${DC_FILE_PATH} build
実行結果は次のようになります。
$ docker compose -f ${DC_FILE_PATH} build
[+] Building 1.1s (6/6) FINISHED docker:default
=> [devcon-python-fastapi-prisma internal] load build definition from Dockerfile 0.0s
(略k)
=> => writing image sha256:a22210aa32e4f84a397a2175c5b446f51d270af3b009d9adfc79943e6302407 0.0s
=> => naming to docker.io/library/devcon-python-fastapi-prisma:0.1 0.0s
用意した Docker イメージ devcon-python-fastapi-prisma:0.1
について、簡単に動作確認します。ビルドで使った compose.yaml
で devcon-python-fastapi-prisma
コンテナーを用意して ls
コマンドを実行してみます。
docker compose -f ${DC_FILE_PATH} run --rm devcon-python-fastapi-prisma ls
実行結果は次のようになります。
$ docker compose -f ${DC_FILE_PATH} run --rm devcon-python-fastapi-prisma ls
[+] Creating 1/0
(略)
bin boot dev etc home lib lib64 media mnt (略)
動作したら、自動で作成された devcon-python-fastapi-prisma_default
を破棄するため、docker compose down
を実行します。Docker Compose のプロジェクト名が devcon-python-fastapi-prisma
なので、-p
オプションで指定します。-f
だと起動時に使った compose.yaml
のパス指定が必要ですが、こちらを使うと Docker Compose のプロジェクト名で済むので楽です。
docker compose -p devcon-python-fastapi-prisma down
実行結果は次のようになります。
$ docker compose -p devcon-python-fastapi-prisma down
[+] Running 1/1
✔ Network devcon-python-fastapi-prisma_default
(略)
それでは、Docker イメージ devcon-python-fastapi-prisma:0.1
を使って、開発プロジェクト用ファイル ${PROJ_DIR}/dev/devcon-python-fastapi-prisma/compose.yaml
を作成します。
ここで使う compose.yaml
は次のようになります。コンテナーの中で作成したファイルを保持するために devcon-python-fastapi-prisma-workspace-data
ボリュームを用意して、そこへ保存するようにします。また、working_dir: /workspace/app001
で作業用ディレクトリーを指定します。なお、この指定があると、コンテナー起動時に /workspace/app001
ディレクトリーがない場合は、自動で作成します。
name: devcon-python-fastapi-prisma
services:
devcon-python-fastapi-prisma:
image: devcon-python-fastapi-prisma:0.1
container_name: devcon-python-fastapi-prisma
hostname: devcon-python-fastapi-prisma
tty: true
working_dir: /workspace/app001
ports:
- 127.0.0.1:8000:8000
volumes:
- workspace-data:/workspace
volumes:
workspace-data:
name: devcon-python-fastapi-prisma-workspace-data
まずは、この compose.yaml
ファイルを使う docker compose
コマンドで、devcon-python-fastapi-prisma:/workspace/app001
(devcon-python-fastapi-prisma コンテナーの /workspace/app001
ディレクトリー)に pyproject.toml
を作成します。
使用する Docker イメージのエントリーポイントを --entrypoint
オプションで上書きすることで、Docker コンテナー起動時に実行するコマンドを変更しています。
ここでは、poetry init
コマンド実行時に --dependency
オプションで依存するパッケージを指定しています。ここでは依存パッケージとして fastapi
と uvicorn[standard]
を指定するので、生成される pyproject.toml
に、このアプリがこれらに依存するという情報が書き込まれます。
docker compose run --rm \
--workdir /workspace/app001 --entrypoint "\
/root/.local/bin/poetry init \
--no-interaction \
--name app001 \
--dependency fastapi \
--dependency uvicorn[standard] \
" devcon-python-fastapi-prisma
実際に実行すると次のようになります。カレントディレクトリーを ${PROJ_DIR}/dev/devcon-python-fastapi-prisma
としてから実行が必要です。
$ cd ${PROJ_DIR}/dev/devcon-python-fastapi-prisma
$ docker compose run --rm \
--workdir /workspace/app001 --entrypoint "\
/root/.local/bin/poetry init \
--no-interaction \
--name app001 \
--dependency fastapi \
--dependency uvicorn[standard] \
" devcon-python-fastapi-prisma
[+] Creating 1/0
✔ Network devcon-python-fastapi-prisma_default Created 0.1s
Using version ^0.110.2 for fastapi
Using version ^0.29.0 for uvicorn
続いて、カレントディレクトリーを変えずに devcon-python-fastapi-prisma
コンテナーで poetry install
を実行します。これで、依存関係にあるパッケージのバージョンを固定する poetry.lock
ファイルが作成されます。
docker compose run --rm \
--workdir /workspace/app001 --entrypoint "\
/root/.local/bin/poetry install --no-root\
" devcon-python-fastapi-prisma
実際に実行すると次のようになります。
$ docker compose run --rm \
--workdir /workspace/app001 --entrypoint "\
/root/.local/bin/poetry install --no-root\
" devcon-python-fastapi-prisma
Creating virtualenv app001-ROkNicwB-py3.10 in /root/.cache/pypoetry/virtualenvs
Updating dependencies
Resolving dependencies... (2.7s)
Package operations: 19 installs, 0 updates, 0 removals
- Installing exceptiongroup (1.2.1)
- Installing idna (3.7)
- Installing sniffio (1.3.1)
- Installing typing-extensions (4.11.0)
- Installing annotated-types (0.6.0)
- Installing anyio (4.3.0)
- Installing pydantic-core (2.18.1)
- Installing click (8.1.7)
- Installing h11 (0.14.0)
- Installing httptools (0.6.1)
- Installing pydantic (2.7.0)
- Installing python-dotenv (1.0.1)
- Installing pyyaml (6.0.1)
- Installing starlette (0.37.2)
- Installing uvloop (0.19.0)
- Installing watchfiles (0.21.0)
- Installing websockets (12.0)
- Installing fastapi (0.110.2)
- Installing uvicorn (0.29.0)
Writing lock file
以上、pyproject.toml
と poetry.lock
のファイルを用意できたら、これらを含む devcon-python-fastapi-prisma:/workspace/app001
を Docker ホストへコピーします。まずは docker compose up
コマンドでコンテナーを起動します。-d
は --detach
の略で、ターミナルからデタッチした状態のデタッチモードでコンテナーを起動します。その結果、コンテナーのプロセスは起動後にバックグラウンドで処理されることになります。
docker compose up -d
実際に実行すると次のようになります。ここでは、念の為 cd
コマンドで compose.yaml
ファイルがあるディレクトリーをカレントディレクトリーにしてから実行しています。
$ cd ${PROJ_DIR}/dev/devcon-python-fastapi-prisma
$ docker compose up -d
[+] Running 1/2
⠸ Network devcon-python-fastapi-prisma_default Created 0.1s
✔ Container devcon-python-fastapi-prisma Started 0.3s
コンテナーが起動したことを docker compose ls
コマンドで確認します。grep
コマンドとパイプライン(|
)を組み合わせて、devcon-python-fastapi-prisma
コンテナーの状態を見ます。running(1)
となっていれば大丈夫です。
docker compose ls | grep devcon-python-fastapi-prisma
実際に実行すると次のようになります。
$ docker compose ls | grep devcon-python-fastapi-prisma
devcon-python-fastapi-prisma running(1) (略)
コンテナーの起動が確認できたら、docker compose cp
コマンドでコンテナー内の /workspace/app001
をコピーします。
docker compose cp devcon-python-fastapi-prisma:/workspace/app001 .
実際に実行すると次のようになります。
$ docker compose cp devcon-python-fastapi-prisma:/workspace/app001 .
[+] Copying 1/0
✔ devcon-python-fastapi-prisma copy devcon-python-fastapi-prisma:/workspace/app001 to (略)
コンテナーを停止します。
docker compose down
実際に実行すると次のようになります。
$ docker compose down
[+] Running 2/2
✔ Container devcon-python-fastapi-prisma Removed 10.3s
✔ Network devcon-python-fastapi-prisma_default Removed 0.3s
以上で、次のように ${PROJ_DIR}/dev/devcon-python-fastapi-prisma/app001/
が用意されます。
app001/
├── poetry.lock
└── pyproject.toml
手元で生成されたものを ${PROJ_DIR}/sample/python_src/v0.0/app001
に用意しました。参考にしてください。
ここで用意された pyproject.toml
は Python の開発プロジェクト用ファイルで次のようになっているはずです。[tool.poetry.dependencies]
を見ると、このプロジェクトは fastapi と uvicorn パッケージに依存していることがわかります。
[tool.poetry]
name = "app001"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.10"
fastapi = "^0.110.2"
uvicorn = {extras = ["standard"], version = "^0.29.0"}
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
pyproject.toml
については、authors
などはデフォルトのままになっています。必要に応じて編集してください。
poetry.lock
は Python の開発で使うパッケージの管理用ファイルになります。pyproject.toml
のパッケージ指定を元にして、実際にインストールしたもののバージョン情報を記録するファイルになります。
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
(略)
[[package]]
name = "fastapi"
version = "0.110.2"
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
optional = false
python-versions = ">=3.8"
files = [
{file = "fastapi-0.110.2-py3-none-any.whl", hash = "sha256:239403f2c0a3dda07a9420f95157a7f014ddb2b770acdbc984f9bdf3ead7afdb"},
{file = "fastapi-0.110.2.tar.gz", hash = "sha256:b53d673652da3b65e8cd787ad214ec0fe303cad00d2b529b86ce7db13f17518d"},
]
(略)
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
content-hash = "e6d9b012f054c6abafe974815f9535d0c4dcd10676cf4fb74c1d89d69371b3b6"
FastAPI アプリの雛形の用意
次に FastAPI アプリの雛形となる Python のコードを app001
に用意します。VS Code で使うワークスペース用ファイルも追加します。devcon-python-fastapi-prisma
コンテナーを起動して、最初の開発コンテナーとして利用します。
cd ${PROJ_DIR}/dev/devcon-python-fastapi-prisma
docker compose up -d
コンテナーの用意ができたら、続けて VS Code を起動して、次の手順で devcon-python-fastapi-prisma
コンテナーへ VS Code をアタッチします。
- Docker 拡張機能の画面を表示
- 「CONTAINERS」の一覧にある
devcon-python-fastapi-prisma
コンテナーをマウス右クリック - 表示されるメニューの「Visual Studio Code をアタッチする」をクリック
これ以降、この操作を「VS Code をアタッチ」と表現します。また、VS Code をアタッチしたコンテナー用の VS Code の画面を「コンテナー用 VS Code の画面」と表現します。たとえば、「devcon-python-fastapi-prisma
コンテナー用 VS Code の画面」は、devcon-python-fastapi-prisma
コンテナーへ VS Code をアタッチしたときに表示される VS Code の画面のことを意味します。
VS Code をアタッチすると、devcon-python-fastapi-prisma
コンテナー用 VS Code の画面が表示されます。その画面で、/workspace/app001
を開いて FastAPI 用のプログラムを作成します。
-
devcon-python-fastapi-prisma
コンテナー用 VS Code の画面のメニューで「ファイル」-「フォルダを開く」をクリック - 入力欄に
/workspace/app001
を指定して「OK」をクリック
ここで作成する app001
のディレクトリーの内容は次のようになります。poetry.lock
と pyproject.toml
はすでに用意されているはずです。
/workspace/app001/
├── api
│ ├── __init__.py
│ └── main.py
├── .gitignore
├── app001.code-workspace
├── init_venv.sh
├── poetry.lock
└── pyproject.toml
最初に init_venv.sh
を用意します。開発コンテナーに VS Code をアタッチしたとき、VS Code が Python のインタプリターを認識できるように Python 仮想環境を用意するときに使います。
#!/bin/sh
# 現在の Python 仮想環境があるなら、それを削除
current_venv=$(poetry env list | awk '{print $1}')
if [ "x${current_venv}" != "x" ]; then
poetry env remove ${current_venv}
fi
# プロジェクト直下に Python 仮想環境を作成
poetry config virtualenvs.in-project true
poetry install --no-root
これを作成してから実行すると、/workspace/app001/.venv
に Python 仮想環境が用意されます。
sh /workspace/app001/init_venv.sh
次に VS Code 用のワークスペースファイルを用意します。ここで用意する app001.code-workspace
では、設定として python.defaultInterpreterPath
へ先程用意した Python 仮想環境用の python
コマンドのパスを指定します。また、推奨拡張機能に Python 拡張機能(ms-python.python
)を指定します。
{
"folders": [
{
"path": "."
}
],
"settings": {
"python.defaultInterpreterPath": "/workspace/app001/.venv/bin/python"
},
"extensions": {
"recommendations": [
"ms-python.python"
]
}
}
このワークスペースファイルを作成すると、VS Code の画面に「ワークスペースを開く」ボタンが表示されるので、クリックします。ワークスペースが開いた時に、Python 拡張機能をインストールするように通知が表示される場合は、指示に従ってインストールします。
これで devcon-python-fastapi-prisma
コンテナー用 VS Code の画面で Python プログラムの開発をするために必要な最低限の環境が用意できます。
次に /workspace/app001/api/__init__.py
を用意します。これは、ファイルがあれば良く、中身は空のものになるので、コマンドで作成します。
mkdir /workspace/app001/api
touch /workspace/app001/api/__init__.py
次に /workspace/app001/api/main.py
を用意します。ここでは、Web ブラウザで /hello
のパスへアクセスすると {"message": "Hello FastAPI!"}
の JSON データを返す単純なものとします。具体的なコードは次のようになります。
from fastapi import FastAPI
app = FastAPI()
@app.get("/hello")
async def hello():
return {"message": "Hello FastAPI!"}
最初の FastAPI アプリの動作確認
動作確認をするために、uvircorn
を使って api/main.py
に用意した FastAPI オブジェクト app
を動かしてみます。ポート番号 8000 で、すべてのネットワークインタフェース上で接続待機させるために、次のコマンドを実行します。
/root/.local/bin/poetry run --directory /workspace/app001 \
uvicorn api.main:app --host 0.0.0.0 --port 8000 --reload
devcon-python-fastapi-prisma
コンテナー用 VS Code の画面のメニューで「ターミナル」-「新しいターミナル」をクリックします。これで新しく用意されたターミナルで curl
コマンドで http://localhost:8000/hello へアクセスします。出力結果へ改行コードを追加するためにオプション -w'\n'
をつけると、結果とプロンプトの区別がつきやすくなります。
curl -w'\n' http://localhost:8000/hello
これで {"message": "Hello FastAPI!"}
が表示されたら、動作確認できたということになります。
実際にコマンドを実行すると、次のような表示となります。
# curl -w'\n' http://localhost:8000/hello
{"message":"Hello FastAPI!"}
動作確認ができたら、uvicorn
を実行していたターミナルでは Ctrl+C
(CtrlとCのキーを同時入力)して、uvicorn
を停止させます。
バージョン管理への対応
開発コンテナーの中でもファイルのバージョン管理をしたいところです。最初に /workspace/app01/.gitignore
ファイルを用意しておきます。
__pycache__
.venv/
それから git init
コマンドで /workspace/app001
を Git リポジトリにします。また、更新をするときのユーザー情報も登録しておきます。ユーザー情報は例としてメールアドレス user001@example.jp
、ユーザー名 user001
を指定しています。実際に使うものを指定してください。
git init /workspace/app001
git -C /workspace/app001 config user.email "user001@example.jp"
git -C /workspace/app001 config user.name "user001"
次に、バージョン管理が必要なファイルを Git リポジトリへ登録します。
git -C /workspace/app001/ add .
git -C /workspace/app001/ commit -m "init"
このようにすると、開発コンテナー内のローカル環境でバージョン管理ができます。
Docker イメージに必要なファイルへの対応
この後に、app001
を起動可能かつ開発で使う Docker イメージをビルドするので、コンテナー内の /workspace/app001
ディレクトリーを Docker ホストの ${PROJ_DIR}/build-image/devcon-python-fastapi-prisma/app001
へコピーします。ただし、.venv
や .git
などは除いて必要なファイルだけを対象とします。
これを実現するには、Docker ホストで ${PROJ_DIR}/dev/devcon-python-fastapi-prisma/script/copy_from_workspace_app_git.sh
のようなスクリプトを用意して実行すると良いでしょう。
#!/bin/sh
PROJ_NAME=devcon-python-fastapi-prisma
APP_DIR_NAME=app001
BASE_DIR=$(cd $(dirname $0)/..;pwd)
DEST_DIR="${BASE_DIR}/${APP_DIR_NAME}"
DC_PROJ_NAME=${PROJ_NAME}
DC_SERVICE_NAME=${PROJ_NAME}
# コンテナーから Git リポジトリー経由でファイルコピー
## - 既存のものは削除
if [ -e ${DEST_DIR} ]; then
rm -fr ${DEST_DIR}
fi
## - リポジトリ用ワーキングディレクトリの用意。
mkdir ${DEST_DIR}
## - リポジトリのコピー
docker compose -p ${DC_PROJ_NAME} \
cp ${DC_SERVICE_NAME}:/workspace/${APP_DIR_NAME}/.git \
${DEST_DIR}/.git
## - リポジトリから HEAD をチェックアウト
git -C ${DEST_DIR} checkout -- .
## - リポジトリの削除
rm -fr ${DEST_DIR}/.git
このスクリプトを利用すれば、devcon-python-fastapi-prisma:/workspace/app001/.git
のリポジトリに登録してあるものだけしか Docker ホスト側にコピーされません。
実行して、${PROJ_DIR}/app001
に Docker イメージに必要なファイルを用意します。
cd ${PROJ_DIR}/dev/devcon-python-fastapi-prisma
sh ./script/copy_from_workspace_app_git.sh
ここで、注意点としては、app001/.gitignore
で指定されているファイルはコピー対象から除外されていることです。たとえば、.env
ファイルのような環境設定用のファイルはリポジトリへいれないことが多いですが、Docker イメージには含めたいこともあるでしょう。こういったファイルを使う場合は対応が必要になります。
対応方法としては、Docker イメージを作成する前に手作業で app001
へ含める、Docker イメージには含めずに docker compose cp
コマンドを使って devcon-python-fastapi-prisma-workspace-data
ボリュームへコピーして用意する、環境変数なら compose.yaml
の environment:
で指定する、などがあります。
これについては、具体的に必要になったところで対応します。
Docker ホストへコピーしてバックアップ
ここで、devcon-python-fastapi-prisma:/workspace/app001
について、丸ごと Docker ホストへコピーしてバックアップをしたい場合があります。devcon-python-fastapi-prisma:/workspace
は Docker ボリュームをマウントしているため、それを削除する前にバックアップしておきたいことがあります。
単純な方法としては、Docker ホストで docker compose cp
コマンドを実行してコピーする方法があります。たとえば、devcon-python-fastapi-prisma:/workspace/app001
を ${PROJ_DIR}/share/app001
へコピーするには、次のようにします。
cd ${PROJ_DIR}
docker compose -p devcon-python-fastapi-prisma \
cp devcon-python-fastapi-prisma:/workspace/app001 ./share/
ただし、Docker ホストが Windows や macOS だと、Linux とファイルシステムがちがうものになるので、この方法では完全なバックアップとすることができません。より良いバックアップ方法としては、少し手間が増えますが、tar
コマンドでアーカイブしたものをバックアップするというものがあります。
アーカイブしたファイルをバックアップするために、ここでは ${PROJ_DIR}/dev/devcon-python-fastapi-prisma/script/copy_from_workspace_app_tgz.sh
スクリプトを用意してあります。
#!/bin/sh
PROJ_NAME=devcon-python-fastapi-prisma
APP_DIR_NAME=app001
BASE_DIR=$(cd $(dirname $0)/../../..;pwd)
DEST_DIR="${BASE_DIR}/share"
DC_PROJ_NAME=${PROJ_NAME}
DC_SERVICE_NAME=${PROJ_NAME}
# コンテナー内でアーカイブ
docker compose -p ${DC_PROJ_NAME} \
exec --workdir=/workspace ${DC_SERVICE_NAME} \
tar cvzf ${APP_DIR_NAME}.tgz ${APP_DIR_NAME}
# コンテナーからコピー
docker compose -p ${DC_PROJ_NAME} \
cp ${DC_SERVICE_NAME}:/workspace/${APP_DIR_NAME}.tgz ${DEST_DIR}/
# コンテナー内のアーカイブ削除
docker compose -p ${DC_PROJ_NAME} \
exec --workdir=/workspace ${DC_SERVICE_NAME} \
rm /workspace/${APP_DIR_NAME}.tgz
これを実行すると、${PROJ_DIR}/share/app001.tgz
ファイルにバックアップができます。
cd ${PROJ_DIR}/dev/devcon-python-fastapi-prisma
sh ./script/copy_from_workspace_app_tgz.sh
後で、devcon-python-fastapi-prisma:/workspace
をマウントしているボリュームは削除するので、ここで必要ならバックアップしておきましょう。
作業が終わったら、devcon-python-fastapi-prisma
コンテナー用 VS Code の画面を閉じてから、コンテナーを停止します。
docker compose -p devcon-python-fastapi-prisma down
開発で使う Docker イメージのビルドと起動
次に、開発で使う Docker イメージをビルドします。
ここでは build-image/devcon-python-fastapi-prisma/build.sh
というスクリプトを用意します。このスクリプトは次のような内容として、${PROJ_DIR}/dev/devcon-python-fastapi-prisma/app001
をコピーして Docker イメージへ書き込むようにしています。
#!/bin/sh
PROJ_NAME=devcon-python-fastapi-prisma
APP_DIR_NAME=app001
BASE_DIR=$(cd $(dirname $0);pwd)
SRC_DIR=$(cd ${BASE_DIR}/../../../dev/${PROJ_NAME};pwd)
DC_FILE_PATH="${BASE_DIR}/compose.yaml"
DC_PROJ_NAME=${PROJ_NAME}
DC_SERVICE_NAME=${PROJ_NAME}
# アプリ用のファイル(イメージ作成用)をコピー。
APP_SRC_PATH=${SRC_DIR}/${APP_DIR_NAME}
# アプリ用のファイルがない場合は処理を終了
if [ ! -e "${APP_SRC_PATH}" ]; then
echo "not found: ${APP_SRC_PATH}"
exit 1
fi
# コピー
cp -r "${APP_SRC_PATH}" "${BASE_DIR}/${APP_DIR_NAME}"
# ビルド
docker compose -f "${DC_FILE_PATH}" build
# キャッシュを使わないビルド(下記を有効にする場合は上記をコメントにすること)
# docker compose -f "${DC_FILE_PATH}" build --no-cache
# アプリ用のファイル(イメージ作成用)を削除
rm -fr ${BASE_DIR}/${APP_DIR_NAME}
このスクリプトの処理内容としては、${PROJ_DIR}/dev/devcon-python-fastapi-prisma
にある app001
をコピーし、compose.yaml
を使ってビルドしているだけです。
compose.yaml
は build-image/init/devcon-python-fastapi-prisma/compose.yaml
と同じで、同じディレクトリーにある Dockerfile
を使って Docker イメージをビルドします。
Dockerfile
は sample/build-image/v0.0_init/devcon-python-fastapi-prisma/Dockerfile
に処理を追加してあります。app001
をコピーして、poetry install
を実行してから、uvicorn
を実行するようにします。app001
には、動作確認済みの FastAPI 用プログラムがあるので、これで動作します。
具体的には次のようになります。
FROM python:3.10.14-bookworm
(略)
COPY app001 /workspace/app001
WORKDIR /workspace/app001
RUN /root/.local/bin/poetry config virtualenvs.in-project true
RUN if [ -f /workspace/app001/pyproject.toml ]; then /root/.local/bin/poetry install --no-root; fi
# uvicorn 起動
ENTRYPOINT ["/root/.local/bin/poetry", "run", "uvicorn", "api.main:app", "--host", "0.0.0.0", "--reload"]
${PROJ_DIR}/build-image/devcon-python-fastapi-prisma/Dockerfile
を更新してから、次のように実行すると、開発で使う Docker イメージとして、FastAPI が動作する devcon-python-fastapi-prisma:0.1
イメージが新しくビルドされます。
cd {PROJ_DIR}
sh ./build-image/v0.1/devcon-python-fastapi-prisma/build.sh
以上で、devcon-python-fastapi-prisma:0.1
イメージを使って、FastAPI 用プログラム app001
のバージョン 0.1 のデモができるようになりました。${PROJ_DIR}/sample/compose/v0.1-demo/devcon-python-fastapi-prisma/compose.yaml
にデモ用の compose.yaml
があります。
このファイルの内容は、次のようになります。デモで使うので /workspace
に Docker ボリュームをマウントすることはしません。
name: devcon-python-fastapi-prisma
services:
devcon-python-fastapi-prisma:
image: devcon-python-fastapi-prisma:0.1
container_name: devcon-python-fastapi-prisma
hostname: devcon-python-fastapi-prisma
tty: true
working_dir: /workspace/app001
ports:
- 127.0.0.1:8000:8000
これを使って app001
のバージョン 0.1 デモ用の devcon-python-fastapi-prisma
コンテナーを起動しましょう。${PROJ_DIR}/dev/devcon-python-fastapi-prisma
ディレクトリーに用意した開発コンテナー用の compose.yaml
ファイルを使って devcon-python-fastapi-prisma
コンテナーを起動します。
cd ${PROJ_DIR}/sample/compose/v0.1-demo/devcon-python-fastapi-prisma
docker compose up -d
起動した devcon-python-fastapi-prisma
コンテナーでは、作成した FastAPI のプログラムが動作しています。そのため、次の URL を Web ブラウザで開くことができるようになっています。
URL | 説明 |
---|---|
http://127.0.0.1:8000/hello | 作成した main/app.py の hello() 関数 |
http://127.0.0.1:8000/docs | Swagger UI による画面 |
http://127.0.0.1:8000/redoc | Redoc による画面 |
なお、各 URL へアクセスした時にエラーとなる場合は、コンテナー内の uvicorn
がうまく動作していません。
そのときは、コンテナー内で別の uvicorn
を起動してみると原因がわかる場合があります。ポート番号 8000 は最初に自動起動する uvicorn
が使ってしまっているので、別のポート番号で起動します。例えばポート番号 8001 を使う場合は、次のようにします。
/root/.local/bin/poetry run --directory /workspace/app001 \
uvicorn api.main:app --host 0.0.0.0 --port 8001 --reload
これを実行すると、VS Code が自動でポート番号 8001 の転送を有効にして、Docker ホストの Web ブラウザから http://127.0.0.1:8001 へアクセスできるようになります。こちらを動かして動作の確認をする場合は、使用する URL について、ポート番号を 8000 から 8001 へ変更して Web ブラウザからアクセスします。
デモの動作確認ができたら、コンテナーを終了します。
docker compose -p devcon-python-fastapi-prisma down
FastAPI 用プログラムの開発
次に、FastAPI 用プログラム app001
のバージョン 0.2 の開発をしましょう。
開発コンテナーの用意
0.2 用の Docker イメージは開発を開始する時点では devcon-python-fastapi-prisma:0.1
と同じで良いので、docker tag
コマンドを使って、次のように用意します。
docker tag devcon-python-fastapi-prisma:0.1 devcon-python-fastapi-prisma:0.2
次に、app001
のバージョン 0.2 を開発するときに使う開発コンテナー用に ${PROJ_DIR}/dev/devcon-python-fastapi-prisma/compose.yaml
を修正します。
次のような内容になります。単に、使用するイメージを devcon-python-fastapi-prisma:0.2
と変更しただけです。
name: devcon-python-fastapi-prisma
services:
devcon-python-fastapi-prisma:
image: devcon-python-fastapi-prisma:0.2
container_name: devcon-python-fastapi-prisma
hostname: devcon-python-fastapi-prisma
tty: true
working_dir: /workspace/app001
ports:
- 127.0.0.1:8000:8000
volumes:
- workspace-data:/workspace
volumes:
workspace-data:
name: devcon-python-fastapi-prisma-workspace-data
用意ができたら起動したいところですが、devcon-python-fastapi-prisma:0.1
イメージ作成のために devcon-python-fastapi-prisma
コンテナーを実行した時に自動で作成された devcon-python-fastapi-prisma-workspace-data
ボリュームを残しておくと、これが使われてしまいます。基本的に Docker イメージに含まれるものと同じはずですが、Docker イメージに書き込まれているファイルを確認するには、一度削除しておいた方が良いです。次のように docker volume rm
コマンドで削除しておきます。
docker volume rm devcon-python-fastapi-prisma-workspace-data
それでは devcon-python-fastapi-prisma
コンテナーを起動します。
cd ${PROJ_DIR}/dev/devcon-python-fastapi-prisma
docker compose up -d
ここで、Docker イメージには .git
は登録していないので、それを使って自動で作成される devcon-python-fastapi-prisma:/workspace/app001
ディレクトリーはリポジトリーではなくなっています。devcon-python-fastapi-prisma
コンテナー内で ls -al
コマンドを実行して確認してみましょう。
docker compose -p devcon-python-fastapi-prisma \
exec devcon-python-fastapi-prisma \
ls -al /workspace/app001/
これで、用意した devcon-python-fastapi-prisma:0.2
イメージ(この時点では devcon-python-fastapi-prisma:0.1
イメージと同じ)に app001
のバージョン 0.1 のコードが .git
ディレクトリーがない状態で含まれていることが確認できるはずです。
次に、取ってあったバックアップを使って復旧しましょう。バックアップから復旧するためのスクリプトが ${PROJ_DIR}/dev/devcon-python-fastapi-prisma/script/restore_workspace_app_git.sh
にあります。これを Docker ホストで実行します。
cd ${PROJ_DIR}/dev/devcon-python-fastapi-prisma
sh ./script/restore_workspace_app_git.sh
これで、devcon-python-fastapi-prisma:/workspace/app001
ディレクトリーは復旧されて、Git リポジトリーになります。
FastAPI アプリ v0.2 の開発
それから、VS Code をアタッチし、その画面で /workspace/app001/app001.code-workspace
のワークスペースを次の手順で開きます。
-
devcon-python-fastapi-prisma
コンテナー用 VS Code の画面のメニューで「ファイル」-「ファイルでワークスペースを開く」をクリック -
/workspace/app001/app001.code-workspace
のファイルを指定して「開く」をクリック
これで、VS Code で app001.code-workspace
のワークスペースが開きます。推奨する拡張機能として、拡張機能の ID が ms-python.python
である Python 拡張機能を登録してあるため、この拡張機能をインストールするかを確認する通知が表示されます。インストールして使うと良いでしょう。
これで、devcon-python-fastapi-prisma
コンテナー用 VS Code の画面でアプリの開発ができます。
ここでは、app001/api/main.py
に、タスク API を Python のリストを使って実装した例を用意しました。モデルクラスとして TaskItem クラスと TaskList クラスを用意して、Python のリストでタスク管理をしています。これを FastAPI 用の関数から利用して Web API を実装しています。
import uuid
import uvicorn
from typing import List
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from pydantic import BaseModel
class CreateTaskItem(BaseModel):
"""タスクアイテム新規作成用オブジェクトを表すクラス"""
title: str
description: str = None
completed: bool = False
class TaskItem(BaseModel):
"""タスクアイテムを表すクラス"""
id: str
title: str
description: str = None
completed: bool = False
class TaskList:
"""タスクリストを管理するクラス"""
def __init__(self):
"""コンストラクタ"""
self.tasks = []
def add_task(self, id, title, description, completed):
# 略
app = FastAPI()
taskNone = TaskItem(id="", title="", description="", completed=False)
task_list = TaskList()
@app.get("/")
async def api_root():
return {"message": "OK"}
@app.post("/tasks/", response_model=TaskItem)
async def create_taskitem(task: CreateTaskItem):
id = str(uuid.uuid4())
values = task.model_dump()
new_task = {"id": id, **values}
ret = task_list.add_task(**new_task)
if ret:
return JSONResponse(content=new_task, status_code=201)
else:
return JSONResponse(content=taskNone, status_code=400)
# 略
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=18000)
app001/api/main.py
をデバッグ起動できるように、このファイルを直接実行したら、uvicorn.run(app, host="0.0.0.0", port=18000)
で uvicorn
が起動するようにしてあります。コードからわかるように、デバッグ実行するときは、ポート番号 18000 を使います。
VS Code ワークスペースの設定追加
VS Code をアタッチする開発コンテナーを使うにあたり、app001.code-workspace
ファイルでは設定を増やして開発しやすくしています。
Python のコードをフォーマットできるように、推奨する拡張機能へ ms-python.black-formatter
を追加し、それをフォーマッターとして使うように "settings":
で指定します。また、Python が参照するパッケージのパスの設定も追加してあります。
また、デバッグ実行ができるように "launch":
の設定も追加してあります。
{
"folders": [
{
"path": "."
}
],
"settings": {
"python.defaultInterpreterPath": "/workspace/app001/.venv/bin/python",
"python.analysis.extraPaths": [
"/workspace/app001"
],
// `PYTHONPATH=/workspace/app001 python api/main.py` で実行可能
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter",
"editor.formatOnSave": true
},
},
"launch": {
"version": "0.2.0",
"configurations": [
{
"name": "Python デバッガー: 現在のファイル",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"env": {
"PYTHONPATH": "/workspace/app001"
},
"console": "integratedTerminal"
}
],
"compounds": []
},
"extensions": {
"recommendations": [
"ms-python.python",
"ms-python.black-formatter"
]
}
}
なお、app001.code-workspace
ファイルを更新したら、devcon-python-fastapi-prisma コンテナー用の VS Code 画面のメニューで「ファイル」-「ワークスペースを閉じる」をクリックします。それから、「ファイル」-「ファイルでワークスペースを開く」で app001.code-workspace
ファイルを指定しワークスペースを開き直します。
ここで、拡張機能の画面を開いて ms-python.python
と ms-python.black-formatter
がインストールされていなかったら、手動でインストールしましょう。それから再度 app001.code-workspace
のワークスペースを開くと、Python ファイルはエディタでコードフォーマットができるようになり、実行とデバッグの画面でデバッグ実行ができるようになります。
ターミナルのプロンプト改善
ここで、ターミナルでコマンド実行する時のプロンプトがシンプルすぎるので表示される情報を増やしたいと思うかもしれません。その場合は、starship を使ってみると良いでしょう。
${PROJ_DIR}/sample/build-image/v0.3/devcon-python-fastapi-prisma/install_starship.sh
に starship をインストールするスクリプトがあります。
#! /bin/sh
DL_URL=https://github.com/starship/starship/releases/latest/download
cd /workspace \
&& curl -LO ${DL_URL}/starship-x86_64-unknown-linux-musl.tar.gz \
&& tar xf starship-x86_64-unknown-linux-musl.tar.gz \
&& rm starship-x86_64-unknown-linux-musl.tar.gz
echo 'eval "$(/workspace/starship init bash)"' >> /root/.bashrc
if [ ! -e /root/.config ]; then mkdir /root/.config; fi
/workspace/starship preset plain-text-symbols > /root/.config/starship.toml
sed -i 's/Rocky = "rky "//' /root/.config/starship.toml
これを devcon-python-fastapi-prisma
コンテナーへ用意して実行すると starship
が適用された bash
が使えるようになります。
ここで、手動で devcon-python-fastapi-prisma
コンテナーに install_starship.sh
ファイルを作成して実行しても良いのですが、Docker ホスト側から一連の作業を実行できるスクリプトを用意することもできます。${PROJ_DIR}/dev/devcon-python-fastapi-prisma/copy_and_install_starship.sh
に、あらかじめ作成したものがあります。
#!/bin/sh
SCRIPT_NAME=install_starship.sh
BASE_DIR=$(cd $(dirname $0)/../../..;pwd)
SRC_DIR=${BASE_DIR}/sample/build-image/v0.3/devcon-python-fastapi-prisma
SRC_FILE_PATH=${SRC_DIR}/${SCRIPT_NAME}
DC_PROJ_NAME=devcon-python-fastapi-prisma
DC_SERVICE_NAME=devcon-python-fastapi-prisma
## - ファイルのコピー
docker compose -p ${DC_PROJ_NAME} \
cp ${SRC_FILE_PATH} ${DC_SERVICE_NAME}:/
## - インストール
docker compose -p ${DC_PROJ_NAME} \
exec ${DC_SERVICE_NAME} sh /${SCRIPT_NAME}
これを Docker ホストで、次のように実行します。これでコンテナーで starship が使えるようになります。
cd ${PROJ_DIR}/dev/devcon-python-fastapi-prisma
sh ./script/copy_and_install_starship.sh
これ以降、devcon-python-fastapi-prisma
コンテナーのターミナルで bash を起動すると starship が有効になり、次のような情報が多いプロンプト表示になります。
root@devcon-python-fastapi-prisma:/workspace# bash
app001 on git main is pkg v0.1.0 via py v3.10.14 (app001-py3.10)
⬢ [Docker] >
気に入ったら、この機能を Docker イメージへ反映すると良いでしょう。とはいえ、一度インストールしたら、コンテナーを down
で破棄しなければ、使い続けることができるので、Docker イメージへ反映しなくても、それほど不便はしないはずです。
ここではすぐには反映せずに Prisma の対応をした Docker イメージを作成する時に反映することにします。それまでは、必要に応じて手動でインストールして使うことにします。
FastAPI アプリ v0.2 の動作確認とリポジトリへの反映
プログラムの更新ができたら、 Web ブラウザで次の URL を開いて動作確認します。/hello
は削除したので使えなくなっています。代わりに /
を用意したので、こちらを開きます。
URL | 説明 |
---|---|
http://127.0.0.1:8000/ | 作成した main/app.py の api_root() 関数 |
http://127.0.0.1:8000/docs | Swagger UI による画面 |
http://127.0.0.1:8000/redoc | Redoc による画面 |
用意したタスク用の API について、/docs
や /redoc
の画面に反映されているはずです。ここでは使い方については省略します。
動作確認ができたら、リポジトリへ反映します。
cd /workspace/app001
git add app001.code-workspace
git add api/main.py
git commit -m "v0.2"
開発したプログラムを Dcoker イメージへ反映
Python アプリの app001
バージョン 0.2 について、devcon-python-fastapi-prisma
コンテナー用 VS Code の画面で開発ができたら、これを Dcoker イメージへ反映します。
バージョン 0.1 のときと同様にして Docker イメージを更新することができます。Docker ホストへソースコードをコピーするためのスクリプトとして、copy_from_workspace_app_git.sh
を用意してありました。devcon-python-fastapi-prisma
コンテナーが起動している状態で、これを実行します。なお、実行時に既存の ${PROJ_DIR}/dev/devcon-python-fastapi-prisma/app001
は削除されます。残しておきたい場合はバックアップしておきましょう。
cd ${PROJ_DIR}/dev/devcon-python-fastapi-prisma
sh ./script/copy_from_workspace_app_git.sh
このあと、devcon-python-fastapi-prisma:/workspace
で使っているボリュームは削除してしまいます。この時点でバックアップしておきましょう。
cd ${PROJ_DIR}/dev/devcon-python-fastapi-prisma
sh ./script/copy_from_workspace_app_tgz.sh
次に ${PROJ_DIR}/dev/devcon-python-fastapi-prisma
にある compose.yaml
をバージョン 0.2 用のものにします。ちなみに Dockerfile
は変更の必要がありません。
compose.yaml
は、ビルドして作成するイメージを devcon-python-fastapi-prisma:0.2
と変更するだけです。
name: devcon-python-fastapi-prisma
services:
devcon-python-fastapi-prisma:
build: .
image: devcon-python-fastapi-prisma:0.2
container_name: devcon-python-fastapi-prisma
hostname: devcon-python-fastapi-prisma
tty: true
準備ができたら、devcon-python-fastapi-prisma
コンテナー用 VS Code の画面を閉じて、devcon-python-fastapi-prisma
コンテナーを破棄します。
docker compose -p devcon-python-fastapi-prisma down
それから、build-image/v0.2/devcon-python-fastapi-prisma/build.sh
を実行すると、Docker イメージが更新できます。
cd ${PROJ_DIR}/build-image/devcon-python-fastapi-prisma
sh ./build.sh
これで app001
のバージョン 0.2 のデモを動かせる Docker イメージが用意できました。デモ用の compose.yaml
ファイルのサンプルを sample/compose/v0.2-demo/devcon-python-fastapi-prisma/compose.yaml
に用意しました。次のように起動して使うことができます。
cd ${PROJ_DIR}/sample/compose/v0.2-demo/devcon-python-fastapi-prisma/
docker compose up -d
これで、開発時に動作確認した URL を開けるようになります。確認したら停止します。
docker compose -p devcon-python-fastapi-prisma down
Prisma を使うアプリの作成
次に app001
のバージョン 0.3 を開発します。ここでは Prisma を導入します。
開発コンテナーの用意
開発用の Docker イメージ devcon-python-fastapi-prisma:0.3
イメージは devcon-python-fastapi-prisma:0.2
イメージから用意します。
docker tag devcon-python-fastapi-prisma:0.2 devcon-python-fastapi-prisma:0.3
${PROJ_DIR}/dev/devcon-python-fastapi-prisma/compose.yaml
で使う Docker イメージも devcon-python-fastapi-prisma:0.3
へ修正します。
ここでは、devcon-python-fastapi-prisma:0.3
イメージから起動したコンテナーを開発環境として使用します。
ところで、devcon-python-fastapi-prisma:0.2
イメージから起動したコンテナーを開発に使う時、devcon-python-fastapi-prisma-workspace-data
ボリュームを削除して動作確認しました。ボリューム削除をすると、Git リポジトリが削除されてしまうので、復旧が必要となります。
それは手間なので、今回は devcon-python-fastapi-prisma-workspace-data
ボリュームを削除せずに、そのまま使用することにします。
それでは、devcon-python-fastapi-prisma:0.3
イメージを使う compose.yaml
を用意したら devcon-python-fastapi-prisma
コンテナーを起動して、VS Code をアタッチします。 devcon-python-fastapi-prisma
コンテナー用の VS Code の画面でターミナルを起動して、app001
アプリのバージョン 0.3 を開発するための環境を整備します。
v0.3 開発に必要な環境の整備
Prisma を使うには https://deb.nodesource.com から入手可能な nodejs
パッケージが必要なので、インストールします。
curl -fsSL https://deb.nodesource.com/setup_21.x | bash - && apt-get install -y nodejs
それから、SQLite3 の DB を使うため、sqlite3
コマンドも使えるようにしておきます。これは apt-get
コマンドでインストールできる sqlite3
パッケージに含まれているので、これを使います。
apt-get -y install sqlite3
それから、poetry add
コマンドで Python 用の prisma
パッケージを app001
のプロジェクトへ追加します。
/root/.local/bin/poetry add --directory /workspace/app001 prisma
ここでは、DB として SQLite3 を使うことにします。また、SQLite3 のデータベースファイルは /workspace/app001/data/app.db
ファイルで用意することにします。DB アクセス時に使う URL は環境変数 DATABASE_URL で指定することにして、/workspace/app001/.env
へ保存します。
DATABASE_URL=file:data/app.db
このファイルは、次のように作成すれば良いでしょう。
echo "DATABASE_URL=file:data/app.db" > /workspace/app001/.env
ちなみに、このファイルは .gitignore
でリポジトリ管理対象外とします。また、DATABASE_URL
で指定する file:data/app.db
は /workspace/app001/prisma/data
と対応します。ここに作成される app.db
などもリポジトリ管理対象外とします。そのため、次のように .gitignore
を修正します。
echo "" >> /workspace/app001/.gitignore
echo ".env" >> /workspace/app001/.gitignore
echo "prisma/data" >> /workspace/app001/.gitignore
TaskItem 用テーブルの用意
ここでは TaskItem 用のテーブルを用意することにします。Prisma では DB の構造をスキーマファイルで指定することができます。/workspace/app001/prisma/schema.prisma
を作成します。
generator client {
provider = "prisma-client-py"
interface = "asyncio"
recursive_type_depth = 5
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model TaskItem {
id String @id @default(uuid())
title String
description String
completed Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
スキーマファイルを用意したら、prisma migrate dev
コマンドを実行して schema.prisma
の指定に従った開発用 DB を作成します。
cd /workspace/app001
prisma migrate dev --name init
ここではマイグレーション名の指定に --name init
としてあります。これを省略すると、コマンド実行時の途中に Enter a name for the new migration:
とマイグレーション名を指定するためのプロンプトが表示されます。そこで init
と入力して進めるのと同じになります。
コマンド実行時に表示される Applying migration
に続く文字列から正確なマイグレーション名がわかります。
実際の実行例を次に示します。
# cd /workspace/app001
# /root/.local/bin/poetry run prisma migrate dev --name init
Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Datasource "db": SQLite database "app.db" at "file:data/app.db"
SQLite database app.db created at file:data/app.db
Applying migration `20240428225915_init`
The following migration(s) have been created and applied from new schema changes:
migrations/
└─ 20240428225915_init/
└─ migration.sql
Your database is now in sync with your schema.
✔ Generated Prisma Client Python (v0.13.1) to ./.venv/lib/python3.10/site-packages/prisma in 145ms
この結果から、ここでのマイグレーション名は 20240428225915_init
になったことがわかります。
マイグレーションの実行が終了すると、そのときに使用した SQL が prisma/migrations/<マイグレーション名>/migration.sql
に出力されます。今回の 20240428225915_init
については、次のようになっていました。
# cat prisma/migrations/20240428225915_init/migration.sql
-- CreateTable
CREATE TABLE "TaskItem" (
"id" TEXT NOT NULL PRIMARY KEY,
"title" TEXT NOT NULL,
"description" TEXT NOT NULL,
"completed" BOOLEAN NOT NULL DEFAULT false,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
なお、prisma migrate
コマンド実行時に Python の DB クライアントプログラムが内部的に生成されて使えるようになっています。これは prisma generate
コマンドを実行することで明示的に生成することができます。
Docker イメージを作成するときは、prisma migrate dev
コマンドを実行するので、そのときに Python の DB クライアントプログラムが生成されます。覚えておきましょう。
次に、DB アクセスする FastAPI のプログラムを作成します。
Prisma で DB アクセス
ここで作成した DB アクセスする FastAPI のプログラム app001/api/main.py
は次のようになります。TaskList の実装を Prisma を使うものへ変更しています。また、Prisma を使うようにしたことで、それに合わせて API の方も若干修正をしています。
import uuid
import uvicorn
from contextlib import asynccontextmanager
from typing import List
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from prisma import Prisma
from pydantic import BaseModel
prisma = Prisma()
@asynccontextmanager
async def lifespan(app: FastAPI):
await prisma.connect()
yield
await prisma.disconnect()
app = FastAPI(lifespan=lifespan)
class ApiTaskItemBase(BaseModel):
"""タスクアイテム新規作成用オブジェクトを表すクラス"""
title: str
description: str = ""
completed: bool = False
class ApiTaskItem(ApiTaskItemBase):
"""タスクアイテムを表すクラス"""
id: str
class TaskList:
"""タスクリストを管理するクラス"""
def __init__(self):
"""コンストラクタ"""
self.prisma = prisma
async def add_task(self, taskitem: ApiTaskItem):
"""タスクを追加
Args:
taskitem (ApiTaskItem):
Returns:
True: タスクを追加した場合
False: タスクを追加しなかった場合
"""
task = await prisma.taskitem.create(data=taskitem)
if task is not None:
return True
else:
return False
# 略
task_none = ApiTaskItem(id="", title="", description="", completed=False)
task_list = TaskList()
@app.get("/")
async def api_root():
return {"message": "OK"}
@app.post("/tasks/", response_model=ApiTaskItem)
async def create_taskitem(task: ApiTaskItemBase):
id = str(uuid.uuid4())
values = task.model_dump()
new_task = {"id": id, **values}
ret = await task_list.add_task(new_task)
if ret:
return JSONResponse(content=new_task, status_code=201)
else:
return JSONResponse(content=task_none, status_code=400)
# 略
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=18000)
動作確認については、これまでと同じです。少し API に変更をいれているので、Web ページをきちんとリロードして表示させるようにしてください。
URL | 説明 |
---|---|
http://127.0.0.1:8000/ | 作成した main/app.py の api_root() 関数 |
http://127.0.0.1:8000/docs | Swagger UI による画面 |
http://127.0.0.1:8000/redoc | Redoc による画面 |
動作確認をしたら Git リポジトリへ修正を反映します。
cd /workspace/app001
git add .gitignore
git add pyproject.toml
git add poetry.lock
git add prisma
git add api/main.py
git commit -m "v0.3"
開発した Prisma 対応版プログラムを Dcoker イメージへ反映
Prisma 対応版プログラムが動作するようになったら、それを Dcoker イメージへ反映します。
バージョン 0.2 のときと同様にして Docker イメージを更新することができます。Docker ホストへのソースコードコピーとバックアップをします。バックアップをしたら devcon-python-fastapi-prisma
コンテナーは破棄します。
cd ${PROJ_DIR}/dev/devcon-python-fastapi-prisma
sh ./script/copy_from_workspace_app_git.sh
sh ./script/copy_from_workspace_app_tgz.sh
docker compose -p devcon-python-fastapi-prisma down
今回は、プログラム以外に DB 関連のファイルとして .env
の追加が必要です。
.env
ファイルについては、${PROJ_DIR}/build-image/devcon-python-fastapi-prisma/resource/.env
に Docker イメージへ含める .env
ファイルを置くことにします。
DATABASE_URL=file:data/app.db
また、この .env
ファイルを使うように ${PROJ_DIR}/build-image/devcon-python-fastapi-prisma/build.sh
スクリプトを修正します。
#!/bin/sh
PROJ_NAME=devcon-python-fastapi-prisma
APP_DIR_NAME=app001
BASE_DIR=$(cd $(dirname $0);pwd)
SRC_DIR=$(cd ${BASE_DIR}/../../dev/${PROJ_NAME};pwd)
RESOURCE_DIR=${BASE_DIR}/resource
DC_FILE_PATH="${BASE_DIR}/compose.yaml"
DC_PROJ_NAME=${PROJ_NAME}
DC_SERVICE_NAME=${PROJ_NAME}
# アプリ用のファイル(イメージ作成用)をコピー。
APP_SRC_PATH=${SRC_DIR}/${APP_DIR_NAME}
# アプリ用のファイルがない場合は処理を終了
if [ ! -e "${APP_SRC_PATH}" ]; then
echo "not found: ${APP_SRC_PATH}"
exit 1
fi
# コピー
cp -r "${APP_SRC_PATH}" "${BASE_DIR}/${APP_DIR_NAME}"
# .env ファイルの用意
APP_ENV_FILE_PATH=${RESOURCE_DIR}/.env
if [ ! -e "${APP_ENV_FILE_PATH}" ]; then
# .env ファイルがない場合はエラー
echo "not found: ${APP_ENV_FILE_PATH}"
exit 1
fi
cp ${APP_ENV_FILE_PATH} "${BASE_DIR}/${APP_DIR_NAME}/"
# ビルド
docker compose -f "${DC_FILE_PATH}" build
# キャッシュを使わないビルド(下記を有効にする場合は上記をコメントにすること)
# docker compose -f "${DC_FILE_PATH}" build --no-cache
# アプリ用のファイル(イメージ作成用)を削除
rm -fr ${BASE_DIR}/${APP_DIR_NAME}
DB ファイル(開発時に指定したもの、サンプルの .env
に指定されたものをそのまま使うなら data/app.db
)については、Docker イメージ作成時に、prisma migrate dev
コマンドを実行すれば、新規の SQLite3 用 DB ファイルが作成できます。この修正を Dockerfile
にします。
他にも nodejs
、starship
、sqlite3
といったものもインストールします。
FROM python:3.10.14-bookworm
ENV PYTHONUNBUFFERED=1
# poetry
RUN curl -sSL https://install.python-poetry.org | python -
# nodejs
RUN curl -fsSL https://deb.nodesource.com/setup_21.x | bash - && apt-get install -y nodejs
# starship
COPY install_starship.sh /workspace/install_starship.sh
RUN sh /workspace/install_starship.sh
ENV PATH /root/.local/bin:$PATH
# app001
COPY app001 /workspace/app001
WORKDIR /workspace/app001
RUN /root/.local/bin/poetry config virtualenvs.in-project true
RUN if [ -f /workspace/app001/pyproject.toml ]; then /root/.local/bin/poetry install --no-root; fi
# prisma
RUN if [ -f /workspace/app001/prisma/schema.prisma ]; then /root/.local/bin/poetry run prisma migrate dev; fi
# SQLite3
RUN apt-get update \
&& apt-get -y upgrade \
&& apt-get -y install --no-install-recommends \
sqlite3 \
&& apt-get autoremove -y \
&& rm -rf /var/cache/apt /var/lib/apt/lists
# uvicorn 起動
ENTRYPOINT ["/root/.local/bin/poetry", "run", "uvicorn", "api.main:app", "--host", "0.0.0.0", "--reload"]
これらを ${REPO_DIR}/build-image/devcon-python-fastapi-prisma
に用意したら、Docker イメージをビルドします。
cd ${REPO_DIR}/build-image/devcon-python-fastapi-prisma
sh ./build.sh
作成した Docker イメージを起動するには、{PROJ_DIR}/sample/compose/v0.3/devcon-python-fastapi-prisma/compose.yaml
ファイルを使います。compose.yaml
を、このファイルの内容で置き換えてから docker compose
コマンドで起動します。
cd ${PROJ_DIR}
cp sample/compose/v0.3/devcon-python-fastapi-prisma/compose.yaml .
docker compose up -d
デモとして動作させるには、{PROJ_DIR}/sample/compose/v0.3-demo/devcon-python-fastapi-prisma/compose.yaml
ファイルを使います。
cd ${PROJ_DIR}/sample/compose/v0.3/devcon-python-fastapi-prisma/compose.yaml
docker compose up -d
まとめ
以上で説明はおしまいです。入門版の開発コンテナーを使って、Poetry、FastAPI、Prisma Client Python を使用する Python アプリの開発をするときの様子を紹介しましたが、いかがだったでしょうか。
開発コンテナーに慣れている人だと、このような感じで開発をしていくと、各バージョンでアプリの開発ができる環境も一緒に Git リポジトリに残るため、少し前のバージョンで動作確認をしつつデバッグもしたい場合に便利そうだと感じたのではないかと思います。
一方で、まだあまり開発コンテナーに慣れていない人だと、ざっくりとした印象としては次のような抵抗感がありそうです。
- Docker イメージの開発とアプリの開発の区別がわかりにくい
- Docker イメージへの反映が手間
- バージョン管理で混乱が起きそう
今回は、自分が開発コンテナーを使う時にどうやっているかを説明するために、開発コンテナーとして利用する前提の Docker イメージの開発とアプリの開発の両輪を回してみたのですが、事前に想像していたよりも大変そうな感じになってしまいました。
ただし、これは開発コンテナーも用意しつつ開発をするような立場にいる人だけが理解していれば良い話でもあります。開発コンテナーを利用するだけの立場の場合は、devcon-python-fastapi-prisma:0.3
用の Dockerfile
と compose.yaml
ファイルを渡されるだけで、app001
アプリの開発へ参加できるようになります。自分で開発に必要なバージョンの Python や Poetry を用意したり、調べたりする必要はありません。
また、今回のように開発コンテナーを用意すると、アプリ開発に必要な要件が自然と compose.yaml
と Dockerfile
に記録されていきます。そういったメリットに気がつくと、こういった開発コンテナーを用意することが重要だということがわかるはずです。
開発コンテナーを利用して、楽しい開発ができるようになれるといいですね。
Discussion