コンテンツへスキップ

デプロイメントの概念

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のような外部コンポーネントを使用して証明書を更新する場合
  • クラウドプロバイダーのサービスの一部として内部的に処理されます(以下参照 👇)

別の選択肢として、HTTPSの設定を含むより多くの作業を行うクラウドサービスを使用することもできます。制限があったり、より高額になる場合があります。しかし、その場合、自分でTLS終端プロキシを設定する必要はありません。

次の章で具体的な例を示します。


次に考慮すべき概念は、実際の API(例: Uvicorn)を実行するプログラムに関するものです。

プログラムとプロセス

プロセス」という言葉をたくさん使うので、それが何を意味するのか、そして「プログラム」という言葉との違いを明確にしておくことが役立ちます。

プログラムとは

プログラムという言葉は、一般的に多くのことを指すために使用されます。

  • あなたが書いたコードPythonファイル
  • オペレーティングシステムが実行できるファイル、例: pythonpython.exeuvicorn
  • オペレーティングシステム上で、CPUを使用し、メモリに何かを保存している、実行中の特定のプログラム。これはプロセスとも呼ばれます。

プロセスとは

プロセスという言葉は、通常、より具体的に、実行中のオペレーティングシステム内のもの(上記の最後の点のように)のみを指すために使用されます。

  • オペレーティングシステム上で実行中の特定のプログラム。
    • これはファイルやコードを指すのではなく、オペレーティングシステムによって実行され、管理されているものに具体的に言及しています。
  • あらゆるプログラム、あらゆるコードは、実行されているときにのみ物事を行うことができます。つまり、プロセスが実行されているときです。
  • プロセスは、あなたによって、またはオペレーティングシステムによって終了(または「キル」)される可能性があります。その時点で、実行/実行が停止し、もはや物事を行うことができなくなります。
  • コンピューターで実行している各アプリケーションには、その背後に何らかのプロセスがあります。実行中の各プログラム、各ウィンドウなど。そして、コンピューターがオンになっている間は、通常、多くのプロセスが同時に実行されています。
  • 同じプログラム複数のプロセスが同時に実行される可能性があります。

オペレーティングシステムの「タスクマネージャー」または「システムモニター」(または類似のツール)を確認すると、実行中の多くのプロセスが表示されます。

そして、例えば、同じブラウザプログラム(Firefox、Chrome、Edgeなど)を実行している複数のプロセスがあることがわかります。通常、タブごとに1つのプロセスと、いくつかの追加プロセスが実行されます。


これでプロセスプログラムという用語の違いがわかったので、デプロイメントについて話し続けることにしましょう。

起動時の実行

ほとんどの場合、Web API を作成する際には、クライアントが常にアクセスできるように、中断なく常に実行されることを望むでしょう。これはもちろん、特定の状況でのみ実行したいという理由がない限りですが、ほとんどの場合、常に実行され利用可能であることを望みます。

リモートサーバーにて

リモートサーバー(クラウドサーバー、仮想マシンなど)を設定する場合、最も簡単なのは、ローカルで開発するときと同じように、fastapi run(Uvicorn を使用)または同様のものを手動で使用することです。

そして、それは機能し、開発中に役立ちます。

しかし、サーバーへの接続が失われた場合、実行中のプロセスはおそらく終了します。

そして、サーバーが再起動された場合(例えば、アップデートの後、またはクラウドプロバイダーからの移行)、おそらくそれに気づかないでしょう。そして、そのため、プロセスを手動で再起動する必要があることすらわからないでしょう。したがって、API は単に停止したままになります。😱

起動時に自動実行

一般的に、サーバーの起動時にサーバープログラム(例: Uvicorn)が自動的に開始され、人間の介入なしに、常に API を実行するプロセス(例: FastAPI アプリを実行する Uvicorn)があることを望むでしょう。

別のプログラム

これを達成するために、通常は別のプログラムがあり、それが起動時にアプリケーションを実行するようにします。そして多くの場合、データベースなど、他のコンポーネントやアプリケーションも実行するようにします。

起動時に実行するための例示ツール

このジョブを実行できるツールの例には、以下のようなものがあります。

  • Docker
  • Kubernetes
  • Docker Compose
  • Docker Swarmモード
  • Systemd
  • Supervisor
  • クラウドプロバイダーのサービスの一部として内部的に処理されます
  • その他...

次の章でより具体的な例を示します。

再起動

アプリケーションを起動時に実行させることと同様に、失敗後に再起動されるようにしたい場合も多いでしょう。

私たちは間違いを犯します

私たち人間は、常に間違いを犯します。ソフトウェアには、ほとんど常に、さまざまな場所に隠れたバグがあります。🐛

