【完全版】Flaskで作ったAPIをHerokuにデプロイする手順（備忘録）

# はじめに
**「もうHerokuデプロイに詰まるのはこりごりだ」**

**「色々なサイト通りにやっても毎回エラーが出てきてしまう」**

と思い、今一度デプロイの手順から過去に自分が出会ったことのあるエラーと対処方法を一箇所にまとめておこうと思った。  
基本的に自分用であるため、何か随時新しいエラーに出会うたびに情報を追加しておこうと思う。

# 基本的なデプロイ手順
この通りにデプロイに成功すれば苦労しないのだが。。

### Flaskプログラムを作る。

```app.py
import os
import io
import time
import numpy as np
import cv2
import dlib
from flask import Flask, render_template, request, redirect, url_for, send_from_directory, session
from werkzeug import secure_filename
app = Flask(__name__)

# <--プログラム-->

@app.route('/')
def index():
    return 'Hello World!'

@app.route('/post', method=['POST'])
def post_json():
#     <--プログラム-->

@app.route('/show-data', methods=['POST'])
def show_json():
#     <--プログラム-->
    
if __name__ == '__main__':
    app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 5000)))
```

[参考例①：related-tweet-words](https://github.com/seven0525/related-tweet-words-api)
[参考例②：face-golden-ratio](https://github.com/seven0525/face-golden-ratio-api) 

基本的に**app.py**という名前ファイルにしておくことをオススメする。
また、最後の*host="0.0.0.0"*箇所はかならず0.0.0.0にすることに注意（外部環境からでもアクセス可能にするため） 
また、そもそもこのプログラム自体が動いているのかを確認するために以下のようなルートを作っておくことをオススメする。

```
@app.route('/')
def index():
    return 'Hello World!'
```

### ローカル環境にて動くかを確認（curlコマンドを使用）

まずは、以下のコマンドからHello Worldが帰ってくるか確認する。
curlの使い方については[こちら](https://qiita.com/takayukioda/items/edf371b3566bea64d046#-x-method-http-%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%AE%E6%8C%87%E5%AE%9A)を参考にすると良い

```
$ curl http://127.0.0.1:5000
```

次に、それぞれのルートが機能するか確認。

```
$ curl -X POST -H "Content-Type: application/json" -d '{"key_word":"お腹すいた"}' http://127.0.0.1:5000/post
```
値の渡し方の形式については調べること。
curlで渡した値やファイルがきちんと読み込まれているかや、どこまでプログラムが実行されているのか確かめるために、怪しいところで*print("a")*とか書いてデバッグすること。

### GitHubにアップロード
ここまでで、きちんとFlaskのAPIが動くことが確認できたらGitHubにリポジトリを作成しファイルをアップロードする。

### 仮想環境をつくる
このまま進めていくと、このプログラムを動かすためには現在ローカルにインストールされているモジュールを全て必要と勘違いされてしまうため、このプログラム専用の仮想環境を構築します。

```
$ cd プログラムと同じディレクトリ
$ python -m venv heroku_env       # 仮想環境を作る(Python 3.3からの標準機能)
$ source heroku_env/bin/activate  # 仮想環境をアクティベート
```

### 必要なモジュールのインストール
そうしたら、この仮想環境に必要なモジュールをインストールしていきます。
app.pyを実行してみたときに何のモジュールが足りないのかをみて、それぞれインストールしていきます。
さらに、インストールしたモジュールをまとめてrequire.txtに書き込んでおきます。

```
$ pip install --upgrade pip       # pipアップグレード
$ python app.pyでinstallが必要なモジュールをpipでインストールしていく
$ pip freeze > requirements.txt   # requirements.txtを作成
```
実際に*cat requirements.txt*などでモジュールが書き込まれているか確認してみましょう。

### Procfileの作成
**Procfile**というファイルを作成し、以下のようにflaskのファイルと設定を書きこみます。

```
web: gunicorn app:app --log-file -
```

### runtimeの作成
これはなくても良いですが、このプログラムを動かすのに使用するPythonのバージョンを書いておきます。

```
python-3.6.4
```

### Herokuの設定
そうしましたら、現在のフォルダの中身がこのようになっているのを確認し、いよいよHerokuの設定を行なっていきます。

```
folder
  ｜- app.py
  ｜- requirements.py
  ｜- runtime.py
  ｜- Procfile
```

```
helok login # webページ画面にてログインをクリック
heroku create flask-api # ユニークなアプリ名をつけてHerokuリポジトリの作成
```


### デプロイ
そうしたらいよいよHerokuに自分のプログラムをデプロイしていきます。

```
git init(エラーでるけど無視)
git add .
git commit -m”aaa”
git push heroku master
```


# エラー対処（そもそもpushできない）
ターミナルに表示されるERRORログを確認しましょう。

### Git remote先にHerokuのリポジトリがない
→ 接続先URLを指定する

```
$ git remote -v # 現在リモート接続されているURLを確認する

$ git remote set-url heroku {ここに変更するURLを貼り付け} #  生成した〇〇.gitみたいなやつ
```
[参照](https://qiita.com/Tattsum/items/b86c9d698b0727934836)

### 必要なモジュールがインストールされていない
→ ログに表示されている足りないモジュールのインストール

```
$ pip install モジュール
```

あるモジュールをしようするために実は必要なモジュールがあったりというケースがよくあります。
(cmakeなど)



※まだまだ見つかり次第追加予定

# エラー対処（Pushはできるがデプロイに成功しない）
こっちのエラーノ方がなかなかやっかいです。
本来"Hello World"が表示されるべきルートにアクセスしても、エラー画面が表示されてしまいます。
この場合は、**heroku log**を使ってひたすら原因分析をしていくしかありません。
ただし、ほとんどの場合は**ERORR**と書かれているログにだけ注目すればよいので実はそこまで大変ではありません。

### gunicornがインストールされていない（し忘れ）
→ gunicornをインストールし、requirements.txtに追記しましょう。

こんな風にH10のエラーになっている場合が多いです。  
そのエラーの上をみましょう。

```
2019-09-05T09:01:21.800995+00:00 heroku[web.1]: Starting process with command `gunicorn app:app --log-file -`
2019-09-05T09:01:24.172663+00:00 heroku[web.1]: State changed from starting to crashed
2019-09-05T09:01:24.078657+00:00 app[web.1]: bash: gunicorn: command not found
2019-09-05T09:01:24.150286+00:00 heroku[web.1]: Process exited with status 127
2019-09-05T09:01:34.471547+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/" host=berief.herokuapp.com request_id=ca9a42de-1515-4aa3-b0b8-8f602e245b70 fwd="124.35.198.10" dyno= connect= service= status=503 bytes= protocol=https
2019-09-05T09:01:35.327634+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/favicon.ico" host=berief.herokuapp.com request_id=b11fc14a-ba08-4d52-a120-4098dfa99aff fwd="124.35.198.10" dyno= connect= service= status=503 bytes= protocol=https
```

```
$ pip install gunicorn 
$ gunicorn -v # バージョンの確認
$ vi requirements.txt # guniornを追記（バージョン指定）
```


### メモリが多すぎる
ERROR R14とか R15とかいうやつです。
重たい処理をさせるAPIの場合Herokuにデプロイできるファイルサイズを超えてしまっている場合があります。
-> サーバーの子プロセスであるworkerのプロセス数を１にする

```
$ heroku config:set WEB_CONCURRENCY=1
```

### libSMのなんちゃらがimportできないと言われれる
特定にモジュールがインストールできないことを表しています。
私の経験ですと、**cv2**など少し複雑なモジュールをimportするプログラムの場合によく起こります。
必ず世界の誰かが同じエラーを踏んで解決してくれているはずなので、エラー文から検索をかけ必要なモジュールをインストールするようにしましょう。

※まだまだ見つかり次第追加予定


# 終わりに
Herokuへのデプロイは本当に毎回困らされます。
一緒に頑張りましょう。

自分が見つけたエラー以外にも過去遭遇したエラーなどありましたら教えてくださると大変助かります。
最終的には、Herokuのエラー対処一覧みたいなものを完成させたいです。