#heroku #python #Webアプリ #Flask #Pillow #デプロイ
Pillowを使った画像処理を行なうアプリをFlaskベースのWebアプリに変え、さらにherokuにデプロイして実行してみました。
- 確認したOS、pythonとバージョン
- サンプルプログラム
- herokuへのサインアップ
- heroku CLIのインストール
- heroku CLIからherokuにログイン
- デプロイ(アプリの配備)
- 作業中に遭遇したトラブル
- Flaskのルール違反
- アプリ画面が表示されず応答待ちが続いてタイムアウトエラーが発生する
- heroku logsに下記エラーが出るModuleNotFoundError: No module named ‘hogehoge’
- heroku logsに下記エラーが出るModuleNotFoundError: No module named ‘PIL’
- heroku logsに下記エラーが出るError R10 (Boot timeout) ->Web process failed to bind to $PORT within 60 seconds of launch
- heroku logsに下記エラーが出るModuleNotFoundError: No module named ‘hogehoge’
- heroku logsに下記エラーが出るFileNotFoundError: [Errno 2] No such file or directory: ‘hogehoge’
確認したOS、pythonとバージョン
Windows 10 Pro 1903(OSビルド 18362.752)
python 3.7.6
サンプルプログラム
本記事では、下記のサンプルプログラムを使用しました。
Pillowを用いた画像表示プログラムです。
<rainbow.py>
import io
from PIL import Image, ImageDraw # PILからImage, ImageDrawクラスを取り出す
from flask import Flask, request, redirect, url_for, render_template, send_file
app = Flask(__name__)
@app.route('/')
def index():
return render_template('img.html')
@app.route('/show_img', methods=['GET', 'POST'])
def show_img():
im = Image.new('RGB', ( 500, 600), (255, 255, 255)) # 500 ✕ 600ドットで白(=(255,255,255)の
# キャンバスを準備する
draw = ImageDraw.Draw(im) # 絵を描き込むバケツ(ImageDraw.Drwオブジェクト)を準備する
red = 0
green = 0
blue = 0
red_step = 5
green_step = 5
blue_step = -5
red_cnt = 0
green_cnt = -250
blue_cnt = 250
for delta in range( 0, 501, 1) :
red_cnt = red_cnt + red_step
if red_cnt > 0 :
red = red + red_step
if red > 250 :
red = 250
if red < 0 :
red = 0
if red_cnt >= 500 or red_cnt <= -250 :
red_step = -red_step
green_cnt = green_cnt + green_step
if green_cnt > 0 :
green = green + green_step
if green > 250 :
green = 250
if green < 0 :
green = 0
if green_cnt >= 500 or green_cnt <= -250 :
green_step = -green_step
blue_cnt = blue_cnt + blue_step
if blue_cnt > 0 :
blue = blue + blue_step
if blue > 250 :
blue = 250
if blue < 0 :
blue = 0
if blue_cnt >= 500 or blue_cnt <= -250 :
blue_step = -blue_step
draw.line((( delta, 0),( 500-delta, 500)), fill=(red, green, blue))
draw.line((( 0, delta),( 500, 500-delta)), fill=(red, green, blue))
im.save("static/images/tmp.png")
return render_template('img.html', img_url="static/images/tmp.png")
if __name__ == '__main__':
app.debug = True
app.run()
以下は、画像の表示先のWebページのHTMLファイルです。”img_url”のところに画像が表示されます。
<templates\img.html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Image</title>
</head>
<body>
<form method="post" action="{{ url_for('show_img') }}" enctype="multipart/form-data">
イメージを表示
<input type="submit" value="送信">
</form>
<div>
<p><img src="{{ img_url }}"></p>
</div>
</body>
</html>
最初から目的のアプリをデプロイしてもいいのですが、herokuに初めてデプロイをする場合は、下記のサンプルプログラムを使用して最初に一通り行ってみるほうが良いです。
<hello.py>
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
このプログラムはHello world!を表示するだけの、必要最小限のモジュール(Flask)だけをimportしており、他にも導入する必要のあるものは最小限となるためです。
このプログラムで最初にデプロイ~アプリ実行を行った後に、目的のプログラムでデプロイしてみて、どこが違うかを確認すると、スムーズに原因が突き止められます。
herokuへのサインアップ
https://id.heroku.com/login に行き、”Sign Up”で、サインアップ画面に行きます。
必要事項を記入すると確認のメールが届きますので、手続きを進めます。
ログインすると以下の画面になります。
ログイン後の画面の左下の”Documentation”を押します。
heroku CLIのインストール
“Documentation”を押すと下記画面になります。
The Heroku CLIを押します。
その後、画面を少し下にスクロールさせると、下記の画面が出てきます。
小生のOSはWindows 10 64bitのため、”64-bit installer”をクリックすると、下記ファイルがダウンロードされました。
実行すると、以下の画面になります。
“Next”を押します。
インストール先フォルダを変更しない場合は、そのまま”Install”を押します。
“Close”を押して終了します。
heroku CLIからherokuにログイン
コマンドプロンプトを起動し、heroku loginを実行します。
以下の画面が表示されます。
“Log in”を押して、メールアドレスとパスワードを入力すると、以下のようにログインが完了します。
コマンドプロンプト上でも、成功している旨のメッセージが表示されます。
デプロイ(アプリの配備)
runtime.txtの作成
以下のような記載をしたファイル runtime.txt を作成します。
バージョンは python –version で確認します。
<runtime.txt>
python-3.7.6
requirements.txtの作成
gunicorn(*1)と、importしているモジュールを記載した requirements.txtを作成します。
ただし、画像処理用のモジュールである”PIL”をインポートしますが、”PIL”のままではだめで、Pillowとする必要がありました。おそらくPillowの前身がPILであり、区別するためにそうなっているのかと思われます。
なお、hello.pyをデプロイする場合は、”Pillow”は必要ありません。
<requirements.txt>
gunicorn
flask
Pillow
gnicornのように、このファイルには、importに指定したもの以外に導入が必要なものも記載が必要になります。
一般のサイトには、そのことも考えて、pip freeze > requirements.txt を実行して作成する方法が多数公開されていますが、Anacondaを導入している小生の環境では、エラーが多発しました。
Anacondaには、導入物が大量にあるためで、herokuでは扱っていないらしいライブラリ・モジュールを導入しようとしてエラーになったようです。
Anacondaのケースのように、かえって必要以上のものをデプロイしようとしてエラーになることもあるので、pip freeze > requirements.txt が万能の方法であるとは言えません。
*1 WSGIサーバーと呼ばれるPython固有の
Webアプリケーションサーバー
WSGIはWeb Server Gateway Interfaceの略。
gnicorni以外にもいくつかある。
Procfileの作成
他のサイトではProcfileにpythonを記載しているものを見かけましたが、pythonではなく、gunicornが必要でした。
前述のようにgnicorn以外のWSGIサーバを使っている場合は、それが必要になります。
<Procfile>
web: gunicorn rainbow:app --log-file=-
上記の “rainbow”は、メインプログラム rainbow.py で、”app”は下記のように代入しているFlaskクラスのオブジェクトを指定しています。
app = Flask(__name__)
hello.pyをデプロイする場合は、 以下のProcfileを作成します。
web: gunicorn hello:app --log-file=-
デプロイ~実行
herokuアプリの名前を “boh-testapp-001” としてデプロイする場合、下記の手順となります。
なお、小生は以下をコマンドプロンプトで実行しました。
: herokuアプリを作成
heroku create boh-testapp-001 --buildpack heroku/python
: gitのローカルレポジトリ作成
git init
: herokuのリモートレポジトリの作成(?)
heroku git:remote -a boh-testapp-001
: herokuのリモートレポジトリとローカルレポジトリのリンクしリモートレポジトリ名"heroku"とする
git remote add heroku https://git.heroku.com/boh-testapp-001.git
: または、上のを訂正したい場合は、
git remote --set-url heroku https://git.heroku.com/boh-testapp-001.git
: 全ファイルをローカルレポジトリに登録
git add .
git commit -m "<comment>"
: herokuのリモートレポジトリmasterブランチにpush
git push heroku master
herokuアプリの実行は以下で行います。
Webブラウザが起動されて、アプリが実行されます。
heroku open
送信ボタンが表示されるので、押すと以下の画面が表示されます。
アプリの実行がうまくいかないとき
アプリの実行がうまくいかなかったときは、まず、どんなエラーが発生しているかを、以下で確認します。
heroku logs
エラーが発生したときには、どんな場合でもそうですが、まずは一番最初に発生しているエラーに着目して原因究明しましょう。
下記のような crash のエラーが常に発生してしまうときは、dynoというコンテナ(アプリを実行する1つの入れ物)を再起動(restart)すると良いようです。
2020-07-28T12:16:15.634251+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/" host=boh-testapp-001.herokuapp.com request_id=fa086a94-16b3-41e7-bbee-d61f3e54362b fwd="211.124.115.230" dyno= connect= service= status=503 bytes= protocol=https
再起動(restart)は以下のように実行します。
“web.1″はdynoの1番目を意味します。
dynoが複数ある場合は、web.2 ~についても実行します。
heroku restart web.1 --app boh-testapp-001
作業中に遭遇したトラブル
Flaskのルール違反
Flaskを使う場合、ルールとして以下のものがありましたが、それに従っていなかったためにうまく動作しませんでした。
他にもルールがあると思いますので、他のサイトをご参照ください。
・render_templateを使う場合は、templatesディレクトリを作って、テンプレートに使うHTMLファイルをそこに配置する必要がある
・プログラム中で、例えばPillowのim.save()などで画像を生成する場合、staticディレクトリを作成してherokuにデプロイする必要がある
staticディレクトリがない場合、空のディレクトリが必要になるため、例えば”.gitkeep”などの空のファイルを echo > static/.gitkeep などとして作成し、git add ~を実行してherokuにデプロイする必要があります。
アプリ画面が表示されず応答待ちが続いてタイムアウトエラーが発生する
gunicornがデプロイされていなかったことが原因でした。
Procfileにgunicornを追加してgit add Procfile ~ git push heroku master を行います。
heroku logsに下記エラーが出るModuleNotFoundError: No module named ‘hogehoge’
hogehogeモジュールの不足が原因でした。
requirement.txtに追記して再実行します。
heroku logsに下記エラーが出るModuleNotFoundError: No module named ‘PIL’
モジュール名はPILではなくPillowにする必要がありました。
requirement.txtに追記して再実行します。
heroku logsに下記エラーが出るError R10 (Boot timeout) ->Web process failed to bind to $PORT within 60 seconds of launch
herokuのポート5000での実行がうまくいっておらず、接続タイムアウトになりました。
このケースの場合、原因はいろいろありますが、このときは、gunicornの導入が必要でした。
Flaskを使ったらポート5000で起動するはずなのですが、gunicornのようなWSGIサーバーも必要らしいです。
ここは、今後調査を行います。
heroku logsに下記エラーが出るModuleNotFoundError: No module named ‘hogehoge’
Procfileに記載のFlaskクラスのオブジェクト名に誤りがありました。
heroku logsに下記エラーが出るFileNotFoundError: [Errno 2] No such file or directory: ‘hogehoge’
以下のような原因が考えられます。
・プログラム中で生成されるファイルの格納ディレクトリがない
格納ディレクトリに、.gitkeepファイルを配置してgit add~してデプロイします。
・git add, commit漏れしているファイルやディレクトリがある
漏れているものをgit add~してデプロイします。
コメント