概要
Dockerコンテナ作成時に仮想環境を起動し、FastAPI アプリを WSGI サーバで起動する必要がありました。
その際、UvicornやGunicorn、 WSGIとASGI、ワーカープロセスやワーカークラスなど多く登場してくるので、それぞれまとめました。
コード
以下のように記載することで実現できました。
DockerfileでCMD ["./startup.sh"]
と記載して、そのシェルスクリプトに以下のように書きます。
(このスクリプトは、コンテナの起動時に実行される主要なアプリケーション、サービス、またはタスクを開始するために使用できます)
. /sample_project/sample_app/.venv/bin/activate && gunicorn -w 4 -k uvicorn.workers.UvicornWorker sample_app.app.main:app --bind 0.0.0.0
もしくは、Dockerfile
に直接以下のように追記するでも大丈夫そうです。
CMD ["/bin/bash", "-c", "python -m venv .venv && source .venv/bin/activate && . /sample_project/sample_app/.venv/bin/activate && gunicorn -w 4 -k uvicorn.workers.UvicornWorker sample_app.app.main:app --bind 0.0.0.0"]
WSGIとASGIって?
次に、コマンドの意味を説明する前に、用語の意味を確認したいと思います。
まず、Gunicorn(WSGI)もUnivorn(ASGI)もPythonウェブアプリケーションをウェブサーバーと連携させるために必要なもので、簡単に言えば、両者の間に入って色々やってくれるサーバ(インターフェース)です。
Gunicornとは?
- 正式名称はGreen Unicornで、グニコーンと呼びます。
- WSGI準拠のPythonアプリケーションを実行するためのサーバ。
- 同期処理アプリケーションに向いており、多くのPythonウェブフレームワークと互換性があります。
WSGIとは?
- Web Server Gateway Interface。(調べたら、ウィズギーって呼ばれているらしい)
- Pythonウェブアプリケーションとウェブサーバーの間の標準的なインターフェース。
- 同期的なインターフェースであり、リクエストを逐次処理。
- 上述の通り、Pythonの多くのウェブフレームーワークがWSGIに準拠しています。
- Django、Flask、CherryPyなど。FastAPIはASGI準拠ですが、WSGIを介して実行することもできます
Uvicornとは?
- ASGI準拠のPythonアプリケーションを実行するためのサーバ。
- 非同期処理が得意で、リアルタイムなアプリケーションや非同期タスクの処理が効率的。
- 上述したFastAPIなどの非同期ウェブフレームワークで利用されます。
ASGIとは?
- Asynchronous Server Gateway Interface。(調べたら、省略名称はないっぽい)
- Pythonの非同期Webアプリケーションを開発および実行するためのインターフェース。
- 非同期処理(非同期I/O操作)をサポートし、複数のタスクを同時に実行できる(=同時に多くのリクエストを非同期に処理できる)。
- 高いスケーラビリティを提供。WebSocket通信などのリアルタイム通信をサポート。
コマンドの意味
ということで、上述のコマンドの意味について。
gunicorn -w 4 -k uvicorn.workers.UvicornWorker sample_app.app.main:app --bind 0.0.0.0
-
gunicorn
: Gunicornコマンドを実行する、という意味 -
-w 4
: Gunicornのワーカープロセスの数を4に指定。ワーカープロセスは、並行リクエストを処理するために使用されます。 -
-k uvicorn.workers.UvicornWorker
: GunicornのワーカークラスをUvicorn
ワーカーに指定。 -
sample_app.app.main:app
: FastAPIアプリケーションのエントリーポイントを指定。main.py
があるとこです。 -
--bind 0.0.0.0
: Gunicornがリスンするホストとポートを指定。この場合、0.0.0.0
なのですべてのネットワークインターフェースを受け付けています。
つまり、Gunicornが4つのワーカープロセスを生成し、各ワーカープロセスがUvicornを介してFastAPIアプリケーションを実行し、リクエストを処理する構成、ということになります。
上述の通り、Gunicornは同期的なWSGIサーバーですが、マルチプロセスの利点を活用して多数の同時リクエストを処理する、ということです。同期的な処理の各ワーカープロセス(サブプロセス)は、独立してリクエストを受け取り、処理します。これにより、並行処理(一つのワーカープロセスがリクエストを処理している間に、他のワーカープロセスが新しいリクエストを処理)が実現できます。ロードバランス(負荷分散)にもなりますね。Gunicornはワーカープロセス管理機能でもある、ということです。
ワーカークラスUvicornWorker
とは?
ちなみに、UvicornWorker
というワーカークラスを指定していますが、他にもsync
もあります。
sync
は同期的な処理に対応し、UvicornWorker
は非同期的な処理に対応しています。
今回はFastAPIだったので、UvicornWorker
を指定し、Uvicornを内部的に起動します。
つまり、GunicornがHTTPリクエストを受け取ると、それをUvicornに委譲し、UvicornがASGIアプリケーションを実行してリクエストを処理する、ということですね。
ローカル環境で起動する場合
ちなみに、ローカル環境で起動する際は、以下のコマンドで実施しました。
uvicorn app.main:app --reload
Gunicornのような同期的なWSGIサーバーは、通常は複数のワーカープロセスを起動してリクエストを並行処理できるようにしますが、Uvicornは単一のプロセスで非同期処理によって多数の同時リクエストを効率的に処理できます。なので、もちろんワーカープロセス数の指定など不要です。
本番環境ではgunicornを使ってロードバランシングをサポートし、複数のワーカープロセス間でリクエストを均等に分散する必要がありましたが、その必要もないので、通常開発者は上記のコマンドで実施していると思います(gunicornを使った方がセキュリティ的に良いとも)。
Uvicorn単体で本番環境も使えないのか?って思いますが、その場合は、Webサーバ(NginxやApache)側でセキュリティ、負荷分散、プロセス管理などを補完する必要があります。単純なアプリケーションであればgunicornを介したUvicornを直接使用し、NginxなどのWebサーバが不要になるケースもあるかもしれませんが。
また、--reload
オプションを使用すると、コードが変更された場合に自動的にリロードされ、開発がスムーズです。デバッグログもリアルタイムで出てくるので嬉しいですね。