デプロイの概念¶
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コントローラーを使用した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(例:FastAPIアプリを実行しているUvicorn)で常に実行されるプロセスがあることを望むでしょう。
独立したプログラム¶
これを実現するには、通常、起動時にアプリケーションが実行されることを保証する独立したプログラムが必要になります。また多くの場合、データベースなど、他のコンポーネントやアプリケーションも実行されるようにします。
起動時に実行するツールの例¶
このジョブを実行できるツールの例をいくつか紹介します。
- 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つの組み合わせでリッスンできることを思い出してください。
これは今でも当てはまります。
したがって、複数のプロセスを同時に持つことができるように、ポートでリッスンする単一のプロセスがあり、それが何らかの方法で各ワーカープロセスに通信を送信する必要があります。
プロセスごとのメモリ¶
プログラムがメモリにものをロードするとき、たとえば、変数内の機械学習モデルや、変数内の大きなファイルの内容など、すべてがサーバーのメモリ(RAM)を少し消費します。
また、複数のプロセスは通常、メモリを共有しません。これは、実行中の各プロセスに、独自の事物、変数、メモリがあることを意味します。また、コードで大量のメモリを消費している場合は、各プロセスが同等の量のメモリを消費します。
サーバーメモリ¶
たとえば、コードが1 GBのサイズの機械学習モデルをロードする場合、APIで1つのプロセスを実行すると、少なくとも1 GBのRAMが消費されます。そして、4つのプロセス(4つのワーカー)を開始すると、それぞれが1 GBのRAMを消費します。したがって、合計で、APIは4 GBのRAMを消費します。
また、リモートサーバーまたは仮想マシンに3 GBのRAMしかない場合、4 GBを超える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%の間などです。重要なのは、これらが、デプロイを調整するために測定し使用する主なものとなる可能性が高いということです。
htop
のようなシンプルなツールを使用して、サーバーで使用されているCPUとRAM、または各プロセスで使用されている量を確認できます。または、サーバー全体に分散されている可能性のある、より複雑な監視ツールを使用することもできます。
まとめ¶
ここでは、アプリケーションをどのようにデプロイするかを決定する際に、おそらく留意する必要がある主な概念のいくつかについて説明しました。
- セキュリティ - HTTPS
- 起動時に実行する
- 再起動
- 複製 (実行中のプロセス数)
- メモリ
- 開始前の手順
これらのアイデアとそれらを適用する方法を理解することで、デプロイを構成および調整する際に、あらゆる決定を下すのに必要な直感が得られるはずです。🤓
次のセクションでは、実行できる可能性のある具体的な戦略の例をさらに示します。🚀