diff --git a/docs/ja/docs/deployment/docker.md b/docs/ja/docs/deployment/docker.md index f10312b51..ca9dedc3c 100644 --- a/docs/ja/docs/deployment/docker.md +++ b/docs/ja/docs/deployment/docker.md @@ -1,71 +1,157 @@ -# Dockerを使用したデプロイ +# コンテナ内のFastAPI - Docker -このセクションでは以下の使い方の紹介とガイドへのリンクが確認できます: +FastAPIアプリケーションをデプロイする場合、一般的なアプローチは**Linuxコンテナ・イメージ**をビルドすることです。 -* **5分**程度で、**FastAPI** のアプリケーションを、パフォーマンスを最大限に発揮するDockerイメージ (コンテナ)にする。 -* (オプション) 開発者として必要な範囲でHTTPSを理解する。 -* **20分**程度で、自動的なHTTPS生成とともにDockerのSwarmモード クラスタをセットアップする (月5ドルのシンプルなサーバー上で)。 -* **10分**程度で、DockerのSwarmモード クラスタを使って、HTTPSなどを使用した完全な**FastAPI** アプリケーションの作成とデプロイ。 +基本的には **Docker**を用いて行われます。生成されたコンテナ・イメージは、いくつかの方法のいずれかでデプロイできます。 -デプロイのために、**Docker** を利用できます。セキュリティ、再現性、開発のシンプルさなどに利点があります。 +Linuxコンテナの使用には、**セキュリティ**、**反復可能性(レプリカビリティ)**、**シンプリシティ**など、いくつかの利点があります。 -Dockerを使う場合、公式のDockerイメージが利用できます: +!!! tip + TODO: なぜか遷移できない + お急ぎで、すでにこれらの情報をご存じですか? [以下の`Dockerfile`の箇所👇](#build-a-docker-image-for-fastapi)へジャンプしてください。 -## tiangolo/uvicorn-gunicorn-fastapi +
+Dockerfile プレビュー 👀 -このイメージは「自動チューニング」機構を含んでいます。犠牲を払うことなく、ただコードを加えるだけで自動的に高パフォーマンスを実現できます。 +```Dockerfile +FROM python:3.9 -ただし、環境変数や設定ファイルを使って全ての設定の変更や更新を行えます。 +WORKDIR /code -!!! tip "豆知識" - 全ての設定とオプションを確認するには、Dockerイメージページを開いて下さい: tiangolo/uvicorn-gunicorn-fastapi. +COPY ./requirements.txt /code/requirements.txt -## `Dockerfile` の作成 +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt -* プロジェクトディレクトリへ移動。 -* 以下の`Dockerfile` を作成: +COPY ./app /code/app -```Dockerfile -FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7 +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] -COPY ./app /app +# If running behind a proxy like Nginx or Traefik add --proxy-headers +# CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80", "--proxy-headers"] ``` -### より大きなアプリケーション +
-[Bigger Applications with Multiple Files](tutorial/bigger-applications.md){.internal-link target=_blank} セクションに倣う場合は、`Dockerfile` は上記の代わりに、以下の様になるかもしれません: +## コンテナとは何か -```Dockerfile -FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7 +コンテナ(主にLinuxコンテナ)は、同じシステム内の他のコンテナ(他のアプリケーションやコンポーネント)から隔離された状態を保ちながら、すべての依存関係や必要なファイルを含むアプリケーションをパッケージ化する非常に**軽量**な方法です。 -COPY ./app /app/app -``` +Linuxコンテナは、ホスト(マシン、仮想マシン、クラウドサーバーなど)の同じLinuxカーネルを使用して実行されます。これは、(OS全体をエミュレートする完全な仮想マシンと比べて)非常に軽量であることを意味します。 -### Raspberry Piなどのアーキテクチャ +このように、コンテナは**リソースをほとんど消費しません**が、プロセスを直接実行するのに匹敵する量です(仮想マシンはもっと消費します)。 -Raspberry Pi (ARMプロセッサ搭載)やそれ以外のアーキテクチャでDockerが作動している場合、(マルチアーキテクチャである) Pythonベースイメージを使って、一から`Dockerfile`を作成し、Uvicornを単体で使用できます。 +コンテナはまた、独自の**分離された**実行プロセス(通常は1つのプロセスのみ)や、ファイルシステム、ネットワークを持ちます。 このことはデプロイ、セキュリティ、開発などを簡素化させます。 -この場合、`Dockerfile` は以下の様になるかもしれません: +## コンテナ・イメージとは何か -```Dockerfile -FROM python:3.7 +**コンテナ**は、**コンテナ・イメージ**から実行されます。 -RUN pip install fastapi uvicorn +コンテナ・イメージは、コンテナ内に存在すべきすべてのファイルや環境変数、そしてデフォルトのコマンド/プログラムを**静的に**バージョン化したものです。 ここでの**静的**とは、コンテナ**イメージ**は実行されておらず、パッケージ化されたファイルとメタデータのみであることを意味します。 -EXPOSE 80 +保存された静的コンテンツである「**コンテナイメージ**」とは対照的に、「**コンテナ**」は通常、実行中のインスタンス、つまり**実行**されているものを指します。 -COPY ./app /app +**コンテナ**が起動され実行されるとき(**コンテナイメージ**から起動されるとき)、ファイルや環境変数などが作成されたり変更されたりする可能性があります。 -CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] +これらの変更はそのコンテナ内にのみ存在しますが、基盤となるコンテナ・イメージには残りません(ディスクに保存されません)。 + +コンテナイメージは **プログラム** ファイルやその内容、例えば `python` と `main.py` ファイルに匹敵します。 + +そして、**コンテナ**自体は(**コンテナイメージ**とは対照的に)イメージをもとにした実際の実行中のインスタンスであり、**プロセス**に匹敵します。 + +実際、コンテナが実行されているのは、**プロセスが実行されている**ときだけです(通常は単一のプロセスだけです)。 コンテナ内で実行中のプロセスがない場合、コンテナは停止します。 + +## コンテナ・イメージ + +Dockerは、**コンテナ・イメージ**と**コンテナ**を作成・管理するための主要なツールの1つです。 + +そして、DockerにはDockerイメージ(コンテナ)を共有するDocker Hubというものがあります。 + +Docker Hubは 多くのツールや環境、データベース、アプリケーションに対応している予め作成された**公式のコンテナ・イメージ**をパブリックに提供しています。 + +例えば、公式イメージの1つにPython Imageがあります。 + +その他にも、データベースなどさまざまなイメージがあります: + +* PostgreSQL +* MySQL +* MongoDB +* Redis, etc. + +予め作成されたコンテナ・イメージを使用することで、異なるツールを**組み合わせて**使用することが非常に簡単になります。例えば、新しいデータベースを試す場合に特に便利です。ほとんどの場合、**公式イメージ**を使い、環境変数で設定するだけで良いです。 + +そうすれば多くの場合、コンテナとDockerについて学び、その知識をさまざまなツールやコンポーネントによって再利用することができます。 + +つまり、データベース、Pythonアプリケーション、Reactフロントエンド・アプリケーションを備えたウェブ・サーバーなど、さまざまなものを**複数のコンテナ**で実行し、それらを内部ネットワーク経由で接続します。 + +すべてのコンテナ管理システム(DockerやKubernetesなど)には、こうしたネットワーキング機能が統合されています。 + +## コンテナとプロセス + +通常、**コンテナ・イメージ**はそのメタデータに**コンテナ**の起動時に実行されるデフォルトのプログラムまたはコマンドと、そのプログラムに渡されるパラメータを含みます。コマンドラインでの操作とよく似ています。 + +**コンテナ**が起動されると、そのコマンド/プログラムが実行されます(ただし、別のコマンド/プログラムをオーバーライドして実行させることもできます)。 + +コンテナは、**メイン・プロセス**(コマンドまたはプログラム)が実行されている限り実行されます。 + +コンテナは通常**1つのプロセス**を持ちますが、メイン・プロセスからサブ・プロセスを起動することも可能で、そうすれば同じコンテナ内に**複数のプロセス**を持つことになります。 + +しかし、**少なくとも1つの実行中のプロセス**がなければ、実行中のコンテナを持つことはできないです。メイン・プロセスが停止すれば、コンテナも停止します。 + +## Build a Docker Image for FastAPI + +ということで、何か作りましょう!🚀 + +FastAPI用の**Dockerイメージ**を、**公式Python**イメージに基づいて**ゼロから**ビルドする方法をお見せします。 + +これは**ほとんどの場合**にやりたいことです。例えば: + +* **Kubernetes**または同様のツールを使用する場合 +* **Raspberry Pi**で実行する場合 +* コンテナ・イメージを実行してくれるクラウド・サービスなどを利用する場合 + +### パッケージ要件(package requirements) + +アプリケーションの**パッケージ要件**は通常、何らかのファイルに記述されているはずです。 + +パッケージ要件は主に**インストール**するために使用するツールに依存するでしょう。 + +最も一般的な方法は、`requirements.txt` ファイルにパッケージ名とそのバージョンを 1 行ずつ書くことです。 + +もちろん、[FastAPI バージョンについて](./versions.md){.internal-link target=_blank}で読んだのと同じアイデアを使用して、バージョンの範囲を設定します。 + +例えば、`requirements.txt` は次のようになります: + +``` +fastapi>=0.68.0,<0.69.0 +pydantic>=1.8.0,<2.0.0 +uvicorn>=0.15.0,<0.16.0 ``` -## **FastAPI** コードの作成 +そして通常、例えば `pip` を使ってこれらのパッケージの依存関係をインストールします: + +
-* `app` ディレクトリを作成し、移動。 -* 以下の`main.py` ファイルを作成: +```console +$ pip install -r requirements.txt +---> 100% +Successfully installed fastapi pydantic uvicorn +``` + +
+ +!!! info + パッケージの依存関係を定義しインストールするためのフォーマットやツールは他にもあります。 + + Poetryを使った例は、後述するセクションでご紹介します。👇 + +### **FastAPI**コードを作成する + +* `app` ディレクトリを作成し、その中に入ります +* 空のファイル `__init__.py` を作成します +* `main.py` ファイルを作成します: ```Python -from typing import Optional +from typing import Union from fastapi import FastAPI @@ -78,23 +164,136 @@ def read_root(): @app.get("/items/{item_id}") -def read_item(item_id: int, q: Optional[str] = None): +def read_item(item_id: int, q: Union[str, None] = None): return {"item_id": item_id, "q": q} ``` -* ここでは、以下の様なディレクトリ構造になっているはずです: +### Dockerfile + +同じプロジェクト・ディレクトリに`Dockerfile`というファイルを作成します: + +```{ .dockerfile .annotate } +# (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 ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] +``` + +1. 公式のPythonベースイメージから始めます + +2. 現在の作業ディレクトリを `/code` に設定します + + ここに `requirements.txt` ファイルと `app` ディレクトリを置きます。 + +3. 要件が書かれたファイルを `/code` ディレクトリにコピーします + + 残りのコードではなく、最初に必要なファイルだけをコピーしてください。 + + このファイルは**頻繁には変更されない**ので、Dockerはこのステップではそれを検知し**キャッシュ**を使用し、次のステップでもキャッシュを有効にします。 + +4. 要件ファイルにあるパッケージの依存関係をインストールします + `--no-cache-dir` オプションはダウンロードしたパッケージをローカルに保存しないように `pip` に指示します。これは、同じパッケージをインストールするために `pip` を再度実行する場合にのみ有効ですが、コンテナで作業する場合はそうではないです。 + + !!! note + `--no-cache-dir`は`pip`に関連しているだけで、Dockerやコンテナとは何の関係もないです。 + + `--upgrade` オプションは、パッケージが既にインストールされている場合、`pip` にアップグレードするように指示します。 + + 何故ならファイルをコピーする前のステップは**Dockerキャッシュ**によって検出される可能性があるためであり、このステップも利用可能な場合は**Dockerキャッシュ**を使用します。 + + このステップでキャッシュを使用すると、開発中にイメージを何度もビルドする際に、**毎回**すべての依存関係を**ダウンロードしてインストールする**代わりに多くの**時間**を**節約**できます。 + +5. ./app` ディレクトリを `/code` ディレクトリの中にコピーする。 + + これには**最も頻繁に変更される**すべてのコードが含まれているため、Dockerの**キャッシュ**は**これ以降のステップ**に簡単に使用されることはありません。 + + そのため、コンテナイメージのビルド時間を最適化するために、`Dockerfile`の **最後** にこれを置くことが重要です。 + +6. `uvicorn`サーバーを実行するための**コマンド**を設定します + + `CMD` は文字列のリストを取り、それぞれの文字列はスペースで区切られたコマンドラインに入力するものです。 + + このコマンドは **現在の作業ディレクトリ**から実行され、上記の `WORKDIR /code` にて設定した `/code` ディレクトリと同じです。 + + そのためプログラムは `/code` で開始しその中にあなたのコードがある `./app` ディレクトリがあるので、**Uvicorn** は `app.main` から `app` を参照し、**インポート** することができます。 + +!!! tip + コード内の"+"の吹き出しをクリックして、各行が何をするのかをレビューしてください。👆 + +これで、次のようなディレクトリ構造になるはずです: ``` . ├── app +│   ├── __init__.py │ └── main.py -└── Dockerfile +├── Dockerfile +└── requirements.txt +``` + +#### TLS Termination Proxyの裏側 + +Nginx や Traefik のような TLS Termination Proxy (ロードバランサ) の後ろでコンテナを動かしている場合は、`--proxy-headers`オプションを追加します。 + +このオプションは、Uvicornにプロキシ経由でHTTPSで動作しているアプリケーションに対して、送信されるヘッダを信頼するよう指示します。 + +```Dockerfile +CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"] +``` + +#### Dockerキャッシュ + +この`Dockerfile`には重要なトリックがあり、まず**依存関係だけのファイル**をコピーします。その理由を説明します。 + +```Dockerfile +COPY ./requirements.txt /code/requirements.txt +``` + +Dockerや他のツールは、これらのコンテナイメージを**段階的に**ビルドし、**1つのレイヤーを他のレイヤーの上に**追加します。`Dockerfile`の先頭から開始し、`Dockerfile`の各命令によって作成されたファイルを追加していきます。 + +Dockerや同様のツールは、イメージをビルドする際に**内部キャッシュ**も使用します。前回コンテナイメージを構築したときからファイルが変更されていない場合、ファイルを再度コピーしてゼロから新しいレイヤーを作成する代わりに、**前回作成した同じレイヤーを再利用**します。 + +ただファイルのコピーを避けるだけではあまり改善されませんが、そのステップでキャッシュを利用したため、**次のステップ**でキャッシュを使うことができます。 + +例えば、依存関係をインストールする命令のためにキャッシュを使うことができます: + +```Dockerfile +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt ``` -## Dockerイメージをビルド +パッケージ要件のファイルは**頻繁に変更されることはありません**。そのため、そのファイルだけをコピーすることで、Dockerはそのステップでは**キャッシュ**を使用することができます。 + +そして、Dockerは**次のステップのためにキャッシュ**を使用し、それらの依存関係をダウンロードしてインストールすることができます。そして、ここで**多くの時間を節約**します。✨ ...そして退屈な待ち時間を避けることができます。😪😆 + +パッケージの依存関係をダウンロードしてインストールするには**数分**かかりますが、**キャッシュ**を使えば**せいぜい数秒**です。 -* プロジェクトディレクトリ (`app` ディレクトリを含んだ、`Dockerfile` のある場所) へ移動 -* FastAPIイメージのビルド: +加えて、開発中にコンテナ・イメージを何度もビルドして、コードの変更が機能しているかどうかをチェックすることになるため、多くの時間を節約することができます。 + +そして`Dockerfile`の最終行の近くですべてのコードをコピーします。この理由は、**最も頻繁に**変更されるものなので、このステップの後にあるものはほとんどキャッシュを使用することができないのためです。 + +```Dockerfile +COPY ./app /code/app +``` + +### Dockerイメージをビルドする + +すべてのファイルが揃ったので、コンテナ・イメージをビルドしましょう。 + +* プロジェクトディレクトリに移動します(`Dockerfile`がある場所で、`app`ディレクトリがあります) +* FastAPI イメージをビルドします:
@@ -106,9 +305,14 @@ $ docker build -t myimage .
-## Dockerコンテナを起動 +!!! tip + 末尾の `.` に注目してほしいです。これは `./` と同じ意味です。 これはDockerにコンテナイメージのビルドに使用するディレクトリを指示します。 + + この場合、同じカレント・ディレクトリ(`.`)です。 -* 用意したイメージを基にしたコンテナの起動: +### Dockerコンテナの起動する + +* イメージに基づいてコンテナを実行します:
@@ -118,62 +322,394 @@ $ docker run -d --name mycontainer -p 80:80 myimage
-これで、Dockerコンテナ内に最適化されたFastAPIサーバが動作しています。使用しているサーバ (そしてCPUコア数) に沿った自動チューニングが行われています。 - -## 確認 +## 確認する -DockerコンテナのURLで確認できるはずです。例えば: http://192.168.99.100/items/5?q=somequeryhttp://127.0.0.1/items/5?q=somequery (もしくはDockerホストを使用したこれらと同等のもの)。 +Dockerコンテナのhttp://192.168.99.100/items/5?q=somequeryhttp://127.0.0.1/items/5?q=somequery (またはそれに相当するDockerホストを使用したもの)といったURLで確認できるはずです。 -以下の様なものが返されます: +アクセスすると以下のようなものが表示されます: ```JSON {"item_id": 5, "q": "somequery"} ``` -## 対話的APIドキュメント +## インタラクティブなAPIドキュメント -ここで、http://192.168.99.100/docshttp://127.0.0.1/docs (もしくはDockerホストを使用したこれらと同等のもの) を開いて下さい。 +これらのURLにもアクセスできます: http://192.168.99.100/docshttp://127.0.0.1/docs (またはそれに相当するDockerホストを使用したもの) -自動生成された対話的APIドキュメントが確認できます (Swagger UIによって提供されます): +アクセスすると、自動対話型APIドキュメント(Swagger UIが提供)が表示されます: ![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png) -## その他のAPIドキュメント +## 代替のAPIドキュメント -また同様に、http://192.168.99.100/redochttp://127.0.0.1/redoc (もしくはDockerホストを使用したこれらと同等のもの) を開いて下さい。 +また、http://192.168.99.100/redochttp://127.0.0.1/redoc (またはそれに相当するDockerホストを使用したもの)にもアクセスできます。 -他の自動生成された対話的なAPIドキュメントが確認できます (ReDocによって提供されます): +代替の自動ドキュメント(ReDocによって提供される)が表示されます: ![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png) -## Traefik +## 単一ファイルのFastAPIでDockerイメージをビルドする + +FastAPI が単一のファイル、例えば `./app` ディレクトリのない `main.py` の場合、ファイル構造は次のようになります: +``` +. +├── Dockerfile +├── main.py +└── requirements.txt +``` + +そうすれば、`Dockerfile`の中にファイルをコピーするために、対応するパスを変更するだけでよいです: + +```{ .dockerfile .annotate hl_lines="10 13" } +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 ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"] +``` + +1. main.py`ファイルを `/code` ディレクトリに直接コピーします。 + +2. Uvicornを実行し、`main`から`app`オブジェクトをインポートするように指示します(`app.main`からインポートするのではなく)。 + +次にUvicornコマンドを調整して、`app.main` の代わりに新しいモジュール `main` を使用し、FastAPIオブジェクトである `app` をインポートします。 + +## デプロイメントのコンセプト + +コンテナという観点から、[デプロイのコンセプト](./concepts.md){.internal-link target=_blank}に共通するいくつかについて、もう一度説明しましょう。 + +コンテナは主に、アプリケーションの**ビルドとデプロイ**のプロセスを簡素化するためのツールですが、これらの**デプロイのコンセプト**を扱うための特定のアプローチを強制するものではないです。 -Traefikは、高性能なリバースプロキシ/ロードバランサーです。「TLSターミネーションプロキシ」ジョブを実行できます(他の機能と切り離して)。 +**良いニュース**は、それぞれの異なる戦略には、すべてのデプロイメントのコンセプトをカバーする方法があるということです。🎉 -Let's Encryptと統合されています。そのため、証明書の取得と更新を含むHTTPSに関するすべての処理を実行できます。 +これらの**デプロイメントのコンセプト**をコンテナの観点から見直してみましょう: -また、Dockerとも統合されています。したがって、各アプリケーション構成でドメインを宣言し、それらの構成を読み取って、HTTPS証明書を生成し、構成に変更を加えることなく、アプリケーションにHTTPSを自動的に提供できます。 +* セキュリティ - HTTPS +* 起動時の実行 +* 再起動 +* **レプリケーション(実行中のプロセス数)** +* メモリ +* 開始前の事前ステップ + +## HTTPS + +FastAPI アプリケーションの **コンテナ・イメージ**(および後で実行中の **コンテナ**)だけに焦点を当てると、通常、HTTPSは別のツールを用いて**外部で**処理されます。 + +例えばTraefikのように、**HTTPS**と**証明書**の**自動**取得を扱う別のコンテナである可能性もあります。 + +!!! tip + TraefikはDockerやKubernetesなどと統合されているので、コンテナ用のHTTPSの設定や構成はとても簡単です。 + +あるいは、(コンテナ内でアプリケーションを実行しながら)クラウド・プロバイダーがサービスの1つとしてHTTPSを処理することもできます。 + +## 起動時および再起動時の実行 + +通常、コンテナの**起動と実行**を担当する別のツールがあります。 + +それは直接**Docker**であったり、**Docker Compose**であったり、**Kubernetes**であったり、**クラウドサービス**であったりします。 + +ほとんどの場合(またはすべての場合)、起動時にコンテナを実行し、失敗時に再起動を有効にする簡単なオプションがあります。例えばDockerでは、コマンドラインオプションの`--restart`が該当します。 + +コンテナを使わなければ、アプリケーションを起動時や再起動時に実行させるのは面倒で難しいかもしれません。しかし、**コンテナ**で作業する場合、ほとんどのケースでその機能はデフォルトで含まれています。✨ + +## レプリケーション - プロセス数 + +**Kubernetes** や Docker Swarm モード、Nomad、あるいは複数のマシン上で分散コンテナを管理するための同様の複雑なシステムを使ってマシンのクラスターを構成している場合、 各コンテナで(Workerを持つGunicornのような)**プロセスマネージャ**を使用する代わりに、**クラスター・レベル**で**レプリケーション**を処理したいと思うでしょう。 + +Kubernetesのような分散コンテナ管理システムの1つは通常、入ってくるリクエストの**ロードバランシング**をサポートしながら、**コンテナのレプリケーション**を処理する統合された方法を持っています。このことはすべて**クラスタレベル**にてです。 + +そのような場合、UvicornワーカーでGunicornのようなものを実行するのではなく、[上記の説明](#dockerfile)のように**Dockerイメージをゼロから**ビルドし、依存関係をインストールして、**単一の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)プロセス**を持ちたいでしょう。 + +この場合、Uvicornワーカーを持つGunicornのようなプロセスマネージャーや、Uvicornワーカーを使うUvicornは**避けたい**でしょう。**コンテナごとにUvicornのプロセスは1つだけ**にしたいでしょう(おそらく複数のコンテナが必要でしょう)。 + +(GunicornやUvicornがUvicornワーカーを管理するように)コンテナ内に別のプロセスマネージャーを持つことは、クラスターシステムですでに対処しているであろう**不要な複雑さ**を追加するだけです。 + +### Containers with Multiple Processes and Special Cases + +もちろん、**特殊なケース**として、**Gunicornプロセスマネージャ**を持つ**コンテナ**内で複数の**Uvicornワーカープロセス**を起動させたい場合があります。 + +このような場合、**公式のDockerイメージ**を使用することができます。このイメージには、複数の**Uvicornワーカープロセス**を実行するプロセスマネージャとして**Gunicorn**が含まれており、現在のCPUコアに基づいてワーカーの数を自動的に調整するためのデフォルト設定がいくつか含まれています。詳しくは後述の[Gunicornによる公式Dockerイメージ - Uvicorn](#gunicornによる公式dockerイメージ---Uvicorn)で説明します。 + +以下は、それが理にかなっている場合の例です: + +#### シンプルなアプリケーション + +アプリケーションを**シンプル**な形で実行する場合、プロセス数の細かい調整が必要ない場合、自動化されたデフォルトを使用するだけで、コンテナ内にプロセスマネージャが必要かもしれません。例えば、公式Dockerイメージでシンプルな設定が可能です。 + +#### Docker Compose + +Docker Composeで**シングルサーバ**(クラスタではない)にデプロイすることもできますので、共有ネットワークと**ロードバランシング**を維持しながら(Docker Composeで)コンテナのレプリケーションを管理する簡単な方法はないでしょう。 + +その場合、**単一のコンテナ**で、**プロセスマネージャ**が内部で**複数のワーカープロセス**を起動するようにします。 + +#### Prometheusとその他の理由 + +また、**1つのコンテナ**に**1つのプロセス**を持たせるのではなく、**1つのコンテナ**に**複数のプロセス**を持たせる方が簡単だという**他の理由**もあるでしょう。 + +例えば、(セットアップにもよりますが)Prometheusエクスポーターのようなツールを同じコンテナ内に持つことができます。 + +この場合、**複数のコンテナ**があると、デフォルトでは、Prometheusが**メトリクスを**読みに来たとき、すべてのレプリケートされたコンテナの**蓄積されたメトリクス**を取得するのではなく、毎回**単一のコンテナ**(その特定のリクエストを処理したコンテナ)のものを取得することになります。 + +その場合、**複数のプロセス**を持つ**1つのコンテナ**を用意し、同じコンテナ上のローカルツール(例えばPrometheusエクスポーター)がすべての内部プロセスのPrometheusメトリクスを収集し、その1つのコンテナ上でそれらのメトリクスを公開する方がシンプルかもしれません。 --- -次のセクションに進み、この情報とツールを使用して、すべてを組み合わせます。 +重要なのは、盲目的に従わなければならない普遍のルールはないということです。 + +これらのアイデアは、**あなた自身のユースケース**を評価し、あなたのシステムに最適なアプローチを決定するために使用することができます: + +* セキュリティ - HTTPS +* 起動時の実行 +* 再起動 +* **レプリケーション(実行中のプロセス数)** +* メモリ +* 開始前の事前ステップ + +## メモリー + +コンテナごとに**単一のプロセスを実行する**と、それらのコンテナ(レプリケートされている場合は1つ以上)によって消費される多かれ少なかれ明確に定義された、安定し制限された量のメモリを持つことになります。 + +そして、コンテナ管理システム(**Kubernetes**など)の設定で、同じメモリ制限と要件を設定することができます。 + +そうすれば、コンテナが必要とするメモリ量とクラスタ内のマシンで利用可能なメモリ量を考慮して、**利用可能なマシン**に**コンテナ**をレプリケートできるようになります。 + +アプリケーションが**シンプル**なものであれば、これはおそらく**問題にはならない**でしょうし、ハードなメモリ制限を指定する必要はないかもしれないです。 + +しかし、**多くのメモリを使用**している場合(たとえば**機械学習**モデルなど)、どれだけのメモリを消費しているかを確認し、**各マシンで実行するコンテナの数**を調整する必要があります(そしておそらくクラスタにマシンを追加します)。 + +**コンテナごとに複数のプロセス**を実行する場合(たとえば公式のDockerイメージで)、起動するプロセスの数が**利用可能なメモリ以上に消費しない**ようにする必要があります。 + +## 開始前の事前ステップとコンテナ + +コンテナ(DockerやKubernetesなど)を使っている場合、主に2つのアプローチがあります。 + +### 複数のコンテナ + +複数の**コンテナ**があり、おそらくそれぞれが**単一のプロセス**を実行している場合(**Kubernetes**クラスタなど)、レプリケートされたワーカーコンテナを実行する**前に**、単一のコンテナで**事前のステップ**の作業を行う**別のコンテナ**を持ちたいと思うでしょう。 + +!!! info + もしKubernetesを使用している場合, これはおそらくInit コンテナでしょう。 + +ユースケースが事前のステップを**並列で複数回**実行するのに問題がない場合(例:データベースの準備チェック)、メインプロセスを開始する前に、それらのステップを各コンテナに入れることが可能です。 + +### 単一コンテナ + +単純なセットアップで、**単一のコンテナ**で複数の**ワーカー・プロセス**(または1つのプロセスのみ)を起動する場合、アプリでプロセスを開始する直前に、同じコンテナで事前のステップを実行できます。公式Dockerイメージは、内部的にこれをサポートしています。 + +## Gunicornによる公式Dockerイメージ - Uvicorn + +前の章で詳しく説明したように、Uvicornワーカーで動作するGunicornを含む公式のDockerイメージがあります: [Server Workers - Gunicorn と Uvicorn](./server-workers.md){.internal-link target=_blank}で詳しく説明しています。 + +このイメージは、主に上記で説明した状況で役に立つでしょう: [複数のプロセスと特殊なケースを持つコンテナ(Containers with Multiple Processes and Special Cases)](#containers-with-multiple-processes-and-special-cases) + +* tiangolo/uvicorn-gunicorn-fastapi. + +!!! warning + このベースイメージや類似のイメージは**必要ない**可能性が高いので、[上記の: FastAPI用のDockerイメージをビルドする(Build a Docker Image for FastAPI)](#build-a-docker-image-for-fastapi)のようにゼロからイメージをビルドする方が良いでしょう。 + +このイメージには、利用可能なCPUコアに基づいて**ワーカー・プロセスの数**を設定する**オートチューニング**メカニズムが含まれています。 + +これは**賢明なデフォルト**を備えていますが、**環境変数**や設定ファイルを使ってすべての設定を変更したり更新したりすることができます。 -## TraefikとHTTPSを使用したDocker Swarmモードのクラスタ +また、スクリプトで**開始前の事前ステップ**を実行することもサポートしている。 -HTTPSを処理する(証明書の取得と更新を含む)Traefikを使用して、Docker Swarmモードのクラスタを数分(20分程度)でセットアップできます。 +!!! tip + すべての設定とオプションを見るには、Dockerイメージのページをご覧ください: tiangolo/uvicorn-gunicorn-fastapi -Docker Swarmモードを使用することで、1台のマシンの「クラスタ」から開始でき(1か月あたり5ドルのサーバーでもできます)、後から必要なだけサーバーを拡張できます。 +### 公式Dockerイメージのプロセス数 -TraefikおよびHTTPS処理を備えたDocker Swarm Modeクラスターをセットアップするには、次のガイドに従います: +このイメージの**プロセス数**は、利用可能なCPU**コア**から**自動的に計算**されます。 + +つまり、CPUから可能な限り**パフォーマンス**を**引き出そう**とします。 + +また、**環境変数**などを使った設定で調整することもできます。 + +しかし、プロセスの数はコンテナが実行しているCPUに依存するため、**消費されるメモリの量**もそれに依存することになります。 + +そのため、(機械学習モデルなどで)大量のメモリを消費するアプリケーションで、サーバーのCPUコアが多いが**メモリが少ない**場合、コンテナは利用可能なメモリよりも多くのメモリを使おうとすることになります。 + +その結果、パフォーマンスが大幅に低下する(あるいはクラッシュする)可能性があります。🚨 + +### Dockerfileを作成する + +この画像に基づいて`Dockerfile`を作成する方法を以下に示します: + +```Dockerfile +FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9 + +COPY ./requirements.txt /app/requirements.txt + +RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt + +COPY ./app /app +``` + +### より大きなアプリケーション + +[複数のファイルを持つ大きなアプリケーション](../tutorial/bigger-applications.md){.internal-link target=_blank}を作成するセクションに従った場合、`Dockerfile`は次のようになります: + +```Dockerfile hl_lines="7" +FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9 + +COPY ./requirements.txt /app/requirements.txt + +RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt + +COPY ./app /app/app +``` + +### いつ使うのか + +おそらく、**Kubernetes**(または他のもの)を使用していて、すでにクラスタレベルで複数の**コンテナ**で**レプリケーション**を設定している場合は、この公式ベースイメージ(または他の類似のもの)は**使用すべきではありません**。 + +そのような場合は、上記のように**ゼロから**イメージを構築する方がよいでしょう: [FastAPI用のDockerイメージをビルドする(Build a Docker Image for FastAPI)](#build-a-docker-image-for-fastapi) を参照してください。 + +このイメージは、主に上記の[複数のプロセスと特殊なケースを持つコンテナ(Containers with Multiple Processes and Special Cases)](#containers-with-multiple-processes-and-special-cases)で説明したような特殊なケースで役に立ちます。 + +例えば、アプリケーションが**シンプル**で、CPUに応じたデフォルトのプロセス数を設定すればうまくいく場合や、クラスタレベルでレプリケーションを手動で設定する手間を省きたい場合、アプリで複数のコンテナを実行しない場合などです。 + +または、**Docker Compose**でデプロイし、単一のサーバで実行している場合などです。 + +## コンテナ・イメージのデプロイ + +コンテナ(Docker)イメージを手に入れた後、それをデプロイするにはいくつかの方法があります。 + +例えば以下のリストの方法です: + +* 単一サーバーの**Docker Compose** +* **Kubernetes**クラスタ +* Docker Swarmモードのクラスター +* Nomadのような別のツール +* コンテナ・イメージをデプロイするクラウド・サービス + +## Poetryを利用したDockerイメージ + +もしプロジェクトの依存関係を管理するためにPoetryを利用する場合、マルチステージビルドを使うと良いでしょう。 + +```{ .dockerfile .annotate } +# (1) +FROM python:3.9 as requirements-stage + +# (2) +WORKDIR /tmp + +# (3) +RUN pip install poetry + +# (4) +COPY ./pyproject.toml ./poetry.lock* /tmp/ + +# (5) +RUN poetry export -f requirements.txt --output requirements.txt --without-hashes + +# (6) +FROM python:3.9 + +# (7) +WORKDIR /code + +# (8) +COPY --from=requirements-stage /tmp/requirements.txt /code/requirements.txt + +# (9) +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt + +# (10) +COPY ./app /code/app + +# (11) +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] +``` + +1. これは最初のステージで、`requirements-stage`と名付けられます +2. `/tmp` を現在の作業ディレクトリに設定します + ここで `requirements.txt` というファイルを生成します。 + +3. このDockerステージにPoetryをインストールします + +4. pyproject.toml`と`poetry.lock`ファイルを`/tmp` ディレクトリにコピーします + + `./poetry.lock*`(末尾に`*`)を使用するため、そのファイルがまだ利用できない場合でもクラッシュすることはないです。 +5. requirements.txt`ファイルを生成します + +6. これは最後のステージであり、ここにあるものはすべて最終的なコンテナ・イメージに保存されます +7. 現在の作業ディレクトリを `/code` に設定します +8. `requirements.txt`ファイルを `/code` ディレクトリにコピーします + このファイルは前のDockerステージにしか存在しないため、`--from-requirements-stage`を使ってコピーします。 +9. 生成された `requirements.txt` ファイルにあるパッケージの依存関係をインストールします +10. app` ディレクトリを `/code` ディレクトリにコピーします +11. uvicorn` コマンドを実行して、`app.main` からインポートした `app` オブジェクトを使用するように指示します +!!! tip + "+"の吹き出しをクリックすると、それぞれの行が何をするのかを見ることができます + +**Dockerステージ**は`Dockerfile`の一部で、**一時的なコンテナイメージ**として動作します。 + +最初のステージは **Poetryのインストール**と Poetry の `pyproject.toml` ファイルからプロジェクトの依存関係を含む**`requirements.txt`を生成**するためだけに使用されます。 + +この `requirements.txt` ファイルは後半の **次のステージ**で `pip` と共に使用されます。 + +最終的なコンテナイメージでは、**最終ステージ**のみが保存されます。前のステージは破棄されます。 + +Poetryを使用する場合、**Dockerマルチステージビルド**を使用することは理にかなっています。 + +なぜなら、最終的なコンテナイメージにPoetryとその依存関係がインストールされている必要はなく、**必要なのは**プロジェクトの依存関係をインストールするために生成された `requirements.txt` ファイルだけだからです。 + +そして次の(そして最終的な)ステージでは、前述とほぼ同じ方法でイメージをビルドします。 + +### TLS Termination Proxyの裏側 - Poetry + +繰り返しになりますが、NginxやTraefikのようなTLS Termination Proxy(ロードバランサー)の後ろでコンテナを動かしている場合は、`--proxy-headers`オプションをコマンドに追加します: + +```Dockerfile +CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"] +``` -### Docker Swarm Mode and Traefik for an HTTPS cluster +## まとめ -### FastAPIアプリケーションのデプロイ +コンテナ・システム(例えば**Docker**や**Kubernetes**など)を使えば、すべての**デプロイメントのコンセプト**を扱うのがかなり簡単になります: -すべてを設定するための最も簡単な方法は、[**FastAPI** Project Generators](../project-generation.md){.internal-link target=_blank}を使用することでしょう。 +* セキュリティ - HTTPS +* 起動時の実行 +* 再起動 +* **レプリケーション(実行中のプロセス数)** +* メモリ +* 開始前の事前ステップ -上述したTraefikとHTTPSを備えたDocker Swarm クラスタが統合されるように設計されています。 +ほとんどの場合、ベースとなるイメージは使用せず、公式のPython Dockerイメージをベースにした**コンテナイメージをゼロからビルド**します。 -2分程度でプロジェクトが生成されます。 +`Dockerfile`と**Dockerキャッシュ**内の命令の**順番**に注意することで、**ビルド時間を最小化**することができ、生産性を最大化することができます(そして退屈を避けることができます)。😎 -生成されたプロジェクトはデプロイの指示がありますが、それを実行するとさらに2分かかります。 +特別なケースでは、FastAPI用の公式Dockerイメージを使いたいかもしれません。🤓