VPSをAPIサーバにして、Flutterアプリを作った話

# 概要
以前からVPSを契約していて活用していなかったので、今回は、VPSをAPIサーバとして、Flutterアプリを作りました。(アプリの内容は、chatgptで作成したナンバーズ3の当選番号予測プログラムであり、正確性が保証されていないためメインでは触れません。)
システム全体の構成から、触れたいところのみ詳細に書こうと思います。

### システム全体の構成
- クライアント：Flutter
- APIサーバ：ubuntu
- DB：Neon(Postgresql)
- API：Python3(FastAPI)
一応全体の構成図も載せておきます。
![システム構成図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/657014/37afcb83-0ecf-4e53-c178-a9fdc5a211eb.png)

### DBについて
APIサーバについては契約していましたが、DBをどうしようと無料のものを探していたところ、[Neon](https://neon.tech)を見つけました。最初はHerokuにしようと思っていたのですが、2022年11月28日に無料枠が廃止されたようです。

### Neonとは
サーバーレスなデータベース(Postgresql)を提供しているサービスです。
無料のプランは以下のようになってます。（公式サイトより抜粋）今回は、このサービスを使用して、APIサーバからアクセスしています。
<img width="200" alt="Neonの無料プラン" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/657014/e440ea9f-bdc7-1b8f-99af-fb82ddbc7150.png">
このプランでいうところの0.25CUというのは、0.25 vCPU,1GB RAMだそうです。
なので、2CU = 2 vCPU,8GB RAMまでAutoScaleします。
ストレージが500MBで少し物足りないですが、今回作るアプリでは十分な容量です。

Neonのセットアップについては、ここでは割愛します。

### APIサーバについて
以前からVPSサーバをWebARENA Indigoを契約していて使ってなかったので、今回APIサーバとして使用することにしました。VPSを契約した時に書いた記事が[こちら](https://kokiblog.net/minipc_3)(個人ブログの記事です)。
そして、同時期にドメインを取得したので、このVPSに割り当てています。

### APIサーバでやること
今回APIサーバの役割は、以下の二つです。
1. ナンバーズ3の過去の当選番号をスクレイピングして、予測した番号をNeonにinsertする(日次)
1. FlutterアプリからのAPI呼び出しにより、Neonから予測した番号をselectしアプリ画面に表示する

どちらもPython3のプログラムですが、1.はcronによる日次バッチ、2.はFastAPIを用いたREST APIとなっています。ここで、FastAPIの環境構築とデーモンプロセス化までの手順を書いておきます。

### FastAPI環境構築、およびデーモンプロセス化手順
まずは、以下のコマンドにより、fastapiとuvicornをインストールします。
```
pip install fastapi[all]
pip install uvicorn
```

次に、VPSサーバが万一再起動された場合を考慮して、デーモンプロセス化して自動起動するようにします。
(サービス名をfastapiとした場合)
```
cd /etc/systemd/system
touch fastapi.service
vi fastapi.service
```

記述内容は以下です。
```
[Unit]
Description=Your FastAPI Application
After=network.target

[Service]
User=username #実際のユーザー名を記述
Group=www-data
WorkingDirectory=/home/fastapi #fastapiのワーキングディレクトリを記述
ExecStart=/usr/local/bin/uvicorn main:app --host 127.0.0.1 --port 8000　#ポート番号は実際のものを記述

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

続いて、上記で指定したワーキングディレクトリにパーミッションを付与します。
```
chown -R username:www-data /home/fastapi
chmod -R 755 /home/fastapi
```

デーモンプロセスのリロードを行い、作成したサービスを起動し、自動起動を有効にします。
```
systemctl daemon-reload
systemctl start fastapi
systemctl enable fastapi
```

### Nginxを介してFastAPIにアクセスする
FlutterからのAPI呼び出しをNginxで受けつつ、FastAPIにリバースプロキシ設定します。(NginxはFastAPIと同一サーバに環境構築します)
ここの手順は割愛します。

### API呼び出しをHTTPS化する
HTTPSでのアクセスは必須です。
SSL証明書の発行となると費用が発生するため、どうにか無料でHTTPS化できないかと探していたら、[Let's Encrypt](https://letsencrypt.org/ja/)というフリーの認証局(CA)を見つけました。
有効期限が90日であるため、月初に自動更新するように設定をしましたが、有効期限が30日以上ある場合は、自動更新されないようです。

以下の手順で、Let's Encryptをインストールします。
```
# Let's Encryptのインストール
apt-get install letsencrypt

# 一旦nginxは停止
systemctl stop nginx

# SSL証明書取得
letsencrypt certonly --standalone -d [実際のドメイン名を記述]
# 途中でメールアドレスを入力します

# nginxを再開
systemctl start nginx
```

証明書が取得されているかを確認します。
以下コマンドを実行し、Found the following certs:以下にドメイン名に関する情報が出力されれば、取得できています。VALID:に残り何日有効か記載されています。
```
certbot certificates


Found the following certs:
  Certificate Name: ドメイン名
    Serial Number: XXXXXXXXXXXXXXXXX
    Key Type: RSA
    Domains: ドメイン名
    Expiry Date: 2024-MM-DD 00:00:00+00:00 (VALID: XX days)
    Certificate Path: /xxx/fullchain.pem
    Private Key Path: /xxx/privkey.pem

```

### 【参考】実際のアプリ画面
参考までに実際のアプリの画面です。
特に画面での操作はなく、起動するとAPIでデータを取得し表示するのみです。
今はまだスクレイピングのプログラムが日次で動いていないので、8/26更新で止まってます。
あと、App Storeに一応審査を出していますがはたして通るのでしょうか？
<img width=200" alt="アプリのスクリーンショット" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/657014/46f16eba-667d-3fe4-1d8f-19489e1fa451.png">


### まとめ
今までは、個人のブログで記事を書いていたのですが、今回初めてQiitaで記事を書いてみました。
情報の粒度感とかわからないですが、今後も個人開発で得た知見などを書いていく予定です。