2012/08/09

Python(Django) + Nginx + uWSGI on Ubuntu のとりあえずなセットアップ


ちょっと前にubuntu12.04で DjangoのアプリをNginx使って動かしてみよう!
という機会があったので、その時の荒削りメモ。


バックエンドサーバはuWSGI
Nginx + Gunicorn という構成がどうやらメジャーらしいですが、
何もわからん状態で調査していた際に先に出会ったuWSGIを採用することにしました。
ということで、実は後はほぼuWSGIのセットアップのお話。

uWSGI 1.0 まではAPTで一発だったわけだが。。
uWSGIの 2012年08月 現在の最新版はversion1.2なのですが、
以下 ubuntu12.04(x86_64) で実行した結果。
$ > aptitude show uwsgi
Package: uwsgi
New: yes
State: not installed
Version: 1.0.3+dfsg-1ubuntu0.1
Priority: optional
Section: universe/web
Maintainer: Ubuntu Developers 
Architecture: amd64
Uncompressed Size: 128 k
Depends: uwsgi-core (= 1.0.3+dfsg-1ubuntu0.1), lsb-base, initscripts (>= 2.88dsf-13.3)
Recommends: sqlite3
Conflicts: uwsgi
Description: fast, self-healing application container server
 uWSGI presents a complete stack for networked/clustered web applications, implementing message/object passing, caching, RPC and process management. It uses the uwsgi protocol for
 all the networking/interprocess communications.

 uWSGI can be run in preforking, threaded, asynchronous/evented modes and supports various forms of green threads/coroutines (such as uGreen, Greenlet, Fiber). uWSGI provides
 several methods of configuration: via command line, via environment variables, via XML, INI, YAML configuration files, via LDAP and more.

 On top of all this, it is designed to be fully modular. This means that different plugins can be used in order to add compatibility with tons of different technology on top of the
 same core.

 This package depends on uWSGI core binary and installs:
 * init.d script for running uWSGI daemon(s) with options defined in custom user-created configuration files
 * infrastructure for running daemons (like common locations of communication sockets, logs)
Homepage: http://projects.unbit.it/uwsgi/

ってな感じでltsな1.0.x 止まりなわけで、
しかもPPA漁ってみてもそれらしいものもないわけで、
最新のuWSGIを使う場合はちゃんとコンパイルなりpip使うなりで入れる必要があります。

pip使えばインストールは結構簡単
てなわけでpipでuWSGIをインストールします。
  • まずは必要なパッケージのインストール
  • ■ laughk@ubuntu12.04
     =================================
    sudo aptitude install \
        python-pip \
        build-essential \
        python-dev
    
  • pipでuWSGIのインストール
  • ■ laughk@ubuntu12.04
     =================================
    sudo pip install uwsgi
    
以上!

ここまではホントにラク。
ただ、この段階だと純粋に uwsgi コマンドが入っただけなので、
サーバにプロセスと常駐させる場合なんかはダルいです。

ということで起動スクリプト書くよ!
uWSGIのtar玉の中とか見てもテンプレートっぽいのが無いんですな。
なので、ここはUbuntu側で用意されているテンプレート、/etc/init.d/skelton を元に作成。
差分は以下の様な感じ。
--- /etc/init.d/skeleton        2012-04-14 18:26:31.000000000 +0900
+++ /etc/init.d/uwsgi      2012-07-12 15:15:53.402515557 +0900
@@ -1,16 +1,15 @@
 #! /bin/sh
 ### BEGIN INIT INFO
-# Provides:          skeleton
+# Provides:          uwsgi
 # Required-Start:    $remote_fs $syslog
 # Required-Stop:     $remote_fs $syslog
 # Default-Start:     2 3 4 5
 # Default-Stop:      0 1 6
-# Short-Description: Example initscript
-# Description:       This file should be used to construct scripts to be
-#                    placed in /etc/init.d.
+# Short-Description: Starts the uwsgi server
+# Description:       Starts uwsgi using start-stop-daemon
 ### END INIT INFO

 # Author: Foo Bar <foobar@baz.org>
 #
 # Please remove the "Author" lines above and replace them
 # with your own name if you copy and modify this script.
@@ -18,13 +17,15 @@
 # Do NOT "set -e"

 # PATH should only include /usr/* if it runs after the mountnfs.sh script
-PATH=/sbin:/usr/sbin:/bin:/usr/bin
-DESC="Description of the service"
-NAME=daemonexecutablename
-DAEMON=/usr/sbin/$NAME
-DAEMON_ARGS="--options args"
+PATH=/usr/local/sbin:/sbin:/usr/sbin:/usr/local/bin:/bin:/usr/bin
+DESC="uWSGI Server Process"
+NAME="uwsgi-web"
+DAEMON="/usr/local/bin/uwsgi"
+UWSGI_CONF_DIR="/usr/local/uwsgi/conf"
+UWSGI_CONF_INI="django-app.ini"
+DAEMON_ARGS=" --enable-thread --ini ${UWSGI_CONF_DIR}/${UWSGI_CONF_INI}"
 PIDFILE=/var/run/$NAME.pid
 SCRIPTNAME=/etc/init.d/$NAME

 # Exit if the package is not installed
 [ -x "$DAEMON" ] || exit 0