そして、開発者である私たちは、それらのバグを見つけたり、新機能(おそらく新しいバグも追加する😅)を実装したりしながら、コードを改善し続けます。

小さなエラーは自動的に処理されます

FastAPI で Web API を構築する場合、コードにエラーがあっても、FastAPI は通常、エラーを引き起こした単一のリクエストにそれを封じ込めます。🛡

クライアントは、そのリクエストに対して500 Internal Server Errorを受け取りますが、アプリケーションは次のリクエストのために動作し続け、完全にクラッシュすることはありません。

より大きなエラー - クラッシュ

それでも、アプリケーション全体をクラッシュさせるようなコードを記述した場合、Uvicorn と Python がクラッシュすることがあります。💥

それでも、エラーが1箇所にあったからといって、アプリケーションが停止したままであることを望まないでしょう。少なくとも壊れていないパス操作については、実行し続けることを望むでしょう。

クラッシュ後の再起動

しかし、実行中のプロセスをクラッシュさせるような非常に深刻なエラーの場合、プロセスを再起動する担当の外部コンポーネントが少なくとも数回は必要になるでしょう。

ヒント

…しかし、アプリケーション全体がすぐにクラッシュする場合は、永遠に再起動し続けるのは意味がないでしょう。しかし、そのような場合、開発中、または少なくともデプロイ直後に気づくでしょう。

そのため、将来的に特定の場合に完全にクラッシュし、それでも再起動する意味がある主なケースに焦点を当てましょう。

アプリケーションの再起動を担当するものは、外部コンポーネントである方が良いでしょう。なぜなら、その時点では、Uvicorn と Python を含む同じアプリケーションがすでにクラッシュしており、同じアプリの同じコードでは何もできないからです。

自動再起動のための例示ツール

ほとんどの場合、起動時にプログラムを実行するために使用されるのと同じツールが、自動再起動を処理するためにも使用されます。

例えば、これは以下によって処理される場合があります。

  • Docker
  • Kubernetes
  • Docker Compose
  • Docker Swarmモード
  • Systemd
  • Supervisor
  • クラウドプロバイダーのサービスの一部として内部的に処理されます
  • その他...

レプリケーション - プロセスとメモリ

FastAPI アプリケーションで、fastapiコマンドのようなサーバープログラムを使用し、Uvicorn を実行する場合、1つのプロセスで実行すると、複数のクライアントに同時にサービスを提供できます。

しかし、多くの場合、同時に複数のワーカープロセスを実行したいでしょう。

複数のプロセス - ワーカー

単一のプロセスで処理できる以上のクライアントがいる場合(例えば、仮想マシンがそれほど大きくない場合)で、サーバーの CPU に複数のコアがある場合、同じアプリケーションを実行する複数のプロセスを同時に実行し、それらすべてにリクエストを分散させることができます。

同じ API プログラムの複数のプロセスを実行する場合、それらは一般的にワーカーと呼ばれます。

ワーカープロセスとポート

HTTPS についてのドキュメントから、サーバー上の1つのポートとIPアドレスの組み合わせごとに1つのプロセスしかリッスンできないことを覚えていますか?

これは依然として真実です。

したがって、同時に複数のプロセスを持つことができるようにするには、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つのワーカープロセスを開始および制御するマネージャープロセスがあります。

このマネージャープロセスは、おそらくポートでリッスンしており、通信をワーカープロセスに転送します。

これらのワーカープロセスは、アプリケーションを実行するプロセスであり、リクエストを受信してレスポンスを返すための主要な計算を実行し、RAM の変数に設定したものをロードします。

そしてもちろん、同じマシンでは、アプリケーションとは別に、他のプロセスも実行されている可能性があります。

興味深い点は、各プロセスが使用するCPUの割合は時間とともに大きく変動する可能性があることですが、メモリ(RAM)は通常、ほぼ安定しています。

API が毎回同程度の計算を行う場合で、クライアントが多い場合、CPU 使用率も(急速に上下するのではなく)安定する可能性が高いです。

レプリケーションツールと戦略の例

これを達成するためのアプローチはいくつかありますが、次の章で特定の戦略についてさらに詳しく説明します。例えば、Docker とコンテナについて話すときです。

考慮すべき主な制約は、パブリックIPポートを処理する単一のコンポーネントが必要であるということです。そして、それをレプリケートされたプロセス/ワーカーに通信を転送する方法を持っている必要があります。

以下に、可能な組み合わせと戦略をいくつか示します。

  • Uvicorn--workers オプション
    • 1つの UvicornプロセスマネージャーIPポートをリッスンし、複数の Uvicorn ワーカープロセスを開始します。
  • Kubernetes およびその他の分散コンテナシステム
    • Kubernetes レベルの何かがIPポートをリッスンします。レプリケーションは、Uvicorn プロセスを1つずつ実行する複数のコンテナを持つことによって行われます。
  • これを処理するクラウドサービス
    • クラウドサービスは、おそらくレプリケーションを処理してくれます。おそらく、実行するプロセス、または使用するコンテナイメージを定義できますが、いずれにしても、おそらく単一の Uvicorn プロセスであり、クラウドサービスがそれをレプリケートする責任を負います。

