uvicornとnginxでwebアプリケーションの環境構築をしました。

# はじめに
はじめまして。
komegoroです。

自然言語処理分野の機械学習エンジニアをフリーランスとしてやっています。
普段は学習モデルの構築や検証をやっており、なかなかインフラやUI周りをやる機会がないので、
簡単なWEBアプリケーションを作ってAWSでリリースしようと思いました。

その第一歩として、WEBアプリケーションが見れるようにまでの環境を構築したので、
知識整理も兼ねて投稿します。
初めての環境構築のため、推奨されない or 非効率な やり方があるかと思いますが、
ご指摘いただけると嬉しいです。

# 紹介する範囲
この記事で紹介する範囲は、AWSのEC2インスタンス生成後から、WEBアプリケーションが表示されるまでです。
AWSの登録方法やEC2インスタンスの生成方法は紹介していません。
また、WEBアプリケーションの作成については触れません。

# 環境
主要なもののみ記載します。
starletteなどはfastapiインストール時に一緒にインストールするため省略します。

インスタンス
・AWSのEC2 Amazon Linux 2

linux上でインストールするもの
・Python (僕の環境では3.7.9)
・nginx

Python上でインストールするもの
・fastapi (僕の環境では0.63.0)
・uvicorn (僕の環境では0.13.4)

# 作業手順
アプリケーションサーバー側の設定

1. Python3のインストール
1. Pythonのライブラリのインストール
1. WEBアプリケーションファイルの配備
1. systemdによるWEBアプリケーションの起動

WEBサーバー側の設定

1. nginxのインストール
1. nginxの設定
 

# 実施
※EC2はAmazon Linux 2です。
※ec2-userでログインしています。

## WEBアプリケーションサーバー側の設定

### 1.Python3のインストール
`sudo yum install python3`
でPython3をインストールします。

### 2.Pythonのライブラリのインストール
`sudo pip3 install fastapi uvicorn`
でインストールします。

### 3.WEBアプリケーションファイルの配備
今回用いるファイルは、

```python:example1.py
import uvicorn
import example2
import example3


if __name__ == "__main__":
    uvicorn.run(app=example2.app)
```

```python:example2.py
from fastapi import FastAPI
from starlette.requests import Request


app = FastAPI(
    title="タイトル",
    description="ここに詳細を書きます",
    version="0.1"
)


def index(request: Request):
    return {"hello": "world"}
```

```python:example3.py
import example2

example2.app.add_api_route("/", example2.index, methods=["GET"])
```

以上の3ファイルです。

この3ファイルを`/home/ec2-user/test_app`に配備します。

```
python3 /home/ec2-user/test_app/example1.py
```
このコマンドを実行することで、
`127.0.0.1:8000`に`{"hello":"world"}`と記載されたページが表示されるようになります。
ただ、このコマンドではコマンドラインを閉じたら、コマンドも終了してしまうので、
裏で動き続けるようにsystemdを利用します。

### 4.systemdによるWEBアプリケーションの起動
`/etc/systemd/system/`に`test_app.service`を作成します。
中身は以下です。
※変数の意味については他記事を参照ください。

```test_app.service
[Unit]
Description=release test_app
After=network.target

[Service]
User=ec2-user
Group=nginx
WorkingDirectory=/home/ec2-user/test_app
ExecStart=/usr/bin/python3 example.py

[Install]
WantedBy=multi-user.target
```

作成後、
`sudo systemctl start test_app`
`sudo systemctl status test_app`
を実行すると、

```
[ec2-user@ip-172-31-1-197 test_app]$ sudo systemctl status test_app
● test_app.service - release test_app
   Loaded: loaded (/etc/systemd/system/test_app.service; static; vendor preset: disabled)
   Active: active (running) since Tue 2021-04-20 10:05:32 UTC; 5s ago
 Main PID: 2858 (python3)
   CGroup: /system.slice/test_app.service
           └─2858 /usr/bin/python3 example1.py

Apr 20 10:05:32 ip-172-31-1-197.ap-northeast-1.compute.internal systemd[1]: Started release test_app.
Apr 20 10:05:32 ip-172-31-1-197.ap-northeast-1.compute.internal systemd[1]: Starting release test_app...
Apr 20 10:05:32 ip-172-31-1-197.ap-northeast-1.compute.internal python3[2858]: INFO:     Started server process [2858]
Apr 20 10:05:32 ip-172-31-1-197.ap-northeast-1.compute.internal python3[2858]: INFO:     Waiting for application startup.
Apr 20 10:05:32 ip-172-31-1-197.ap-northeast-1.compute.internal python3[2858]: INFO:     Application startup complete.
Apr 20 10:05:32 ip-172-31-1-197.ap-northeast-1.compute.internal python3[2858]: INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
となり、裏で上手く起動できました。
これでWEBアプリケーション側の設定が終わったので、WEBサーバー側の設定をしていきます。

## WEBサーバー側の設定

### 1.nginxのインストール

nginxのインストールコマンドは
`sudo amazon-linux-extras install nginx1`
です。

### 2.nginxの設定
`/etc/nginx/conf.d/`に`test_app.conf`を作成します。
中身は以下です。

```
server {
  listen 80;               # ポート番号80番にアクセスされたら
  server_name <"パブリック IPv4 アドレス" or "パブリック IPv4 DNS">;
#  http://<"パブリック IPv4 アドレス"> or http://<"パブリック IPv4 DNS"> にアクセスされたら

  location / {
    proxy_pass http://127.0.0.1:8000; # test_appの起動場所に移動
    proxy_redirect                          off;
    proxy_set_header Host                   $host;
    proxy_set_header X-Real-IP              $remote_addr;
    proxy_set_header X-Forwarded-Host       $host;
    proxy_set_header X-Forwarded-Server     $host;
    proxy_set_header X-Forwarded-Proto      $scheme;
    proxy_set_header X-Forwarded-For        $proxy_add_x_forwarded_for;
  }
}
```

作成後、`sudo service nginx start`を実行すると、
`http://<"パブリック IPv4 アドレス">` or `http://<"パブリック IPv4 DNS">`
にアクセスすることで`{"hello":"world"}`と記載されたページが見られるようになります。

# おわりに
今回はAWSのEC2インスタンス生成後から、WEBアプリケーションが表示されるまで、
WEBサーバーとアプリケーションサーバーの構築を行いました。
私自身、なぜこの作業が必要なのか、をまだ理解できておらず、ただの作業手順書になってしまいましたが、
今後追記か別記事で投稿していきたいと思います。
読んでいただきありがとうございました。