Flask × postgresql × Heroku でデプロイまでしたときの過程・備忘録・エラー対処まとめ

# はじめに
Flaskの勉強で簡単なアプリを作ったので、Herokuにデプロイしてみました。手順も忘れてしまいそうなので備忘録ついでに書き記しておきます。

# 前提
- herokuアカウントを持っていること
- herokuコマンドが使えること
- gitコマンドが使えること

# ローカルで開発
まずはPythonでアプリを作りましょう。

今回はPostgreSQLと連携してSQLAlchemyを使ってテーブルのデータを表示するアプリを作りました。

ファイル構成はこんな感じです。
```
─
│ server.py
│
└─flask_app
    │  config.py
    │  __init__.py
    │
    ├─models
    │      data.py
    ├─static
    │
    ├─templates
    │      index.html
    ├─views
           views.py
```
\_\_init__.pyとserver.pyはこんな感じです。
```__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config.from_object('flask_app.config')

db = SQLAlchemy(app)

from flask_app.views import views
```

```server.py
from flask_app import app

if __name__ == '__main__':
    app.run()
```

`python server.py`を実行することでwebアプリケーションサーバが起動し、デフォルトではループバックアドレスの5000番ポートにアクセスすることで見ることができます。ちなみに、デバッグモードをオンにしているとホットリロードに対応するので便利です。

また、今回はSQLAlchemyのORMを利用して、ローカルのPostgreSQLサーバからデータを取得しています。

