nginx/gunicorn/supervisorでflaskアプリを動かす
こんにちは
かねしろ@pinkrootです
今まで、flaskで作ったアプリはapache2 + wsgiの構成で動かしていたのですが、
Railsの勉強をしているうちに、nginx/unicorn構成も良いなぁと思いましたので、
類似環境でflaskアプリを動かすようにしてみました。
構成としてはnginx/gunicornとなり、gunicornをデーモン化させるためにsupervisorを利用しました。
なお、OSはubuntuです。AWS上にEC2インスタンスとして構築しました。
簡単ですが備忘録を残します。
必要なもののインストール
とりあえずざっくりと書きコマンドで必要なものをインストールします。
sudo apt-get install -y nginx
sudo apt-get install -y gunicorn
sudo apt-get install -y python-pip
sudo apt-get install -y python-dev
sudo apt-get install -y supervisor
sudo apt-get install -y git
うまくinstallできない場合はapt-getのupdateなどをお試しください。
flaskアプリを配備
動かしたいflaskアプリを
/var/www/sample
にでも配備しましょう。
動作確認だけで良ければ下記で十分かと思います。
#!/usr/bin/env python
# -*- encoding:utf8 -*-
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run(host='0.0.0.0', debug=True)
ファイル名はapp.pyなどわかりやすい感じで。
次に、必要なモジュール群をpipでインストールしましょう。
上記だけであれば
sudo pip install Flask
で十分かと思います。
viewをちゃんと書くならJinja2を入れたり、ログを出すためにloggingを入れたり、というのがバラバラと発生するようであれば、
プロジェクトディレクトリのトップに
requirements.txt
でも作成し、その中に
Flask
Jinja2
logging
とインストールしたいものを一覧で記載し、
sudo pip install -r requirements.txt
とすると楽ができます。
なお、最近のトレンドはvenvを使ってプロジェクトごとに環境を作り分けるそうですが今回はやっていません。
この時点での動作を確認したい場合、
app.run(host='0.0.0.0', debug=True)
となっている箇所を
app.run(host='0.0.0.0', debug=True, port=8080)
という風にポート番号指定の形に書き換えてあげて、
python app.py
とすると、
http://サーバのIPアドレス:8080へアクセスすることでHello Worldが垣間見れるかと思います。
AWSの場合はセキュリティグループ設定で8080ポートへのアクセスを許可するように設定してあげてください。
ついでに後述する設定のために80へのアクセスも許可しておくとハッピーです。
nginxの設定
nginxの設定ファイルについては
/etc/nginx/sites-available
に設定ファイルを作成し、
/etc/nginx/sites-enabled
にシンボリックリンクを作成するのがセオリーなようですが、今回はシンボリックリンクだとうまく動作しなかったので、直接sites-enabled直下に設定ファイルを作成しました。
内容は下記のようになっています。
# gunicornとソケット通信を行うという設定
upstream my_app_server{
server unix:/tmp/gunicorn_my_app.sock fail_timeout=0;
}
server {
# 独自ドメインを設定する場合は
# server_name ドメイン名;
server_name localhost;
charset utf-8;
client_max_body_size 75M;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
# ドメイン直下へのアクセスについての設定は @my_appを参照するように、という指定
location / {
try_files $uri @my_app;
}
location @my_app {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_redirect off;
proxy_pass http://my_app_server;
}
}
上記設定に問題がない場合は
sudo /etc/init.d/nginx restart
でnginxが起動してくれるはず。
この時点で
http://サーバのIPアドレスへアクセスすると502エラーが表示されるはずです。
gunicornが起動していないので現時点だとそれで問題ありません。
他のエラーの場合はflaskのポート指定が間違えている可能性があるので見なおしてください。
もしくはAWSのセキュリティグループの設定ミスです。ポート開けてないとかそういう感じの。
gunicornの設定
まずはgunicornの設定ファイルを作りましょう。
flaskのプロジェクトディレクトリにgunicorn_conf.pyなどといった名前でファイルを作成し、内容を下記のようにします。
import multiprocessing
# Server Socket
bind = 'unix:/tmp/gunicorn_my_app.sock'
backlog = 2048
# Worker Processes
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = 'sync'
worker_connections = 1000
max_requests = 0
timeout = 30
keepalive = 2
debug = False
spew = False
# Logging
logfile = '/var/log/gunicorn/my_app.log'
loglevel = 'info'
logconfig = None
# Process Name
proc_name = 'gunicorn_my_app'
ここまで問題なければ、flaskのディレクトリで
sudo gunicorn app:app –config /var/www/sample/gunicorn_tofu.conf.py
でgunicornが起動します。
nginxとgunicornが無事に連携して仕事をしてくれていれば、
http://サーバのIPアドレスへアクセスした際に、今度はエラーではなくHello Worldがコンニチハするはずです。
supervisor
さて、gunicornさんですが、デーモン化をきちんと管理させるためにsupervisor経由で動かすようにしたほうが安全とのことです。
名残惜しいですが一旦
sudo pkill gunicorn
で起動プロセスを殺し、supervisorの設定を行いましょう。
/etc/supervisor/conf.d
の下にmy_appといったファイルを作成し、下記のような内容を記載します。
[program:my_app]
command = gunicorn app:app --config /var/www/sample/gunicorn_conf.py
directory = /var/www/sample
user = root
先程手動で起動させる際に用いたコマンドを記載し、ディレクトリとユーザを指定しているだけです。
本来的にはrootではないユーザを作成し、そのユーザで起動するようにしてあげたほうがセキュアではあるのですが今回はざっくりとrootで。
supervisor経由での起動は下記コマンドを用います。
流れとしては設定ファイルの再読み込み・更新・起動となります。
設定ファイルに更新がない場合は起動コマンドだけで十分かと。
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start my_app
念のために
ps alx|grep gunicorn
コマンドなどでプロセスを確認すると、問題なくgunicornさんが仕事をしてくれているはずです。
これで当初目的としていた組み合わせの実現ができました。やった。
細かいところは省略したりもしていますし、venvやらユーザの指定やらは実施せずにざっくりと設定しているので、更に改善はできる設定ではありますが、参考になれば幸いです。
いいゴールデンウィークのスタートを切れました。
おしまい。