38 KiB
コンテナ内のFastAPI - Docker
FastAPIアプリケーションをデプロイする場合、一般的なアプローチはLinuxコンテナ・イメージをビルドすることです。基本的には Dockerを用いて行われます。生成されたコンテナ・イメージは、いくつかの方法のいずれかでデプロイできます。
Linuxコンテナの使用には、セキュリティ、反復可能性(レプリカビリティ)、シンプリシティなど、いくつかの利点があります。
/// tip | 豆知識
お急ぎで、すでにこれらの情報をご存じですか? 以下のDockerfileの箇所👇へジャンプしてください。
///
Dockerfile Preview 👀
FROM python:3.9
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
# If running behind a proxy like Nginx or Traefik add --proxy-headers
# CMD ["fastapi", "run", "app/main.py", "--port", "80", "--proxy-headers"]
コンテナとは何か
コンテナ(主にLinuxコンテナ)は、同じシステム内の他のコンテナ(他のアプリケーションやコンポーネント)から隔離された状態を保ちながら、すべての依存関係や必要なファイルを含むアプリケーションをパッケージ化する非常に軽量な方法です。
Linuxコンテナは、ホスト(マシン、仮想マシン、クラウドサーバーなど)の同じLinuxカーネルを使用して実行されます。これは、(OS全体をエミュレートする完全な仮想マシンと比べて)非常に軽量であることを意味します。
このように、コンテナはリソースをほとんど消費しませんが、プロセスを直接実行するのに匹敵する量です(仮想マシンはもっと消費します)。
コンテナはまた、独自の分離された実行プロセス(通常は1つのプロセスのみ)や、ファイルシステム、ネットワークを持ちます。 このことはデプロイ、セキュリティ、開発などを簡素化させます。
コンテナ・イメージとは何か
コンテナは、コンテナ・イメージから実行されます。
コンテナ・イメージは、コンテナ内に存在すべきすべてのファイルや環境変数、そしてデフォルトのコマンド/プログラムを静的にバージョン化したものです。 ここでの静的とは、コンテナイメージは実行されておらず、パッケージ化されたファイルとメタデータのみであることを意味します。
保存された静的コンテンツである「コンテナイメージ」とは対照的に、「コンテナ」は通常、実行中のインスタンス、つまり実行されているものを指します。
コンテナが起動され実行されるとき(コンテナイメージから起動されるとき)、ファイルや環境変数などが作成されたり変更されたりする可能性があります。これらの変更はそのコンテナ内にのみ存在しますが、基盤となるコンテナ・イメージには残りません(ディスクに保存されません)。
コンテナイメージは プログラム ファイルやその内容、例えば python と main.py ファイルに匹敵します。
そして、コンテナ自体は(コンテナイメージとは対照的に)イメージをもとにした実際の実行中のインスタンスであり、プロセスに匹敵します。実際、コンテナが実行されているのは、プロセスが実行されているときだけです(通常は単一のプロセスだけです)。 コンテナ内で実行中のプロセスがない場合、コンテナは停止します。
コンテナ・イメージ
Dockerは、コンテナ・イメージとコンテナを作成・管理するための主要なツールの1つです。
そして、多くのツールや環境、データベース、アプリケーションに対応している予め作成された公式のコンテナ・イメージをパブリックに提供しているDocker Hubというものがあります。
例えば、公式イメージの1つにPython Imageがあります。
その他にも、データベースなどさまざまなイメージがあります:
- PostgreSQL
- MySQL
- MongoDB
- Redis, etc.
予め作成されたコンテナ・イメージを使用することで、異なるツールを組み合わせて使用することが非常に簡単になります。例えば、新しいデータベースを試す場合に特に便利です。ほとんどの場合、公式イメージを使い、環境変数で設定するだけで良いです。
そうすれば多くの場合、コンテナとDockerについて学び、その知識をさまざまなツールやコンポーネントによって再利用することができます。
つまり、データベース、Pythonアプリケーション、Reactフロントエンド・アプリケーションを備えたウェブ・サーバーなど、さまざまなものを複数のコンテナで実行し、それらを内部ネットワーク経由で接続します。
すべてのコンテナ管理システム(DockerやKubernetesなど)には、こうしたネットワーキング機能が統合されています。
コンテナとプロセス
通常、コンテナ・イメージはそのメタデータにコンテナの起動時に実行されるデフォルトのプログラムまたはコマンドと、そのプログラムに渡されるパラメータを含みます。コマンドラインでの操作とよく似ています。
コンテナが起動されると、そのコマンド/プログラムが実行されます(ただし、別のコマンド/プログラムをオーバーライドして実行させることもできます)。
コンテナは、メイン・プロセス(コマンドまたはプログラム)が実行されている限り実行されます。
コンテナは通常1つのプロセスを持ちますが、メイン・プロセスからサブ・プロセスを起動することも可能で、そうすれば同じコンテナ内に複数のプロセスを持つことになります。
しかし、少なくとも1つの実行中のプロセスがなければ、実行中のコンテナを持つことはできないです。メイン・プロセスが停止すれば、コンテナも停止します。
FastAPI用のDockerイメージをビルドする
ということで、何か作りましょう!🚀
FastAPI用のDockerイメージを、公式Pythonイメージに基づいてゼロからビルドする方法をお見せします。
これはほとんどの場合にやりたいことです。例えば:
- Kubernetesまたは同様のツールを使用する場合
- Raspberry Piで実行する場合
- コンテナ・イメージを実行してくれるクラウド・サービスなどを利用する場合
パッケージ要件
アプリケーションのパッケージ要件は通常、何らかのファイルに記述されているはずです。
パッケージ要件は主にインストールするために使用するツールに依存するでしょう。
最も一般的な方法は、requirements.txt ファイルにパッケージ名とそのバージョンを 1 行ずつ書くことです。
もちろん、FastAPI バージョンについて{.internal-link target=_blank}で読んだのと同じアイデアを使用して、バージョンの範囲を設定します。
例えば、requirements.txt は次のようになります:
fastapi[standard]>=0.113.0,<0.114.0
pydantic>=2.7.0,<3.0.0
そして通常、例えば pip を使ってこれらのパッケージの依存関係をインストールします:
$ pip install -r requirements.txt
---> 100%
Successfully installed fastapi pydantic
/// info | 情報
パッケージの依存関係を定義しインストールするためのフォーマットやツールは他にもあります。
///
FastAPIコードを作成する
appディレクトリを作成し、その中に入ります。- 空のファイル
__init__.pyを作成します。 - 次の内容で
main.pyファイルを作成します:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
Dockerfile
同じプロジェクト・ディレクトリにDockerfileというファイルを作成します:
# (1)!
FROM python:3.9
# (2)!
WORKDIR /code
# (3)!
COPY ./requirements.txt /code/requirements.txt
# (4)!
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
# (5)!
COPY ./app /code/app
# (6)!
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
-
公式のPythonベースイメージから始めます
-
現在の作業ディレクトリを
/codeに設定しますここに
requirements.txtファイルとappディレクトリを置きます。 -
要件が書かれたファイルを
/codeディレクトリにコピーします残りのコードではなく、最初に必要なファイルだけをコピーしてください。
このファイルは頻繁には変更されないので、Dockerはこのステップではそれを検知しキャッシュを使用し、次のステップでもキャッシュを有効にします。
-
要件ファイルにあるパッケージの依存関係をインストールします
--no-cache-dirオプションはダウンロードしたパッケージをローカルに保存しないようにpipに指示します。これは、同じパッケージをインストールするためにpipを再度実行する場合にのみ有効ですが、コンテナで作業する場合はそうではないです。/// note | 備考
--no-cache-dirはpipに関連しているだけで、Dockerやコンテナとは何の関係もないです。///
--upgradeオプションは、パッケージが既にインストールされている場合、pipにアップグレードするように指示します。何故ならファイルをコピーする前のステップはDockerキャッシュによって検出される可能性があるためであり、このステップも利用可能な場合はDockerキャッシュを使用します。
このステップでキャッシュを使用すると、開発中にイメージを何度もビルドする際に、毎回すべての依存関係をダウンロードしてインストールする代わりに多くの時間を節約できます。
-
./appディレクトリを/codeディレクトリの中にコピーする。これには最も頻繁に変更されるすべてのコードが含まれているため、Dockerのキャッシュはこれ以降のステップに簡単に使用されることはありません。
そのため、コンテナイメージのビルド時間を最適化するために、
Dockerfileの 最後 にこれを置くことが重要です。 -
内部でUvicornを使用する
fastapi runを使うためのコマンドを設定しますCMDは文字列のリストを取り、それぞれの文字列はスペースで区切られたコマンドラインに入力するものです。このコマンドは 現在の作業ディレクトリから実行され、上記の
WORKDIR /codeにて設定した/codeディレクトリと同じです。
/// tip | 豆知識
コード内の各番号バブルをクリックして、各行が何をするのかをレビューしてください。👆
///
/// warning | 注意
以下で説明する通り、CMD 命令は常に exec形式を使用してください。
///
CMD を使う - Exec形式
Docker命令 CMD は2つの形式で書けます:
✅ Exec 形式:
# ✅ Do this
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
⛔️ Shell 形式:
# ⛔️ Don't do this
CMD fastapi run app/main.py --port 80
FastAPIが正常にシャットダウンでき、lifespan events{.internal-link target=_blank}がトリガーされるように、常に exec 形式を使用してください。
詳しくは、shell形式とexec形式に関するDockerドキュメントをご覧ください。
これは docker compose を使用する場合にかなり目立つことがあります。より技術的な詳細は、このDocker ComposeのFAQセクションをご覧ください:Why do my services take 10 seconds to recreate or stop?。
ディレクトリ構造
これで、次のようなディレクトリ構造になるはずです:
.
├── app
│ ├── __init__.py
│ └── main.py
├── Dockerfile
└── requirements.txt
TLS Termination Proxyの裏側
Nginx や Traefik のような TLS Termination Proxy (ロードバランサ) の後ろでコンテナを動かしている場合は、--proxy-headersオプションを追加します。これにより、(FastAPI CLI経由で)Uvicornに対して、そのプロキシから送信されるヘッダを信頼し、アプリケーションがHTTPSの裏で実行されていることなどを示すよう指示します。
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
Dockerキャッシュ
このDockerfileには重要なトリックがあり、まず依存関係だけのファイルをコピーします。その理由を説明します。
COPY ./requirements.txt /code/requirements.txt
Dockerや他のツールは、これらのコンテナイメージを段階的にビルドし、1つのレイヤーを他のレイヤーの上に追加します。Dockerfileの先頭から開始し、Dockerfileの各命令によって作成されたファイルを追加していきます。
Dockerや同様のツールは、イメージをビルドする際に内部キャッシュも使用します。前回コンテナイメージを構築したときからファイルが変更されていない場合、ファイルを再度コピーしてゼロから新しいレイヤーを作成する代わりに、前回作成した同じレイヤーを再利用します。
ただファイルのコピーを避けるだけではあまり改善されませんが、そのステップでキャッシュを利用したため、次のステップでキャッシュを使うことができます。
例えば、依存関係をインストールする命令のためにキャッシュを使うことができます:
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
パッケージ要件のファイルは頻繁に変更されることはありません。そのため、そのファイルだけをコピーすることで、Dockerはそのステップではキャッシュを使用することができます。
そして、Dockerは次のステップのためにキャッシュを使用し、それらの依存関係をダウンロードしてインストールすることができます。そして、ここで多くの時間を節約します。✨ ...そして退屈な待ち時間を避けることができます。😪😆
パッケージの依存関係をダウンロードしてインストールするには数分かかりますが、キャッシュを使えばせいぜい数秒です。
加えて、開発中にコンテナ・イメージを何度もビルドして、コードの変更が機能しているかどうかをチェックすることになるため、多くの時間を節約することができます。
そしてDockerfileの最終行の近くですべてのコードをコピーします。この理由は、最も頻繁に変更されるものなので、このステップの後にあるものはほとんどキャッシュを使用することができないのためです。
COPY ./app /code/app
Dockerイメージをビルドする
すべてのファイルが揃ったので、コンテナ・イメージをビルドしましょう。
- プロジェクトディレクトリに移動します(
Dockerfileがある場所で、appディレクトリがあります)。 - FastAPI イメージをビルドします:
$ docker build -t myimage .
---> 100%
/// tip | 豆知識
末尾の . に注目してほしいです。これは ./ と同じ意味です。 これはDockerにコンテナイメージのビルドに使用するディレクトリを指示します。
この場合、同じカレント・ディレクトリ(.)です。
///
Dockerコンテナの起動する
- イメージに基づいてコンテナを実行します:
$ docker run -d --name mycontainer -p 80:80 myimage
確認する
Dockerコンテナのhttp://192.168.99.100/items/5?q=somequery や http://127.0.0.1/items/5?q=somequery (またはそれに相当するDockerホストを使用したもの)といったURLで確認できるはずです。
アクセスすると以下のようなものが表示されます:
{"item_id": 5, "q": "somequery"}
インタラクティブなAPIドキュメント
これらのURLにもアクセスできます: http://192.168.99.100/docs や http://127.0.0.1/docs (またはそれに相当するDockerホストを使用したもの)
アクセスすると、自動対話型APIドキュメント(Swagger UIが提供)が表示されます:
代替のAPIドキュメント
また、http://192.168.99.100/redoc や http://127.0.0.1/redoc (またはそれに相当するDockerホストを使用したもの)にもアクセスできます。
代替の自動ドキュメント(ReDocによって提供される)が表示されます:
単一ファイルのFastAPIでDockerイメージをビルドする
FastAPI が単一のファイル、例えば ./app ディレクトリのない main.py の場合、ファイル構造は次のようになります:
.
├── Dockerfile
├── main.py
└── requirements.txt
そうすれば、Dockerfileの中にファイルをコピーするために、対応するパスを変更するだけでよいです:
FROM python:3.9
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
# (1)!
COPY ./main.py /code/
# (2)!
CMD ["fastapi", "run", "main.py", "--port", "80"]
-
main.pyファイルを/codeディレクトリに直接コピーします(./appディレクトリなし)。 -
単一ファイル
main.py内のアプリケーションを配信するためにfastapi runを使用します。
fastapi run にファイルを渡すと、それがパッケージの一部ではなく単一ファイルであることを自動的に検出し、インポートしてFastAPIアプリを配信する方法を判断します。😎
デプロイメントのコンセプト
コンテナという観点から、デプロイのコンセプト{.internal-link target=_blank}に共通するいくつかについて、もう一度説明しましょう。
コンテナは主に、アプリケーションのビルドとデプロイのプロセスを簡素化するためのツールですが、これらのデプロイのコンセプトを扱うための特定のアプローチを強制するものではなく、いくつかの戦略があります。
良いニュースは、それぞれの異なる戦略には、すべてのデプロイメントのコンセプトをカバーする方法があるということです。🎉
これらのデプロイメントのコンセプトをコンテナの観点から見直してみましょう:
- HTTPS
- 起動時の実行
- 再起動
- レプリケーション(実行中のプロセス数)
- メモリ
- 開始前の事前ステップ
HTTPS
FastAPI アプリケーションの コンテナ・イメージ(および後で実行中の コンテナ)だけに焦点を当てると、通常、HTTPSは別のツールを用いて外部で処理されます。
例えばTraefikのように、HTTPSと証明書の自動取得を扱う別のコンテナである可能性もあります。
/// tip | 豆知識
TraefikはDockerやKubernetesなどと統合されているので、コンテナ用のHTTPSの設定や構成はとても簡単です。
///
あるいは、(コンテナ内でアプリケーションを実行しながら)クラウド・プロバイダーがサービスの1つとしてHTTPSを処理することもできます。
起動時および再起動時の実行
通常、コンテナの起動と実行を担当する別のツールがあります。
それは直接Dockerであったり、Docker Composeであったり、Kubernetesであったり、クラウドサービスであったりします。
ほとんどの場合(またはすべての場合)、起動時にコンテナを実行し、失敗時に再起動を有効にする簡単なオプションがあります。例えばDockerでは、コマンドラインオプションの--restartが該当します。
コンテナを使わなければ、アプリケーションを起動時や再起動時に実行させるのは面倒で難しいかもしれません。しかし、コンテナで作業する場合、ほとんどのケースでその機能はデフォルトで含まれています。✨
レプリケーション - プロセス数
Kubernetes や Docker Swarm モード、Nomad、あるいは複数のマシン上で分散コンテナを管理するための同様の複雑なシステムを使ってマシンのclusterを構成している場合、 各コンテナで(Workerを持つUvicornのような)プロセスマネージャを使用する代わりに、クラスター・レベルでレプリケーションを処理したいと思うでしょう。
Kubernetesのような分散コンテナ管理システムの1つは通常、入ってくるリクエストのロードバランシングをサポートしながら、コンテナのレプリケーションを処理する統合された方法を持っています。このことはすべてクラスタレベルにてです。
そのような場合、上記の説明のようにDockerイメージをゼロからビルドし、依存関係をインストールして、単一のUvicornプロセスを実行したいでしょう。複数のUvicornワーカーを使う代わりにです。
ロードバランサー
コンテナを使用する場合、通常はメイン・ポートでリスニングしているコンポーネントがあるはずです。それはおそらく、HTTPSを処理するためのTLS Termination Proxyでもある別のコンテナであったり、同様のツールであったりするでしょう。
このコンポーネントはリクエストの 負荷 を受け、 (うまくいけば) その負荷をバランスよく ワーカーに分配するので、一般に ロードバランサ とも呼ばれます。
/// tip | 豆知識
HTTPSに使われるものと同じTLS Termination Proxyコンポーネントは、おそらくロードバランサーにもなるでしょう。
///
そしてコンテナで作業する場合、コンテナの起動と管理に使用する同じシステムには、ロードバランサー(TLS Termination Proxyの可能性もある)からネットワーク通信(HTTPリクエストなど)をアプリのあるコンテナ(複数可)に送信するための内部ツールが既にあるはずです。
1つのロードバランサー - 複数のワーカーコンテナー
Kubernetesや同様の分散コンテナ管理システムで作業する場合、その内部のネットワーキングのメカニズムを使用することで、メインのポートでリッスンしている単一のロードバランサーが、アプリを実行している可能性のある複数のコンテナに通信(リクエスト)を送信できるようになります。
アプリを実行するこれらのコンテナには、通常1つのプロセス(たとえば、FastAPIアプリケーションを実行するUvicornプロセス)があります。これらはすべて同一のコンテナであり同じものを実行しますが、それぞれが独自のプロセスやメモリなどを持ちます。そうすることで、CPUの異なるコア、あるいは異なるマシンでの並列化を利用できます。
そして、ロードバランサーを備えた分散コンテナシステムは、順番にあなたのアプリを含む各コンテナにリクエストを分配します。つまり、各リクエストは、あなたのアプリを実行している複数のレプリケートされたコンテナの1つによって処理されます。
そして通常、このロードバランサーは、クラスタ内の他のアプリケーション(例えば、異なるドメインや異なるURLパスのプレフィックスの配下)へのリクエストを処理することができ、その通信をクラスタ内で実行されている他のアプリケーションのための適切なコンテナに送信します。
1コンテナにつき1プロセス
この種のシナリオでは、すでにクラスタ・レベルでレプリケーションを処理しているため、おそらくコンテナごとに単一の(Uvicorn)プロセスを持ちたいでしょう。
この場合、例えばコマンドラインオプションの --workers で、コンテナ内に複数のワーカーを持つことは避けたいでしょう。コンテナごとにUvicornのプロセスは1つだけにしたいでしょう(おそらく複数のコンテナが必要でしょう)。
(複数のワーカーの場合のように)コンテナ内に別のプロセスマネージャーを持つことは、クラスターシステムですでに対処しているであろう不要な複雑さを追加するだけです。
複数プロセスのコンテナと特殊なケース
もちろん、特殊なケースとして、コンテナ内で複数のUvicornワーカープロセスを起動させたい場合があります。
そのような場合、--workers コマンドラインオプションを使って、実行したいワーカー数を設定できます:
FROM python:3.9
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
# (1)!
CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"]
- ここでは
--workersコマンドラインオプションを使って、ワーカー数を4に設定しています。
以下は、それが理にかなっている場合の例です:
シンプルなアプリ
アプリケーションが、クラスタではなく単一サーバで実行できるほどシンプルである場合、コンテナ内にプロセスマネージャが欲しくなることがあります。
Docker Compose
Docker Composeで単一サーバ(クラスタではない)にデプロイすることもできますので、共有ネットワークとロードバランシングを維持しながら(Docker Composeで)コンテナのレプリケーションを管理する簡単な方法はないでしょう。
その場合、単一のコンテナで、プロセスマネージャが内部で複数のワーカープロセスを起動するようにします。
重要なのは、これらのどれも、盲目的に従わなければならない「絶対的なルール」ではないということです。これらのアイデアは、あなた自身のユースケースを評価し、あなたのシステムに最適なアプローチを決定するために使用できます。次の概念をどう管理するかを確認してください:
- セキュリティ - HTTPS
- 起動時の実行
- 再起動
- レプリケーション(実行中のプロセス数)
- メモリ
- 開始前の事前ステップ
メモリ
コンテナごとに単一のプロセスを実行すると、それらのコンテナ(レプリケートされている場合は1つ以上)によって消費される多かれ少なかれ明確に定義された、安定し制限された量のメモリを持つことになります。
そして、コンテナ管理システム(Kubernetesなど)の設定で、同じメモリ制限と要件を設定することができます。
そうすれば、コンテナが必要とするメモリ量とクラスタ内のマシンで利用可能なメモリ量を考慮して、利用可能なマシンにコンテナをレプリケートできるようになります。
アプリケーションがシンプルなものであれば、これはおそらく問題にはならないでしょうし、ハードなメモリ制限を指定する必要はないかもしれないです。
しかし、多くのメモリを使用している場合(たとえば機械学習モデルなど)、どれだけのメモリを消費しているかを確認し、各マシンで実行するコンテナの数を調整する必要があります(そしておそらくクラスタにマシンを追加します)。
コンテナごとに複数のプロセスを実行する場合、起動するプロセスの数が利用可能なメモリ以上に消費しないようにする必要があります。
開始前の事前ステップとコンテナ
コンテナ(DockerやKubernetesなど)を使っている場合、主に2つのアプローチがあります。
複数のコンテナ
複数のコンテナがあり、おそらくそれぞれが単一のプロセスを実行している場合(例えば、Kubernetesクラスタなど)、レプリケートされたワーカーコンテナを実行する前に、単一のコンテナで事前のステップの作業を行う別のコンテナを持ちたいと思うでしょう。
/// info | 情報
もしKubernetesを使用している場合, これはおそらくInit Containerでしょう。
///
ユースケースが事前のステップを並列で複数回実行するのに問題がない場合(例:データベースマイグレーションを実行するのではなく、データベースの準備ができたかをチェックするだけの場合)、メインプロセスを開始する直前に、それらのステップを各コンテナに入れることも可能です。
単一コンテナ
単純なセットアップで、単一のコンテナで複数のワーカープロセス(または1つのプロセスのみ)を起動する場合、アプリでプロセスを開始する直前に、同じコンテナで事前のステップを実行できます。
ベースDockerイメージ
以前は、公式のFastAPI Dockerイメージがありました:tiangolo/uvicorn-gunicorn-fastapi。しかし、現在は非推奨です。⛔️
おそらく、このベースDockerイメージ(またはその他の類似のもの)は使用しない方がよいでしょう。
すでにKubernetes(または他のもの)を使用していて、複数のコンテナで、クラスタレベルでレプリケーションを設定している場合。そのような場合は、上記で説明したようにゼロからイメージを構築する方がよいでしょう:FastAPI用のDockerイメージをビルドする。
また、複数のワーカーが必要な場合は、単純に --workers コマンドラインオプションを使用できます。
/// note | 技術詳細
このDockerイメージは、Uvicornが停止したワーカーの管理と再起動をサポートしていなかった頃に作成されたため、Uvicornと一緒にGunicornを使う必要がありました。これは、GunicornにUvicornワーカープロセスの管理と再起動をさせるだけのために、かなりの複雑さを追加していました。
しかし現在は、Uvicorn(および fastapi コマンド)が --workers をサポートしているため、自分でビルドする代わりにベースDockerイメージを使う理由はありません(コード量もだいたい同じです 😅)。
///
コンテナ・イメージのデプロイ
コンテナ(Docker)イメージを手に入れた後、それをデプロイするにはいくつかの方法があります。
例えば以下のリストの方法です:
- 単一サーバーのDocker Compose
- Kubernetesクラスタ
- Docker Swarmモードのクラスター
- Nomadのような別のツール
- コンテナ・イメージをデプロイするクラウド・サービス
uv を使ったDockerイメージ
uv を使ってプロジェクトのインストールと管理をしている場合は、uv Docker guideに従ってください。
まとめ
コンテナ・システム(例えばDockerやKubernetesなど)を使えば、すべてのデプロイメントのコンセプトを扱うのがかなり簡単になります:
- HTTPS
- 起動時の実行
- 再起動
- レプリケーション(実行中のプロセス数)
- メモリ
- 開始前の事前ステップ
ほとんどの場合、ベースとなるイメージは使用せず、公式のPython Dockerイメージをベースにしたコンテナイメージをゼロからビルドします。
DockerfileとDockerキャッシュ内の命令の順番に注意することで、ビルド時間を最小化し、生産性を最大化できます(そして退屈を避けることができます)。😎

