はじめに
uWSGI はプロトコルであり、同時にプログラムでもあります。なぜこれほど複雑なのでしょうか。まずはプログラムの話から始めましょう。uWSGI は、もともと Python で書かれた Web アプリケーションが Nginx や Apache などの Web サーバーと通信できるようにするために作られたプログラムです。uWSGI は独自の uwsgi 通信プロトコルを採用しています。また、cgi、fastcgi、socket などほかのプロトコルにも対応しているため、Python だけでなく PHP や Go などのプログラミング言語とも連携できます。
私の理解では、uWSGI はサーバーのようなもので、入口と出口があります。対応しているさまざまなプロトコルでデータを入力すると、プラグイン(python、php など)で処理されます。処理が終わると、さまざまなプロトコルでデータが出力されます。その出力データは nginx や apache に転送することも、そのまま http で出力することもできます。
uWSGI の仕組みは FastCGI とよく似ています。複数のワーカープロセスを常駐させておき、タスクが来ると、それらのプロセスに処理を任せ、処理が終わったら結果を返します。
Nginx は uWSGI をビルトインでサポートしています。 uWSGI では vhost も有効にできます。
インストール
ここでは python3 と関連ツールをインストールし、pip を使って uwsgi をインストールします。$ sudo apt-get install python3 python3-pip python-dev build-essential
$ pip3 install --upgrade pip
$ pip3 install virtualenv uwsgi
ネット上の記事には、apt-get で直接 uwsgi をインストールしているものもありますが、そのバージョンは古いため、pip でのインストールをおすすめします。
また、apt-get で最新版の Nginx をインストールします。$ sudo apt-get install python-software-properties
$ sudo add-apt-repository ppa:nginx/stable
$ sudo apt-get update
$ sudo apt-get install nginx
このようにインストールするのは、apt-get に標準で入っている nginx のバージョンが古すぎるためです。設定
設定は大まかに Nginx 側と uWSGI 側に分かれます。その処理の流れは次の図のとおりです。すべてのトラフィックはまず Nginx に入ります。画像、HTML、ファイルなどの静的なリクエストは Nginx が処理します。Python や PHP などの動的なリクエストは uWSGI に転送され、uWSGI で処理が終わると、その結果が Nginx に戻されます。
ここで設定する項目は次のとおりです。
- Nginx vHost
- uWSGI vHost and forward
また、設定は大きく2つの段階に分かれます。
- 基本設定(Nginx、uWSGI)
- 自動起動とサービスの設定
まずは Nginx の設定から始めましょう!
Nginx に関係する主なフォルダーは2つです:
- /etc/nginx/sites-available
- /etc/nginx/sites-enabled
$ sudo rm /etc/nginx/sites-available/default
$ sudo rm /etc/nginx/sites-enabled/default
$ cd /etc/nginx/sites-enabled
$ sudo vi demo.conf
内容はおおむね次のとおりです:server {
listen 80;
server_name 52.68.0.97;
charset utf-8;
client_max_body_size 20M;
location / {
uwsgi_pass unix:/var/www/demo/demo.sock;
# uwsgi_pass 127.0.0.1:8080;
include uwsgi_params;
}
location /static {
alias /var/www/demo/static;
}
}
設定ファイルは分かりやすいものです。特定のポートとドメインを待ち受けており、重要なのは uwsgi_pass の部分です。待ち受けているポートやドメインにリクエストが届くと、uwsgi_pass に設定されたものへ転送されます。設定方法は2通りあります。uWSGI が作成した sock ファイルを直接読み込む方法と、指定したポートに socket で送信する方法(そのポートで uWSGI が待ち受けます)です。ここでは、少し複雑ですが sock ファイルの方法で設定します。
設定を保存したら、Nginx の再起動を忘れずに行ってください:
$ sudo service nginx restart
次に、/var/www 内にデモ用の Python ウェブサイト(Flask)を作成します。ここで virtualenv を使っているのは、uWSGI がアプリケーションごとに異なる環境を読み込めるためです。そのため、ウェブサイトのプログラムは Python 2 でも Python 3 でも問題なく書けます。
$ sudo mkdir demo
$ sudo chown -R ubuntu:ubuntu demo
$ cd demo
$ virtualenv venv
$ source venv/bin/activate
$ pip install flask
$ vi hello_module.py
hello_module.py ファイルに、最も基本的な Flask の例(Hello world)を書き込みます:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()
デモ用のプログラムを作成したら、まずは最もシンプルな uwsgi コマンドで、ここまでのすべての設定が正しく動くかをテストしましょう。段階的にテストすると、問題の箇所を見つけやすくなります。
$ uwsgi --module demo_module --callable app --http :1234 --venv /var/www/demo/venv
このコマンドは demo_module.py をモジュールとして uWSGI に読み込ませます。さらに、Nginx から呼び出されるたびに、uWSGI は app というオブジェクト(callable)を探し、http プロトコルで 1234 番ポートに出力するように設定しています。最後に、必要なパッケージの場所(–venv)を指定しています。
ブラウザで http://localhost:1234 と入力すると確認できます。サーバーがリモートにある場合は、SSH プロキシでトンネルを構築してテストすることもできます( ssh -ND 2001 remote_server )
ここで問題が起きた場合は、uWSGI が画面に出力するメッセージを確認してください。通常はどこに問題があるかが書かれています。基本的に、この段階の問題は Nginx とは無関係です。現在の設定では uWSGI がすべてのパケットを処理しており、まだ Nginx とはつながっていないからです。
この段階でテストがうまくいったら、コマンドラインの方法で一時的に Nginx とつないでテストしてみましょう:
$ uwsgi --module demo_module --callable app --socket /var/www/demo/demo.sock --chown-socket www-data:www-data --venv /var/www/demo/venv
このコマンドは前のものと似ていますが、次の項目が追加されています:
–socket:Nginx と通信するために使う sock ファイルを指定するパラメーターです。 – chown-socket:生成される sock ファイルの権限を設定します。Nginx のユーザー名である www-data に設定しています。
前の設定で Nginx と socket を使って通信している場合は、「–socket」を 127.0.0.1:8080 に設定でき、その場合は chown-socket を設定する必要はありません。
続けてブラウザを開き、http://localhost:80 にアクセスしてテストします(Nginx で設定したポートです)。Hello World が表示されるはずです。表示されない場合は、/var/log/nginx のログでエラーを確認してください。先ほど uWSGI は正常に動くことを確認済みなので、この問題は Python や uWSGI とは無関係だと判断できます。
ここまでうまくいったら、おめでとうございます。先ほどの長いパラメーターの並びを設定ファイルに変換しましょう。uWSGI は ini、xml、json などの複数の設定ファイル形式に対応しています。ここでは ini を例に使います:
$ vi demo.config.ini
次の内容を書き込みます:
[uwsgi]
chdir = /var/www/demo
module = demo_module
callable = app
venv = /var/www/demo/venv
socket = /var/www/demo/demo.sock
chown-socket = www-data:www-data
次にこのコマンドを実行します。ブラウザで Hello World が表示されれば、おめでとうございます、完成です! あとは uWSGI をデーモンとして実行するだけです!
$ sudo uwsgi --ini demo.config.ini
ここで一息ついて、お茶でも飲みましょう。次は uwsgi を起動時に自動起動するように設定します。方法はいくつもあります。たとえば、スクリプトを書いて Upstart サービスにする方法があります。この方法の利点は、簡単に起動や停止ができることです。また、多数のアプリを起動する場合(Heroku のような環境)は、uWSGI Emperor を使って管理することもできます。ここでは Upstart サービスを例に説明します:
$ sudo vi /etc/init/uwsgi.conf
内容は次のとおりです:
description "uWSGI instance to serve myapp"
start on runlevel [2345]
stop on runlevel [!2345]
script
cd /var/www/demo
source venv/bin/activate
exec /usr/local/bin/uwsgi --ini demo.config.ini
end script
保存したら、サービスとして起動できます:
$ sudo service uwsgi start
More config
ここでは設定に関する補足をいくつか紹介します。参考:公式オプションページ 参考:非公式の中国語版設定翻訳
おすすめ
マスタープロセスとして設定master = true
ワーカープロセス数を設定
processes = 4
各プロセスの物理メモリ使用量が上限を超えたら、そのプロセスを再起動
reload-on-rss = 80
各プロセスの仮想メモリ使用量が上限を超えたら、そのプロセスを再起動
reload-on-as = 80
各プロセスの仮想メモリ使用量の上限を制限
limit-as = 120
uwsgi を終了するとき、生成した sock ファイルも一緒に削除
vacuum = true
uWSGI が生成するログにメモリ使用量のフィールドを追加
memory-report = true
vhost を有効にして、1つのプロセスで複数のサイトを実行
vhost = true
ログファイルを指定した場所に置く
logto = /var/log/uwsgi/pada.log
変更があったら自動で再読み込みし、サービスを手動で再起動する必要をなくします。後に設定する秒数は、プロセスが作業中の場合に最大 N 秒待ってから強制終了することを意味します。
reload-mercy = 8
簡単なパフォーマンス最適化
先ほど触れた reload-on-rss や processes などの設定は、うまく設定すればパフォーマンスが向上しますが、うまく設定しないと問題が起きます。ここでは設定方法を簡単に説明します。理論上は、processes の数字が大きいほど処理速度は速くなります。 理論上は、reload-on-rss の数字が小さいほど処理速度は遅くなります。
ただし、これらはあくまで理論です。実際の数字は、uWSGI 上で動かすプログラムがどのくらいのメモリを使うか、そして機器にどれだけ空きメモリがあるかによります。
memory-report を有効にすると、プログラムが1回の実行でどのくらいのメモリを消費するかを確認できます。その数字より少し高めの値を reload-on-rss に設定します。さらに、nginx、Linux、その他のサービスを動かした後にサーバーにどのくらいのメモリが残るかを計算します。たとえば、空きメモリが 500M あり、そこから他の用途のために確保しておく 200M を差し引くと、uwsgi に使える 300M が残ります。プログラムを1回実行するたびに 30M のメモリを消費するなら、processes は 10 に設定できます。