背景・目的
Pythonで実装したアプリケーションを本番環境にデプロイする際にuWSGIを用いるのは知っていたが、なぜ使うのかや使い方について知らなかった。そこで、NginxとuWSGIとPython + Flaskを用いてRaspberry Pi 3 Model B+で動かしてみる。
そもそもWSGIとはWeb Server Gateway Interfaceの略である。PEP 3333のAbstructに記載されているようにウェブサーバーとPythonアプリケーションを繋ぐインターフェースのことで、様々なWebサーバ間でWebアプリケーションの移植性を高めるものである。
This document specifies a proposed standard interface between web servers and Python web applications or frameworks, to promote web application portability across a variety of web servers.
このWSGIに即したWSGIアプリケーションコンテナのuWSGIを用いてPython + Flaskで作成したアプリケーションをデプロイする。
実装
実装した順序は以下である。
- 開発環境でFlaskのアプリケーションを動かす
- uWSGIでFlaskのアプリケーションを動かす
- Nginxの設定をしてブラウザからラズパイのIPを叩く
- uWSGIを自動起動化させる
開発環境でFlaskのアプリケーションを動かす
まず、開発環境のディレクトリは以下のようになっている。
$ tree
.
├── README.md
├── conf
│ └── uwsgi.ini
├── requirements.txt
└── src
└── main.py
requirements.txtに必要なライブラリを記載している。これを用いて pip install -r requirements.txt
でライブラリをインストールできる。
$ cat requirements.txt
flask
isort
flake8
ipython
uwsgi
src/main.py は以下である。
$ cat src/main.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World!'
if __name__ == '__main__':
app.run()
python src/main.py
で開発用サーバーが5000番ポートで起動し、アプリケーションが実際に動いていることがわかる。
uWSGIでFlaskのアプリケーションを動かす
実際にアプリケーションを運用する際には、様々な設定をする必要がある。例えば、ソケットの出力先やプロセス数やスレッド数などが挙げられる。これらの設定をuwsgiコマンドのオプションに連ねて指定するのは面倒なので、以下のuwsgi.iniファイルにオプションをまとめる。
そうすると、uwsgi --ini uwsgi.ini
でアプリケーションが実際に動いているかを確認することができる。
$ cat conf/uwsgi.ini
[uwsgi]
wsgi-file = ../src/main.py
; module = main
callable = app
chmod-socket = 666
socket = /tmp/%n.sock
;http = 0.0.0.0:8080
;daemonize = /var/log/uwsgi/flask/%n.log
;pidfile = /tmp/app.pid
;harakiri = 600
Nginxの設定をしてブラウザからラズパイのIPを叩く
Nginx の80番ポートにHTTPリクエストでアクセスした後、UNIXドメインソケットを利用してuWSGIにプロキシを行う。そのNginx側の設定が以下になる。
server {
listen 80;
# 80番ポートにアクセスするとFlaskのアプリにリバースプロキシする
# この方法だとポートを無駄に使わずに済む
location / {
include uwsgi_params;
uwsgi_pass unix:///tmp/uwsgi.sock;
}
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_http_version 1.1;
proxy_connect_timeout 600;
proxy_read_timeout 600;
proxy_send_timeout 600;
send_timeout 600;
uwsgi_read_timeout 600;
}
ちなみに、UNIXドメインソケットとは、同一マシン上で動いているプロセス間通信を行うためのソケットのことである。従って、他のマシンと通信することはできない。一方で、IPとポート番号を頼りにネットワークを介して他マシンと通信するソケットは、INETドメインソケットと呼ばれる。UNIXドメインソケットは同一マシン上のプロセスとしか通信することができないが、INETドメインソケットと比較すると高速である。
uWSGIを自動起動化させる
uWSGIをdaemon化する方法はいくつか挙げられる。1つ目は、uwsgiコマンドのオプションにdaemonize
のオプションを付けることである。2つ目は、systemctlにuWSGIを登録して自動起動化させる方法である。今回の実装では2つ目を採用した。
- サービス(/etc/systemd/system/app.service) の作成
[Unit]
Description=uWSGI instance to serve myapp
After=network.target
[Service]
;User==www-data
Group=www-data
WorkingDirectory=/home/pi/WorkSpace/DeployFlask/conf
ExecStart=/home/pi/.pyenv/shims/uwsgi --ini /home/pi/WorkSpace/DeployFlask/conf/uwsgi.ini
[Install]
WantedBy=multi-user.target
- サービスの起動
$ sudo systemctl start app
- サービスの自動起動の設定
$ sudo systemctl enable app
- サービスの状況把握
$ sudo systemctl status app
● app.service - uWSGI instance to serve myapp
Loaded: loaded (/etc/systemd/system/app.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2020-05-12 18:22:25 JST; 7min ago
Main PID: 2615 (uwsgi)
Tasks: 1 (limit: 2200)
Memory: 10.9M
CGroup: /system.slice/app.service
└─2615 /home/pi/.pyenv/versions/DeployFlask/bin/uwsgi --ini /home/pi/WorkSpace/DeployFlask/conf/uwsgi.ini
詰まったこと・課題
- uwsgi.iniファイルに daemonize = /var/log/uwsgi/flask/%n.log を記述していたので、一定時間後にサーバーが502を返す自体が生じた。uwsgiをdaemon化させるために取った1つ目の方法だったが、その記述が悪さをしていたので削除した。
参考
Deploy
- PEP 3333 -- Python Web Server Gateway Interface v1.0.1
- Web Server Gateway Interface
- uWSGI
- DeployFlask