はじめに
python軽量なウェブアプリケーションFlaskとそれを包み込むgunicornをつかって動かすところまでメモした記事です。
#Flask
公式:http://flask.pocoo.org/
wikiによると、
プログラミング言語Python用の、
軽量なウェブアプリケーションフレームワークである。(wiki)
メリット・デメリット
あくまで個人的な考えですが
- メリット:必要最低限のメソッド(GET, POST, ルーティング等)しかないので、早くて使いやすい → 学習コストが低い
- デメリット:比較的自由度は高いので、フォルダ構成など、決めることが多い(逆を言い換えるとなんでもできる)
FlaskでHelloWorld
まずはpip install Flask
でFlaskをinstall
# インストールした「flask.py」から「Flask」Classをimport
from flask import Flask
# Flaskクラスをnewしてappに代入
app = Flask(__name__)
# urlとして”/hello”がgetメソッドで呼ばれると定義した関数が実行される
@app.route('/hello')
def hello():
hello = "Hello world"
return hello
if __name__ == "__main__":
#runメソッドでビルトインサーバーが走る
app.run()
補足:関数外やmain文で定義した変数は「グローバル変数」になるので、mainの中でapp~を使っててもエラーにはならない。
実行すると,
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
がでてくるので、http://127.0.0.1:5000/hello
こうURLをたたくと"Hello World"がでてくるはず(ポートとかなければ...)。
ルーティングに関してはデフォルトがGET
になっているのですが、POSTを指定したければ
@app.route('/hello', methods=['POST'])
このようにmethodを指定する必要がある。
#あくまで簡易的なビルトインサーバー
公式:http://flask.pocoo.org/
によると、
You can use the builtin server during development, but you should use a full deployment option for production applications.
日本語:あなたは開発の間はFlaskをつかってサーバーをビルトインできるけど、フルデプロイメント機能があるアプリケーションを使うべきだよ
ここでの、「デプロイ」はウェブサーバーの公開だと解釈すればいいかと。
なので、Flask単体でpublicにするアプリケーションにするのはいろいろまずいとのことです...
そこでgunicornの登場
gunicorn
Gunicorn ‘Green Unicorn’ is a Python WSGI HTTP Server for UNIX. It’s a pre-fork worker model ported from Ruby’s Unicorn project. The Gunicorn server is broadly compatible with various web frameworks, simply implemented, light on server resources, and fairly speedy.
日本語:GunicornはpythonのWSGI HTTP ServerでRubyのUnicornの派生だよ。Gunicornは様々なフレームワークと比べると、とってもシンプルな実装で、軽量なサーバーリソース、そしてとっても早いんだ。
WSGI HTTP Server:Web Server Gateway Interfaceのこと。
Pythonにおいて、WebサーバとWebアプリケーションを接続するための、標準化されたインタフェース定義
(wiki)
#Gunicornの良いところ(個人的)
-
ワーカーを複数設定できる(マルチプロセスみたいな)
→デーモン化が可能 -
systemdと組み合わせれる
-
サーバー再起動時もちゃんと自動で立ち上げてくれる
-
flaskソース変更してdeployしてもrestartで素早く変更点を反映できる
なのかなって思っています。(そうじゃないって方はコメントください)
gunicornの起動方法
pip install gunicorn
でgunicornをインストールして、
gunicorn hello:app
で起動する。
"hello"はhello.pyのファイル名
"app"はなかで宣言しているFlaskクラスインスタンス
起動するとListening at: http://127.0.0.1:8000 (25737)
が出てくるので、
http://127.0.0.1:8000/hello
を叩くと同様な結果がでるはずです。
gunicornのconfig
gunicornはワーカーを複数起動することができるので、そういったものはconfigで書いておきます。
import multiprocessing
# Worker Processes
workers = 2
worker_class = 'sync'
# Logging
logfile = '/var/www/apps/sampleapp/app.log'
loglevel = 'info'
logconfig = None
worker_classはsync, asyncで決めれますし、logの場所も設定できるので便利
なんなら、このコンフィグにのみ指定できる環境変数も設定できます。
configをよみこませつつ起動させる方法は
gunicorn hello:app --config guniconf.py
で起動されます。
もっと設定を見たい方は、http://docs.gunicorn.org/en/stable/settings.html#
起動すると、ps -aux|grep gunicorn
なんかでプロセス数を確認するとマスター+ワーカー2台の3つのプロセスが確認できます。
ワーカーのどちらかをkillしてみると、別プロセスが自動で生成されるはずです。
ただし、マスターをkillするとどっちも消えます。
#Gunicorn+nginx
Although there are many HTTP proxies available, we strongly advise that you use Nginx. If you choose another proxy server you need to make sure that it buffers slow clients when you use default Gunicorn workers. Without this buffering Gunicorn will be easily susceptible to denial-of-service attacks. You can use Hey to check if your proxy is behaving properly.
日本語訳:httpプロキシはたくさんの種類があるけど、我々(gunicorn制作会社)は強く”Nginx”をおすすめします。もし、あなたがほかのプロキシサーバーを必要だと感じ、使うとしたら、デフォルトのGunicornのワーカーを使ったときに、クライアントのバッファは遅くなります。
参考:
- https://stackoverflow.com/questions/26861761/why-use-gunicorn-with-a-reverse-proxy/34273740
- http://docs.gunicorn.org/en/stable/deploy.html
Dos攻撃などを考えるとリバースプロキシを使うNginxがいいよってことなのかなと思います。
構成図的には
client ⇔ Nginx ⇔ gunicorn ⇔ Flask
みたいな感じになります。
(https://medium.com/ymedialabs-innovation/deploy-flask-app-with-nginx-using-gunicorn-and-supervisor-d7a93aa07c18)
nginxの設定ファイル
nginxの設定ファイル(/etc/nginx/site-available/defaultやvhost)を書いていきます。
upstream hello_app {
server unix:/tmp/hello_app-api.sock;
}
server {
listen 80;
access_log /var/log/nginx/access.log main;
root /var/www/hello_project/
server_name hello.hogehoge.com
location / {
try_files $uri @flask;
}
location @flask {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_redirect off;
proxy_pass http://hello_app;
}
}
こんな感じで設定します。
http://hello.hogehoge.com/
を打つと(80番ポート)、location /
でマッチングして、@flask
にマッチングして、来たアクセスをproxy_passで設定されている hello_app というサーバ群(upstreamブロックに書かれている中身)に投げるという流れです。なので、rootである/var/www/hello_project/
下にgunicornのプロジェクトをおいておくと、その下でルーティングした/helloが叩くと表示されるわけです。そして、gunicornのconfigでaccess.logの設定を行っていればそこに書き込まれます。なければ、/var/logしたのnginxのaccessログに書き込まれているはず。
もっと詳しい設定はgunicornの公式にあります。ロードバランサとか設ける場合はx-forwarded-for
の設定をすべきかと思います。(実務寄り)
また、/tmpしたにsocketを設定してやることで、gunicornとの通信を可能にします。
gunicorn側でもconfigファイルにそのパスをつけてやることで、gunicornがsocketを生成し、nginxでそのsocketを通じてgunicornとの通信を行います。
socket_path = 'unix:/tmp/hello_app-api.sock'
bind = socket_path
これをgunicornのconfigファイルに書けばおk
Systemdでサービス化
前述でnginxとの設定ができて無事デプロイメントはできたものの、もし変更を加えたらいちいち、プロセスをしらべて止めて更新して再度起動する必要があります。なので、そこをsystemdでサービス化してしまえばsudo gunicorn restart
みたいに一発コマンドで再起動できるので便利です。
supervisorもあるのですが、3系ではなかった気がします。
(3系にできるのですが、β版をフォークしたような気がします。
参考:http://supervisord.org/upgrading.html
こちらもgunicornの公式に乗っているものを少しいじったくらいです。
http://docs.gunicorn.org/en/stable/deploy.html#systemd
[Unit]
Description=hello-api daemon
Requires=hello-api.socket
After=network.target
[Service]
PIDFile=/run/gunicorn/pid
User=root
Group=root
RuntimeDirectory=gunicorn
WorkingDirectory=/var/www/hello/
ExecStart=/usr/local/bin/gunicorn --pid /run/gunicorn/pid hello:app --config guniconfig.py
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
留意する点としては、
-
gunicornのコマンドはフルパスでかくこと。
-
PIDFileは/run下を設定すると、ubuntuの場合、リブート時に消えてしまうので、消えないように設定する必要があります。
/run下が消えないように、
/etc/tmpfiles.d/gunicorn.conf
に d (PIDFileのディレクトリ) 0755 root root -
といった内容を、vimなどで追加します。
これで、パスの中にあるgunicornファイルは再起動しても消えないはずです。
hello-api.socketも下記のように書けばおk
[Unit]
Description=hello-api socket
[Socket]
ListenStream=/tmp/hello-api.sock
[Install]
WantedBy=sockets.target
最後に、systemctl enable ソケット名
で起動できればsudo サービス名 start
ができればおk。
systemctl list-unit-files --type=service|grep gunicorn
などで、
gunicorn.service enabled
が確認できればpsなどでgrepして動いていることが確認できると思います。
だめな場合は、sudo systemctl status gunicorn
などで状態を調べつつやるしかないんや....!!!
終わりに
一応ここまで書いた記事をdocker-composeでかけるとこまでをここでかけたらなぁと思っている次第であります。
参考
2018年12月13日追記
アドベントカレンダーを書いてたところ、nginxのdefault.confが nginx -t
で通らなかったため、
修正しています。
変更点
- access.logのlstv形式は別途定義しないといけないため、mainに変更
-
try_files $uri @flask;
->try_files $uri @flask;