nginxとgunicornとsupervisorを連携させる
私はVPSと自宅LAN内でDjangoで作った個人的なwebサービスをいくつか運用しています。
現在の運用環境はapache+mod_wsgiですが、ネットで色々調べていると、nginxとgunicornとsupervisorを組み合わせるのが旬(?)のようなので、その方法について色々調べたことを書きたいと思います。
具体的には、nginxがリバースプロキシ兼Webサーバとして、キャッシュと静的ファイルの配信を担当し、gunicornがバックエンドとして動的なページ生成を担当し、supervisorがサーバプロセスの監視を行う、という構成です。
環境としては、CentOS 5.X を想定しています。
nginxのインストール
CentOSにnginxをイントールする方法はこちらのページに詳しく書かれているので、それを参考にしました。configureの引数のうち、追加した方が良さそうなものがあったので、それだけ掲載しておきます。
./configure \ --prefix=/usr/local \ --conf-path=/etc/nginx/nginx.conf \ --error-log-path=/var/log/nginx/error.log \ --pid-path=/var/run/nginx/nginx.pid \ --lock-path=/var/lock/nginx.lock \ --user=nginx \ --group=nginx \ --with-http_stub_status_module \ --with-http_ssl_module \ --with-http_gzip_static_module \ --http-log-path=/var/log/nginx/access.log \ --http-client-body-temp-path=/var/tmp/nginx/client/ \ --http-proxy-temp-path=/var/tmp/nginx/proxy/ \ --http-fastcgi-temp-path=/var/tmp/nginx/fcgi/ \ --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi/ \ --http-scgi-temp-path=/var/tmp/nginx/scgi/
gunicornのインストール
gunicornはeasy_installを使って一発でインストールできます。また、setproctitleというライブラリをインストールしておけば、psコマンドやtopコマンドで見えるプロセス名を任意の名前に設定できるようになります。
sudo easy_install gunicorn sudo easy_install setproctitle
supervisorのインストール
supervisorもeasy_installを使ってインストールすることができます。
sudo easy_install supervisor
supervisorをインストールするとecho_supervisord_confというコマンドが使用できるようになります。
このコマンドを実行すると、supervisorの設定ファイルの雛形が標準出力に出力されるので、リダイレクトして保存し、編集した後、/etc/supervisord.confに保存します。
echo_supervisord_conf > supervisord_conf
vim supervisord_conf
sudo cp supervisord_conf /etc/supervisord_conf
ついでに、supervisord自体の起動スクリプトを見よう見まねで作成したので、掲載しておきます。
#!/bin/sh # # supervisord - this script starts and stops the supervisord daemon # # chkconfig: - 90 10 # description: Supervisor is a client/server system that allows \ # its users to monitor and control a number of \ # processes on UNIX-like operating systems. # processname: supervisord # config: /etc/supervisord.conf # pidfile: /tmp/supervisord.pid # Source function library. . /etc/init.d/functions # Source networking configuration. . /etc/sysconfig/network # Check that networking is up. [ "$NETWORKING" = "no" ] && exit 0 RETVAL=0 supervisord="/usr/local/bin/supervisord" prog=$(basename $supervisord) pidfile=/tmp/supervisord.pid lockfile=/var/lock/subsys/supervisord start () { echo -n $"Starting $prog: " daemon $supervisord --pidfile $pidfile RETVAL=$? echo [ $RETVAL -eq 0 ] && touch $lockfile return $RETVAL } stop () { echo -n $"Stopping $prog: " killproc -p $pidfile $supervisord -QUIT RETVAL=$? echo [ $RETVAL -eq 0 ] && rm -f $lockfile return $RETVAL } restart () { stop sleep 1 start } reload () { echo -n $"Reloading $prog: " killproc -p $pidfile $supervisord -HUP RETVAL=$? echo } case "$1" in start) start ;; stop) stop ;; reload) reload ;; restart) restart ;; status) status -p ${pidfile} supervisord RETVAL=$? ;; *) echo $"Usage: $0 {start|stop|status|restart|reload}" RETVAL=2 ;; esac exit $RETVAL
このファイルを/etc/rc.d/init.d/supervisordに保存して、以下のコマンドを実行すると、マシン起動時にsupervisordが自動的に起動されます。
sudo chmod 755 /etc/rc.d/init.d/supervisord sudo /sbin/chkconfig --add supervisord sudo /sbin/chkconfig supervisord on
gunicornの設定
gunicornの設定例を以下に示します。(memoはアプリケーション名)
bind = 'unix:/tmp/gunicorn_memo.sock' backlog = 2048 workers = 1 worker_class = 'sync' worker_connections = 1000 max_requests = 0 timeout = 30 keepalive = 2 debug = False spew = False preload_app = True daemon = False pidfile = '/var/run/gunicorn/memo.pid' # /var/run/gunicornを作成しておく user = 'memo_app' group = 'nginx' umask = 0002 # tmp_upload_dir = None logfile = '/var/log/gunicorn/memo.log' # /var/log/gunicornを作成しておく loglevel = 'info' logconfig = None proc_name = gunicorn_memo'
ワーカーの数は個人的なサービスを考えているので1としていますが、経験的にはCPUコア数×2+1が良いようです。nginxとsupervisorとの連携を考える上では、以下の点がポイントです。
supervisorの設定
supervisorはHTTPサーバ機能を持っており、ウェブブラウザを介してプロセスの状態確認や起動/停止等を行うためのインターフェースが用意されています。
これをnginxを介して利用できるようにしたいと思います。
supervisorのHTTPサーバにははUNIXドメインソケットを使ったサーバとINETドメインソケットを使ったサーバの二種類ありますが、UNIXドメインソケットを使う方はnginxとの連携がうまくいかなかったので、INETドメインソケットを使ったサーバを使用します。
unix_http_serverのセクションをコメントアウトして、inet_http_serverを有効化します(パスワードは適宜設定します)。
;[unix_http_server] ;file=/tmp/supervisor.sock ; (the path to the socket file) ;chmod=0700 ; sockef file mode (default 0700) ;chown=nobody:nobody ; socket file uid:gid owner ;username=user ; (default is no username (open server)) ;password=123 ; (default is no password (open server)) [inet_http_server] ; inet (TCP) server disabled by default port=127.0.0.1:9001 ; (ip_address:port specifier, *:port for all iface) ;username=user ; (default is no username (open server)) ;password=123 ; (default is no password (open server))
supervisorctlのセクションも合わせて変更します。
[supervisorctl] ;serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket
supervisorを使ってgunicornプロセスを起動するための設定例
[program:gunicorn_memo] command=/usr/local/bin/gunicorn_django --config /opt/www/memo/gunicorn_conf.py /opt/www/memo/settings.py directory=/opt/www/memo user=root autostart=true autorestart=true redirect_stderr=true environment=PYTHON_EGG_CACHE=/opt/www/memo/.python-eggs
nginxの設定
supervisorのバーチャルホストの設定
upstream supervisor-backend { server 127.0.0.1:9001 fail_timeout=0; } server { listen 80; server_name supervisor.local.saitodev.com; access_log /var/log/nginx/supervisor.local.saitodev.com-access.log main; error_log /var/log/nginx/supervisor.local.saitodev.com-error.log info; location / { allow 127.0.0.1; allow 192.168.0.0/24; deny all; proxy_set_header Host $host; proxy_pass http://supervisor-backend; break; } }
proxy_set_headerの行が無いと、リダイレクト処理で失敗します。
gunicornのバーチャルホストの設定
upstream memo-backend {
server unix:/tmp/gunicorn_memo.sock fail_timeout=0;
}
server {
listen 80;
server_name memo.local.saitodev.com;
access_log /var/log/nginx/memo.local.saitodev.com-access.log main;
error_log /var/log/nginx/memo.local.saitodev.com-error.log info;
# djangoのadminの静的ファイルを配信する場合に必要
location ~ /media/(.*)$ {
alias /usr/local/lib/python2.7/site-packages/django/contrib/admin/media/$1;
break;
}
location / {
proxy_set_header Host $host;
proxy_pass http://memo-backend;
break;
}
}
最後にコメント
今回の例では、nginxとgunicornを別ユーザで動かそうとしたため、結果的にsupervisordとgunicornのmasterがrootで動いてしまっています。
実際の運用では、nginxとgunicornとsupervisordを全て同一の非rootユーザで動かした方がセキュリティ的にましな気がしてきました。