前提条件
- Python 3.11.6
- Poetry 1.82
フォルダ構成
大体poetry new
で生成されるものを使用しました。
my_project/
├── .venv/
├── main/
│ └─ server.py
├── tests/
│ └─ テストファイルたち
├── Dockerfile
├── poetry.lock
├── pyproject.toml
├── README.md
├── .dockerignore
└── .pre-commit-config.yaml
testやpre-commitは今回の記事では触れません。
ライブラリ追加
poetryで必要なライブラリをインストールしていきます。
必要なのはfastapi
とuvicorn
です。
poetry add fastapi uvicorn
ファイル作成
server.py
server.py
from fastapi import FastAPI
server = FastAPI()
@server.get("/")
async def root():
return {"message": "Hello World"}
よくある"Hello, World"を返すだけのやつです。
.dockerignore
.dockerignore
# git関連
.git
.gitignore
# 開発でしか使用しない
tests
.pre-commit-config.yaml
.vscode
# コンテナに含める必要はない
Dockerfile
.dockerignore
# ドキュメント
README.md
# 仮想環境
.venv
# キャッシュファイル
**/*/__pycache__
# このプロジェクトでは使用していない
**/*/__init__.py
コンテナに含める必要のないファイルを指定します。
Dockerfile
# builderとrunnerで使うベースイメージを決めています
FROM python:3.11.9-slim-bullseye AS base
# baseをbuilderステージで使用
FROM base AS builder
RUN pip install poetry
WORKDIR /app
# 作業ディレクトリにpyproject.tomlとpoetry.lockをコピー
COPY pyproject.toml poetry.lock ./
# poetryの仮想環境を作らないように指定、依存関係のインストールを行う
# --no-devを付けることで、開発にしか使用しないパッケージを除いてインストールする
RUN poetry config virtualenvs.create false \
&& poetry install --no-dev
# プロジェクトのコードをコピー
# COPY main ./ とすると失敗します。理由がわかる方教えてください。
COPY . ./
# baseをrunnerステージで使用
FROM base AS runner
WORKDIR /app
# システムグループを作成
RUN addgroup --system --gid 1001 python
# システムユーザーを作成
RUN adduser --system --uid 1001 api
# builderステージから/appディレクトリ全体をrunnerステージにコピー
COPY --from=builder /app /app
# builderステージからPythonのサイトパッケージディレクトリをrunnerステージにコピー
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
# builderステージから/usr/local/binディレクトリをrunnerステージにコピー
COPY --from=builder /usr/local/bin /usr/local/bin
# ユーザーをapiに設定
USER api
# コンテナの8080番ポートを開ける
EXPOSE 8080
# poetryを使ってuvicornを実行し、APIサーバーの起動を行っている
CMD ["poetry", "run", "uvicorn", "main.server:server", "--host", "0.0.0.0", "--port", "8080"]
マルチステージビルドを採用することで最終的なイメージのサイズを小さくしています。
また、一般ユーザー(api)にユーザー切り替えすることで、コンテナがroot権限で実行されないようにしています。
# docker runまで
Dockerfileがあるディレクトリからターミナルを操作する想定です。
# name=任意の名前
# tag=任意のタグ
docker build -t name:tag .
最後のコロンはDockerfileに位置を指定しているものです。
docker runを実行します。
docker run -it -p 8080:8080 name:tag
> Creating virtualenv chroma-key-api-9TtSrW0h-py3.11 in /home/api/.cache/pypoetry/virtualenvs
> INFO: Started server process [1]
> INFO: Waiting for application startup.
> INFO: Application startup complete.
> INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
これでhttp://0.0.0.0:8080
に接続できれば成功です。