ヒント

これらのコンテナ、Docker、Kubernetes に関する項目がまだよくわからない場合でも心配しないでください。

コンテナイメージ、Docker、Kubernetes などについては、将来の章「コンテナでの FastAPI - Docker」で詳しく説明します。

開始前の手順

アプリケーションを開始する前にいくつかの手順を実行したい場合が多くあります。

例えば、データベースマイグレーションを実行したい場合があります。

しかし、ほとんどの場合、これらの手順は一度だけ実行したいでしょう。

したがって、アプリケーションを開始する前に、それらの前の手順を実行する単一のプロセスが必要になります。

そして、後でアプリケーション自体の複数のプロセス(複数のワーカー)を開始する場合であっても、それらの前の手順を実行する単一のプロセスであることを確認する必要があります。もしそれらの手順が複数のプロセスによって実行された場合、それらは並行して実行することで作業を重複させ、もし手順がデータベースマイグレーションのようなデリケートなものであれば、互いに競合を引き起こす可能性があります。

もちろん、前の手順を複数回実行しても問題がない場合もあります。その場合は、処理がはるかに簡単になります。

ヒント

また、セットアップによっては、アプリケーションを開始する前にまったく前の手順が必要ない場合もあることに注意してください。

その場合、これらについて心配する必要はありません。🤷

開始前の手順戦略の例

これはデプロイメントの方法に大きく依存し、プログラムの起動方法、再起動の処理方法などと関連付けられるでしょう。

以下に、考えられるアイデアをいくつか示します。

  • Kubernetes の「Init Container」で、アプリコンテナの前に実行されます。
  • 前の手順を実行してからアプリケーションを起動する bash スクリプト
    • それでも、*その* bash スクリプトを起動/再起動し、エラーを検出するなどの方法が必要になります。

ヒント

コンテナでの実行方法については、将来の章「コンテナでの FastAPI - Docker」でより具体的な例を示します。

リソース利用率

サーバーはリソースであり、プログラムで CPU の計算時間と利用可能な RAM メモリを消費または利用できます。

システムリソースのどれくらいを消費/利用したいですか?「それほど多くない」と考えるのは簡単かもしれませんが、実際には、クラッシュせずに可能な限り多く消費したいでしょう。

3台のサーバーの料金を支払っているのに、その RAM と CPU のほんの一部しか使用していない場合、おそらくお金を無駄にしており、おそらくサーバーの電力も無駄にしています。💸🌎

その場合、サーバーを2台にして、リソース(CPU、メモリ、ディスク、ネットワーク帯域幅など)の使用率を高くする方が良いかもしれません。

一方、サーバーが2台あり、CPUとRAMの100%を使用している場合、ある時点でプロセスがより多くのメモリを要求し、サーバーはディスクを「メモリ」として使用しなければならず(これは数千倍遅くなる可能性があります)、あるいはクラッシュすることもあります。あるいは、プロセスが計算を実行する必要がある場合、CPU が再び利用可能になるまで待たなければならないかもしれません。

この場合、追加のサーバーを1台取得し、そこにいくつかのプロセスを実行して、すべてが十分な RAM と CPU 時間を得られるようにする方が良いでしょう。

また、API の使用量が何らかの理由で急増する可能性もあります。 viral になった、または他のサービスやボットが使用し始めたのかもしれません。そして、それらの場合に安全であるために追加のリソースを用意しておきたいかもしれません。

例えば、50% から 90% の間のような、任意の値をターゲットとして設定できます。重要なのは、これらがデプロイメントを調整するために測定して使用したい主なものであるということです。

htopのような単純なツールを使用して、サーバーで使用されている CPU と RAM、または各プロセスによって使用されている量を確認できます。または、より複雑な監視ツールを使用することもできます。これらはサーバー全体に分散している場合があります。

まとめ

ここでは、アプリケーションのデプロイ方法を決定する際に考慮する必要がある主な概念のいくつかを読んできました。

  • セキュリティ - HTTPS
  • 起動時の実行
  • 再起動
  • レプリケーション(実行中のプロセス数)
  • メモリ
  • 起動前の事前準備

これらのアイデアを理解し、それらを適用する方法を学ぶことで、デプロイメントの設定と調整を行う際に必要な直感を得られるはずです。🤓

次のセクションでは、従うことができる可能性のある戦略のより具体的な例を示します。🚀