# デプロイ準備
Herokuを利用することで無料でウェブアプリケーションをデプロイすることができます。なお、無料版だと様々な制限があるので、詳しくは[こちら](https://jp.heroku.com/pricing)を参照してください。普通に自分の製作物を一時的に公開したいだけなら無料版で十分だと思います。また、アドオンを使用することで新たなDBサーバやメールサーバ、モニタリングなどができるようになりますが、今回使用するHeroku Postgresは無料で使用することができます。

### 1．デプロイ用のファイルを追加
まずはプロジェクトをHerokuに上げるためのファイルを用意しましょう。
必要なものは以下の3つのファイルです。なお、プロジェクトのフォルダと並列な場所に配置する必要があります。

:::note info
- Procfile  
⇒ウェブアプリがどのようなコマンドで実行するかを示す指示書のようなもの
- requirements.txt
⇒実行に必要なライブラリとバージョンを指定
- runtime.txt
⇒Pythonのランタイムバージョンを指定
:::

- #### Procfile
Procfileは`web: gunicorn <実行ファイル名 ※拡張子は含まない>:app --log-file=-`というように記述します。gunicornとはHTTPサーバで、Herokuのドキュメントで使用が推奨されています。
今回の私の環境の場合は、
```Procfile
web: gunicorn server:app --log-file=-
```
というようになります。

- #### requirements.txt
`requirement`ではなく`requirements`なので気を付けましょう。中身はライブラリ群とバージョンが記述されます。。
デプロイ後のHTTPサーバはgunicornを使用するので、`pip install gunicorn`をするか、直接requirements.txtに書き込んでください。
コマンドで`pip freeze > requirements.txt`とすればライブラリ情報が書き込まれます。
手動で必要なライブラリを指定したい場合は、`<ライブラリ名>==<バージョン名>`と記述してください。なお、依存ライブラリの関係を調べるならpipdeptreeを使用すると便利です。
ちなみに私の環境では、
```
Flask==2.1.0
Flask-SQLAlchemy==2.5.1
Jinja2==3.1.1
SQLAlchemy==1.4.36
click==8.1.3
colorama==0.4.4
importlib-metadata==2.0.0
zipp==3.2.0
itsdangerous==2.1.2
MarkupSafe==2.1.1
Werkzeug==2.1.2
greenlet==1.1.2
gunicorn==20.0.4
psycopg2==2.9.3
psycopg2-binary==2.9.3
```
というようになりました。

- #### runtime.txt
Pythonのランタイムバージョンを指定します。
私の環境では、このように記述しました。
```runtime.txt
python-3.7.13
```
ここで気を付けるべき点が2点あります。一つは`Python`ではなく`python`であることで、もう一つはバージョンの指定です。
```
remote: -----> Using Python version specified in runtime.txt
remote:  !     Requested runtime (python-3.7.5) is not available for this stack (heroku-20).
remote:  !     Aborting.  More info: https://devcenter.heroku.com/articles/python-support
remote:  !     Push rejected, failed to compile Python app.
remote: 
remote:  !     Push failed
```
もしデプロイ時にこのようなエラーログが出たら、ランタイムバージョンの指定が間違っています。[こちら](https://devcenter.heroku.com/articles/python-support)の"supported runtimes"からサポートされているバージョンを指定してください。
ちなみに現在のサポートは、
```
python-3.10.4 on all supported stacks
python-3.9.13 on all supported stacks
python-3.8.13 on Heroku-18 and Heroku-20 only
python-3.7.13 on Heroku-18 and Heroku-20 only
```
となっています。

### 2．Heroku上にアプリを作成
`heroku create <アプリ名>`でheroku上にアプリを作成することができます。
なお、createした時点でリモートリポジトリにherokuのリポジトリが登録されるので、ここへプッシュするとデプロイができるようになります。
```
heroku create flask-app
```

### 3．HerokuにPostgreSQLのアドオンを追加
今回はPostgresを使ってデータを操作しているので、PostgresのサーバをHerokuアプリ上で連携させる必要があります。
アドオンの追加は、`heroku addons:create heroku-postgresql:<PLAN_NAME>`を実行することで追加できます。PLAN_NAMEにはhobbyやstandardなどがありますが、今回のように試しに上げてみる程度でしたら一番下のhobbyで十分です。詳しいプラン内容は[こちら](https://devcenter.heroku.com/ja/articles/heroku-postgres-plans#hobby-tier)をご覧ください。
```
heroku addons:create heroku-postgresql:hobby-dev
```

### 4．Heroku上のPostgresにデータを移行
今回はローカルの開発で使用した、PostgresのデータをHeroku上のPostgresに移したいと思います。
ローカルでバックアップファイルを作成してHeroku postgresでインポートする方法などもありますが、今回は`pg:push`を使用したいと思います。
```
SET PGUSER=postgres
heroku pg:push postgres DATABASE_URL --app flask-app
```
なお、DATABASE_URLの変更や新たなDBを追加したい場合はHerokuの管理画面から簡単にできます。

次に、ソースコードのconfig.pyで設定しているpostgresのURIをローカルからHeroku postgresのものに変更します。
そのためにはまずHeroku postgresのURIを確認しましょう。Herokuの管理画面から、Setting⇒config varsを開き、DATABASE_URLの横のURLをコピーします。そしてconfig.pyに貼り付けてください。
```config.py
SQLALCHEMY_DATABASE_URI = 'postgresql://xxxxxx'
```
なお、ここで注意点があります。管理画面では最初が`postgres`になっているかと思いますが、ここを`postgresql`に直してください。ここをそのままにすると`sqlalchemy.exc.NoSuchModuleError: Can’t load plugin: sqlalchemy.dialects:postgres`というエラーが出ます。原因は、SQLAlchemyがpostgresのサポートを削除したためらしいです。

これでHeroku postgresの設定が完了しました。あとはデプロイするだけです。

### 5．Gitからアプリケーションをデプロイ
リモートリポジトリのmasterブランチにデプロイすることでアプリが動きます。
```
git init
git .
git commit -m "first commit"
git push heroku master
```
これでデプロイ完了です。
`heroku open`を実行して実際に確認してみましょう。
もし、期待通りに反映されていない場合は`heroku logs`でエラーログを確認してみてください。


# 所感
今回初めてHerokuを使用してみましたが、無料でいろいろとできるので素晴らしいサービスだと感じました。
特にデプロイまでの手順が簡単なことや管理画面のUIがシンプルで使いやすい面も良いと思います。

# 追記
pg:pushはPowerShellだとエラーが起きるのでコマンドプロンプトから実行する