@@ -51,7 +52,7 @@
        #   2 if daemon could not be started
        start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
                || return 1
-       start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
+       start-stop-daemon --start --background --quiet --make-pidfile --pidfile $PIDFILE --exec $DAEMON -- \
                $DAEMON_ARGS \
                || return 2
        # Add code here, if necessary, that waits for the process to be ready
@@ -69,17 +70,16 @@
        #   1 if daemon was already stopped
        #   2 if daemon could not be stopped
        #   other if a failure occurred
-       start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
-       RETVAL="$?"
-       [ "$RETVAL" = 2 ] && return 2
+       # start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
        # Wait for children to finish too if this is a daemon that forks
        # and if the daemon is only ever run from this initscript.
        # If the above conditions are not satisfied then add some other code
        # that waits for the process to drop all resources that could be
        # needed by services started subsequently.  A last resort is to
        # sleep for some time.
-       start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
-       [ "$?" = 2 ] && return 2
+       start-stop-daemon --stop --quiet --oknodo --retry=0/5/KILL/5 --pidfile $PIDFILE --exec $DAEMON
+       RETVAL="$?"
+       [ "$RETVAL" = 2 ] && return 2
        # Many daemons don't delete their pidfiles when they exit.
        rm -f $PIDFILE
        return "$RETVAL"
@@ -100,19 +100,19 @@

 case "$1" in
   start)
-       [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
+       log_daemon_msg "Starting $DESC" "$NAME"
        do_start
        case "$?" in
-               0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
-               2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+               0|1) log_end_msg 0 ;;
+               2)   log_end_msg 1 ;;
        esac
        ;;
   stop)
-       [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
+       log_daemon_msg "Stopping $DESC" "$NAME"
        do_stop
        case "$?" in
-               0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
-               2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+               0|1) log_end_msg 0 ;;
+               2)   log_end_msg 1 ;;
        esac
        ;;
   status)
オプション(DAEMON_ARGS)とかはまあ状況にあわせてね。。

ちなみに
上記のように起動スクリプトを作成する場合のオペレーションは以下のような感じ。
■ laughk@ubuntu12.04
  ================================
## プロセスは www-data ユーザーで実行させたいのでパーミッション調整
sudo chown www-data `which uwsgi`
sudo chmod +xs `which uwsgi`

## テンプレートからコピー
sudo cp /etc/init.d/skelton /etc/init.d/uwsgi

## 上記の差分になるように編集
sudo vim /etc/init.d/uwsgi

## 確認
diff -u /etc/init.d/skelton /etc/init.d/uwsgi

## デフォルト起動の設定
sudo update-rc.d uwsgi default

設定ファイルとか設置
基本的に先ほどの差分のものにあわせてやると以下の様な感じ。
  • django-app.iniの例 (詳細はぐぐってくださいませ)
    [uwsgi]
    chdir = /path/to/django-app
    pythonpath = /path/to/django-app
    env = DJANGO_SETTINGS_MODULE=django-app.settings
    module = django.core.handlers.wsgi:WSGIHandler()
    socket = 127.0.0.1:8000
    logto = /var/log/uwsgi/django-app.log
    
オペレーションは以下。
■ laughk@ubuntu12.04
  ================================
## 設定ファイル置き場の作成 & 移動
sudo mkdir -p /usr/local/uwsgi/conf
cd /usr/local/uwsgi/conf

## djangoアプリの環境にあわせて設定を作成
vim django-app.ini

## ログ置き場の作成
sudo mkdir -p /var/log/uwsgi
sudo touch /var/log/uwsgi/django-app.log

## パーミッション調整
sudo chown -R www-data {/var/log,/usr/local}/uwsgi
これでuWSGIの最低限のセットアップはOKなはず。

Nginx側の設定
NginxはPPAもあるし、aptでさくっとね。
設定ファイルは以下の様な感じで。
  • /etc/nginx/sites-available/hogehoge.com の例
    server {
        server_name hogehoge.com;
    
        access_log /var/log/nginx/hogehoge.com-access.log;
        error_log /var/log/nginx/hogehoge.com-access.log warn;
    
        location / {
            allow all;
            include uwsgi_params;
            uwsgi_pass 127.0.0.1:8000;
        }
    
        localtion /static {
            allow all;
            alias /path/to/django-app/static;
        }
    }
    
オペレーションは以下。
■ laughk@ubuntu12.04
  ==================================
## Nginxのインストール
sudo aptitude install nginx

## もろもろ設定。
cd /etc/nginx/sites-enabled
sudo rm default
sudo vim ../sites-available/hogehoge.com
sudo ln -s ../sites-available/hogehoge.com

## 設定反映
sudo service nginx configtest
sudo service nginx reload

というような感じ。

あとはuWSGIを起動
これで /path/to/django-app にデプロイしたいDjangoアプリをおいてsyndbかけるなりしてuWSGIを起動。

■ laughk@ubuntu12.04
  ==============================
sudo service uwsgi start

とりあえずサーバで動かす際はこんなものでOKなはず。