デプロイメントのコンセプト¶
FastAPIアプリケーション、あるいは実際にはあらゆる種類のWeb APIをデプロイする際、考慮すべきいくつかのコンセプトがあり、それらを利用することでアプリケーションをデプロイするための最適な方法を見つけることができます。
重要なコンセプトの一部は次のとおりです。
- セキュリティ - HTTPS
- 起動時の実行
- 再起動
- レプリケーション(実行中のプロセス数)
- メモリ
- 起動前の事前準備
これらがデプロイメントにどのように影響するかを見ていきます。
最終的な目標は、安全な方法でAPIクライアントにサービスを提供し、中断を回避し、コンピューティングリソース(例:リモートサーバー/仮想マシン)を可能な限り効率的に使用することです。🚀
これらのコンセプトについて、もう少し詳しく説明します。これにより、さまざまな環境、場合によってはまだ存在しない将来の環境でもAPIをデプロイする方法を決定するために必要な直感が得られることを願っています。
これらのコンセプトを考慮することで、独自のAPIをデプロイするための最適な方法を評価し、設計できるようになります。
次の章では、FastAPIアプリケーションをデプロイするための、より具体的なレシピを紹介します。
しかし、今はこれらの重要な概念的なアイデアを確認しましょう。これらのコンセプトは、他の種類のWeb APIにも適用されます。💡
セキュリティ - HTTPS¶
HTTPSに関する前章では、HTTPSがAPIに暗号化を提供する仕組みについて学びました。
また、HTTPSは通常、アプリケーションサーバーの外部コンポーネントであるTLSターミネーションプロキシによって提供されることも確認しました。
そして、HTTPS証明書を更新する責任を負うものがなければなりません。それは同じコンポーネントであることもあれば、別のコンポーネントであることもあります。
HTTPSのツール例¶
TLSターミネーションプロキシとして使用できるツールの一部は次のとおりです。
- Traefik
- 証明書の更新を自動的に処理します ✨
- Caddy
- 証明書の更新を自動的に処理します ✨
- Nginx
- 証明書更新のためのCertbotのような外部コンポーネントを使用します。
- HAProxy
- 証明書更新のためのCertbotのような外部コンポーネントを使用します。
- NginxのようなIngress Controllerを備えたKubernetes
- 証明書更新のためのcert-managerのような外部コンポーネントを使用します。
- クラウドプロバイダーのサービスの一部として内部的に処理されます (以下を参照 👇)
もう1つの選択肢は、HTTPSの設定を含む多くの作業を行うクラウドサービスを使用することです。これにはいくつかの制限があったり、より高額な料金がかかる場合があります。しかし、その場合、自分でTLSターミネーションプロキシを設定する必要はありません。
次の章で具体的な例をいくつか紹介します。
次に考慮すべき概念は、実際のAPIを実行するプログラム (例: Uvicorn) に関するものです。
プログラムとプロセス¶
実行中の「プロセス」について多く語るため、それが何を意味し、「プログラム」という言葉との違いを明確にしておくことは有用です。
プログラムとは¶
プログラムという言葉は、一般的に多くのものを指すのに使われます。
- あなたが書いたコード、Pythonファイル。
- オペレーティングシステムによって実行可能なファイル、例えば:
python
、python.exe
、またはuvicorn
。 - オペレーティングシステム上でCPUを使用し、メモリに情報を格納しながら実行中の特定のプログラム。これはプロセスとも呼ばれます。
プロセスとは¶
「プロセス」という言葉は通常、より具体的な方法で使われ、オペレーティングシステムで実行されているもののみを指します(上記の最後の点のように)。
- オペレーティングシステム上で実行中の特定のプログラム。
- これはファイルやコードを指すのではなく、オペレーティングシステムによって実行され、管理されているものを特に指します。
- どんなプログラム、どんなコードも、実行されているときにのみ何かを行うことができます。つまり、プロセスが実行されているときです。
- プロセスはあなたによって、またはオペレーティングシステムによって終了(または「強制終了」)される可能性があります。その時点で、実行/実行が停止し、何もできなくなります。
- コンピュータ上で実行されている各アプリケーションには、その背後に何らかのプロセスがあり、各実行中のプログラム、各ウィンドウなどがあります。そして、コンピュータがオンになっている間は、通常、多くのプロセスが同時に実行されています。
- 同じプログラムの複数のプロセスが同時に実行されることがあります。
オペレーティングシステムの「タスクマネージャー」や「システムモニター」(または類似のツール)をチェックすると、多くのプロセスが実行されているのを確認できます。
例えば、同じブラウザプログラム(Firefox、Chrome、Edgeなど)が複数のプロセスで実行されているのがわかるでしょう。通常、タブごとに1つのプロセスと、いくつかの追加プロセスが実行されています。
プロセスとプログラムという用語の違いを理解したところで、デプロイメントについて引き続き説明しましょう。
起動時に実行¶
ほとんどの場合、Web APIを作成したら、クライアントが常にアクセスできるように、中断することなく常に実行し続けたいと思うでしょう。もちろん、特定の状況でのみ実行したい特定の理由がない限り、ほとんどの場合、常に実行中で利用可能であることを望みます。
リモートサーバーで¶
リモートサーバー(クラウドサーバー、仮想マシンなど)を設定する場合、最も簡単な方法は、ローカルで開発するときと同じように、手動でfastapi run
(Uvicornを使用)または同様のコマンドを使用することです。
そして、それは機能し、開発中に役立つでしょう。
しかし、サーバーへの接続が失われると、実行中のプロセスはおそらく停止します。
また、サーバーが再起動された場合(例えば、アップデート後やクラウドプロバイダーからの移行後など)、おそらく気づかないでしょう。そのため、手動でプロセスを再起動する必要があることさえわかりません。したがって、APIはそのまま停止したままになります。😱
起動時に自動実行¶
一般的に、サーバープログラム (Uvicorn など) をサーバー起動時に自動的に起動し、人間の介入なしに、API が常に実行されるプロセス (Uvicorn が FastAPI アプリを実行しているなど) を持たせたいと思うでしょう。
別のプログラム¶
これを実現するために、通常、アプリケーションが起動時に実行されることを確認する別のプログラムを使用します。多くの場合、データベースなど、他のコンポーネントやアプリケーションも実行されることを確認します。
起動時に実行するツール例¶
このタスクを実行できるツールの例をいくつか紹介します。
- Docker
- Kubernetes
- Docker Compose
- SwarmモードのDocker
- Systemd
- Supervisor
- クラウドプロバイダーのサービスの一部として内部的に処理される
- その他...
次の章で、より具体的な例を紹介します。
再起動¶
アプリケーションが起動時に実行されることを確認するのと同様に、障害発生後に再起動されることも確認したいと思うでしょう。
私たちは間違いを犯す¶
私たち人間は、常に間違いを犯します。ソフトウェアにはほとんど常に、さまざまな場所にバグが潜んでいます。🐛
そして、私たち開発者は、それらのバグを見つけ、新しい機能を実装するにつれて(おそらく新しいバグも追加しながら😅)、コードを改善し続けています。
自動的に処理される小さなエラー¶
FastAPIでWeb APIを構築する際、コードにエラーがあった場合、FastAPIは通常、そのエラーをトリガーした単一のリクエストに限定します。🛡
クライアントは、そのリクエストに対して500 Internal Server Errorを受け取りますが、アプリケーションは完全にクラッシュするのではなく、次のリクエストに対して動作し続けます。
より大きなエラー - クラッシュ¶
それでも、UvicornとPythonをクラッシュさせてアプリケーション全体をクラッシュさせるコードを書いてしまうケースがあるかもしれません。💥
それでも、どこか一箇所でエラーが発生したからといってアプリケーションが停止したままになることを望まないでしょう。おそらく、少なくとも壊れていない「パス操作」については動作し続けることを望むでしょう。
クラッシュ後の再起動¶
しかし、実行中のプロセスをクラッシュさせるような深刻なエラーが発生した場合、外部コンポーネントがプロセスの再起動を担当することを望むでしょう。少なくとも数回は...
ヒント
...アプリケーション全体がすぐにクラッシュし続ける場合、永久に再起動し続けるのはおそらく意味がありません。しかし、そのようなケースでは、開発中に、または少なくともデプロイ直後に気づくでしょう。
したがって、主要なケース、つまり将来特定のケースで完全にクラッシュする可能性があり、それでも再起動する意味があるケースに焦点を当てましょう。
アプリケーションを再起動する責任を持つものは外部コンポーネントとして持ちたいと思うでしょう。なぜなら、その時点でUvicornとPythonを備えた同じアプリケーションはすでにクラッシュしているため、同じアプリケーションの同じコードで対処できることは何もないからです。
自動再起動するツール例¶
ほとんどの場合、起動時にプログラムを実行するために使用されるのと同じツールが、自動再起動の処理にも使用されます。
たとえば、これは次によって処理できます。
- Docker
- Kubernetes
- Docker Compose
- SwarmモードのDocker
- Systemd
- Supervisor
- クラウドプロバイダーのサービスの一部として内部的に処理される
- その他...
レプリケーション - プロセスとメモリ¶
FastAPIアプリケーションでは、Uvicornを実行するfastapi
コマンドのようなサーバープログラムを使用して、1つのプロセスで1回実行するだけで、複数のクライアントに同時にサービスを提供できます。
しかし、多くの場合、複数のワーカープロセスを同時に実行したいと思うでしょう。
複数プロセス - ワーカー¶
単一のプロセスで処理できるクライアントよりも多くのクライアントがあり(例えば、仮想マシンが大きすぎない場合)、サーバーのCPUに複数のコアがある場合、同じアプリケーションを同時に複数のプロセスで実行し、すべてのリクエストをそれらの間で分散させることができます。
同じAPIプログラムの複数のプロセスを実行する場合、それらは一般的にワーカーと呼ばれます。
ワーカープロセスとポート¶
HTTPSについてのドキュメントから覚えていますか?1つのサーバーでポートとIPアドレスの1つの組み合わせをリッスンできるプロセスは1つだけです。
これはまだ真実です。
したがって、複数のプロセスを同時に持つためには、ポートをリッスンする単一のプロセスがあり、それが何らかの方法で各ワーカープロセスに通信を転送する必要があります。
プロセスごとのメモリ¶
さて、プログラムがメモリに何かをロードするとき、例えば、機械学習モデルを変数に、または大きなファイルのコンテンツを変数にロードするとき、それらはすべてサーバーのメモリ(RAM)を少し消費します。
そして、複数のプロセスは通常、メモリを共有しません。これは、各実行中のプロセスが独自の変数、メモリを持っていることを意味します。そして、コードで大量のメモリを消費している場合、各プロセスが同量のメモリを消費します。
サーバーメモリ¶
たとえば、コードが1GBのサイズの機械学習モデルをロードする場合、APIで1つのプロセスを実行すると、少なくとも1GBのRAMを消費します。そして、4つのプロセス(4つのワーカー)を起動すると、それぞれが1GBのRAMを消費します。したがって、合計でAPIは4GBのRAMを消費します。
そして、リモートサーバーまたは仮想マシンが3GBのRAMしか持っていない場合、4GBを超えるRAMをロードしようとすると問題が発生します。🚨
複数プロセス - 例¶
この例では、マネージャープロセスが2つのワーカープロセスを開始および制御しています。
このマネージャープロセスは、おそらくIPのポートをリッスンし、すべての通信をワーカープロセスに転送するでしょう。
これらのワーカープロセスはアプリケーションを実行するものであり、リクエストを受信してレスポンスを返すための主要な計算を実行し、RAMに変数として配置したものをすべてロードします。
もちろん、同じマシンでは、アプリケーション以外にも他のプロセスも実行されている可能性があります。
興味深い詳細は、各プロセスによって使用されるCPUの割合は時間とともに大きく変化する可能性がありますが、メモリ(RAM)は通常、ほぼ安定していることです。
毎回同量の計算を行うAPIがあり、多数のクライアントがいる場合、CPU使用率も(常に急速に上下するのではなく)安定するでしょう。
レプリケーションツールと戦略の例¶
これを実現するにはいくつかの方法があり、次の章、例えばDockerとコンテナについて話すときに、特定の戦略について詳しく説明します。
考慮すべき主な制約は、パブリックIPのポートを処理する単一のコンポーネントが必要であるということです。そして、複製されたプロセス/ワーカーに通信を転送する方法が必要です。
いくつかの可能な組み合わせと戦略を以下に示します。
--workers
付きのUvicorn- 1つのUvicornプロセス マネージャーがIPとポートをリッスンし、複数のUvicornワーカープロセスを開始します。
- Kubernetesおよびその他の分散コンテナシステム
- Kubernetes層の何らかのものがIPとポートをリッスンします。レプリケーションは、それぞれ1つのUvicornプロセスを実行する複数のコンテナを持つことによって行われます。
- これを処理してくれるクラウドサービス
- クラウドサービスは、おそらくレプリケーションを処理してくれます。実行するプロセスや使用するコンテナイメージを定義させることができ、いずれにしても、ほとんどの場合単一のUvicornプロセスであり、クラウドサービスがそのレプリケーションを担当します。
ヒント
コンテナ、Docker、Kubernetesに関するこれらの項目の一部がまだよく理解できなくても心配しないでください。
コンテナイメージ、Docker、Kubernetesなどについては、今後の章(コンテナ内のFastAPI - Docker)で詳しく説明します。
開始前の事前ステップ¶
アプリケーションを起動する前に、いくつかのステップを実行したい場合が多くあります。
例えば、データベースのマイグレーションを実行したい場合などです。
しかし、ほとんどの場合、これらのステップは一度だけ実行したいと思うでしょう。
そのため、アプリケーションを起動する前に、これらの事前ステップを実行するための単一のプロセスを持つことを望むでしょう。
そして、その後アプリケーション自体に複数のプロセス(複数のワーカー)を起動する場合でも、それらの事前ステップを実行するプロセスは単一であることを確認する必要があります。もしこれらのステップが複数のプロセスによって実行された場合、それらは並行して実行されることで作業を重複させ、データベースのマイグレーションのようなデリケートなステップだった場合、互いに競合を引き起こす可能性があります。
もちろん、以前のステップを複数回実行しても問題ないケースもあります。その場合は、処理がはるかに簡単になります。
ヒント
また、設定によっては、アプリケーションを起動する前に事前ステップが不要な場合もあることに注意してください。
その場合、これらのことを心配する必要はありません。🤷
事前ステップ戦略の例¶
これは、システムをデプロイする方法に大きく依存し、おそらくプログラムの起動方法、再起動の処理方法などと関連付けられるでしょう。
いくつかのアイデアを以下に示します。
- アプリコンテナの前に実行されるKubernetesの「Initコンテナ」
- 以前のステップを実行し、その後アプリケーションを起動するbashスクリプト
- そのbashスクリプトを起動/再起動し、エラーを検出する方法は依然として必要です。
ヒント
コンテナでこれを行うためのより具体的な例は、今後の章で説明します。コンテナ内のFastAPI - Docker。
リソース利用率¶
あなたのサーバーはリソースであり、プログラムでCPUの計算時間と利用可能なRAMメモリを消費または利用できます。
システムのどの程度のリソースを消費/利用したいですか?「あまり多くない」と考えるのは簡単かもしれませんが、実際にはクラッシュしない限りできるだけ多く消費したいと思うでしょう。
3台のサーバーに料金を払っているのに、RAMとCPUを少ししか使っていない場合、おそらくお金を無駄にしている💸、そしておそらくサーバーの電力も無駄にしている🌎、などです。
その場合、サーバーを2台だけにして、それらのリソース(CPU、メモリ、ディスク、ネットワーク帯域幅など)のより高い割合を使用する方が良いかもしれません。
一方、2台のサーバーがあり、CPUとRAMを100%使用している場合、ある時点で1つのプロセスがより多くのメモリを要求し、サーバーはディスクを「メモリ」として使用する必要がある(これは数千倍遅くなる可能性があります)、またはクラッシュすることさえあります。あるいは、あるプロセスがいくつかの計算を行う必要があり、CPUが再び空くまで待つ必要があるかもしれません。
この場合、追加のサーバーを1台用意し、そこにいくつかのプロセスを実行して、すべてが十分なRAMとCPU時間を持つようにする方が良いでしょう。
APIの使用量が何らかの理由で急増する可能性もあります。もしかしたら、それがバイラルになったり、他のサービスやボットがそれを使い始めたりするかもしれません。そして、そのような場合に備えて、追加のリソースを確保しておきたいと思うかもしれません。
例えば、リソース利用率の目標を50%から90%の間という任意の数字に設定できます。重要なのは、それらがおそらく、デプロイメントを調整するために測定し、使用したい主要な要素になるということです。
サーバーで使用されているCPUとRAM、または各プロセスによって使用されている量をhtop
のようなシンプルなツールで確認できます。あるいは、サーバー全体に分散しているような、より複雑な監視ツールを使用することもできます。
まとめ¶
アプリケーションをデプロイする方法を決定する際に留意すべき主な概念をいくつか読みました。
- セキュリティ - HTTPS
- 起動時の実行
- 再起動
- レプリケーション(実行中のプロセス数)
- メモリ
- 起動前の事前準備
これらの考え方を理解し、適用する方法を学ぶことで、デプロイメントの設定や調整を行う際に必要な直感が得られるはずです。🤓
次のセクションでは、従うことができる戦略の具体的な例をさらに示します。🚀