Howto: running Zine with Nginx and WSGI server
This article will look at running Zine with Nginx, Cogen and Supervisor on Ubuntu server.
Zine
Zine is an open-source blog engine written in Python.
Install dependencies:
$ sudo aptitude install python2.5-dev libxml2-dev libxslt-dev $ sudo easy_install Werkzeug Jinja2 MySQL-python SQLAlchemy simplejson pytz Babel lxml html5lib
Download Zine sources:
$ mkdir ~/temp && cd ~/temp $ hg clone http://dev.pocoo.org/hg/zine-main zine $ cd zine $ ./configure --prefix=/usr/local $ checkinstall --pkgname="zine" --pkgversion=0.2-dev --maintainer="foo@bar.com" --provides="zine" --strip=yes --stripso=yes --backup=no -y
Create your blog folder and folder for blog media (images, files, …):
$ mkdir ~/zine $ mkdir ~/zine/uploads
You can put favicon.ico and robots.txt files to ~/zine too.
Nginx
Nginx is a high perfomance and light weight HTTP and reverse proxy server.
Install dependencies:
$ sudo aptitude install libc6 libpcre3 libpcre3-dev libpcrecpp0 libssl0.9.8 libssl-dev zlib1g zlib1g-dev lsb-base
Download Nginx and install it:
$ cd ~/temp $ wget http://sysoev.ru/nginx/nginx-0.7.42.tar.gz && tar zxvf nginx-0.7.42.tar.gz $ cd nginx-0.7.42 $ ./configure --with-http_ssl_module && make $ checkinstall --pkgname="nginx" --pkgversion=0.7.42 --maintainer="foo@bar.com" --provides="nginx" --strip=yes --stripso=yes --backup=no -y
Create init script for nginx daemon:
$ sudo touch /etc/init.d/nginx $ sudo chmod +x /etc/init.d/nginx $ sudo update-rc.d nginx defaults
Content of /etc/init.d/nginx:
#! /bin/sh ### BEGIN INIT INFO # Provides: nginx # Required-Start: $all # Required-Stop: $all # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: starts the nginx web server # Description: starts nginx using start-stop-daemon ### END INIT INFO PATH=/usr/local/nginx/sbin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin DAEMON=/usr/local/nginx/nginx PID=/usr/local/nginx/nginx.pid NAME=nginx DESC=nginx test -x $DAEMON || exit 0 # Include nginx defaults if available if [ -f /etc/default/nginx ] ; then . /etc/default/nginx fi set -e case "$1" in start) echo -n "Starting $DESC: " start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON -- $DAEMON_OPTS echo "$NAME." ;; stop) echo -n "Stopping $DESC: " start-stop-daemon --stop --quiet --pidfile $PID --exec $DAEMON -- $DAEMON_OPTS echo "$NAME." ;; restart|force-reload) echo -n "Restarting $DESC: " start-stop-daemon --stop --quiet --pidfile $PID --exec $DAEMON -- $DAEMON_OPTS sleep 1 start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON -- $DAEMON_OPTS echo "$NAME." ;; reload) echo -n "Reloading $DESC configuration: " start-stop-daemon --stop --signal HUP --quiet --pidfile $PID --exec $DAEMON -- $DAEMON_OPTS echo "$NAME." ;; *) N=/etc/init.d/$NAME echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2 exit 1 ;; esac exit 0
Change Nginx configuration /usr/local/nginx/nginx.conf:
user www-data; worker_processes 1; error_log logs/error.log warn; pid nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] $request ' '"$status" $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log logs/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; server_names_hash_bucket_size 128; keepalive_timeout 75 20; gzip on; gzip_min_length 1100; gzip_buffers 4 32k; gzip_comp_level 2; gzip_proxied any; gzip_types text/plain text/html text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript; include sites/yourblogname.com; }
Create /usr/local/nginx/sites/yourblogname.com:
server { listen 80; server_name www.yourblogname.com; rewrite ^/(.*)$ http://yourblogname.com/$1 redirect; } server { listen 80; server_name yourblogname.com; error_log logs/yourblogname_error.log warn; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header X-Forwarded-Host $server_name; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; access_log logs/yourblogname_access.log main; } location = /favicon.ico { root /home/username/zine; access_log off; expires 30d; } location = /robots.txt { root /home/username/zine; access_log off; expires 30d; } location ~ ^/_shared/(akismet_spam_filter|core|dark_vessel_colorscheme|eric_the_fish|miniblog_theme|myrtle_theme|vessel_theme)/(.+\.(?:jpe?g|gif|png|ico|bmp|js|css|gz|swf))$ { alias /usr/local/share/zine/htdocs/$1/$2; expires 30d; access_log off; } location ~ ^/_shared/(?!(pygments_support|creole_parser|markdown_parser|typography))([^/]+)/(.+\.(?:jpe?g|gif|png|ico|bmp|js|css|gz|swf))$ { alias /home/username/zine/plugins/$2/shared/$3; expires 30d; access_log off; } location /uploads/ { root /home/username/zine; expires 30d; access_log off; autoindex off; } location /admin { rewrite ^/(.*)$ https://yourblogname.com/$1 redirect; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } error_page 403 /403.html; location = /403.html { root html; } } server { listen 443; server_name www.yourblogname.com; rewrite ^/(.*)$ https://yourblogname.com/$1 redirect; } server { listen 443; server_name yourblogname.com; error_log logs/yourblogname_admin_error.log warn; ssl on; ssl_certificate /etc/ssl/certs/some_ssl_cert.crt; ssl_certificate_key /etc/ssl/private/some_ssl_cert.key; ssl_session_timeout 5m; ssl_protocols SSLv2 SSLv3 TLSv1; ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP; ssl_prefer_server_ciphers on; add_header Front-End-Https on; location / { rewrite ^/(.*)$ http://yourblogname.com/$1 permanent; } location /admin { proxy_pass http://127.0.0.1:8080; proxy_set_header X-Forwarded-Host $server_name; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; access_log logs/yourblogname_admin_access.log main; } location = /favicon.ico { root /home/username/zine; access_log off; expires 30d; } location ~ ^/_shared/(akismet_spam_filter|core|dark_vessel_colorscheme|eric_the_fish|miniblog_theme|myrtle_theme|vessel_theme)/(.+\.(?:jpe?g|gif|png|ico|bmp|js|css|gz|swf))$ { alias /usr/local/share/zine/htdocs/$1/$2; expires 30d; access_log off; } location ~ ^/_shared/(?!(pygments_support|creole_parser|markdown_parser|typography))([^/]+)/(.+\.(?:jpe?g|gif|png|ico|bmp|js|css|gz|swf))$ { alias /home/username/zine/plugins/$2/shared/$3; expires 30d; access_log off; } location /uploads/ { root /home/username/zine; expires 30d; access_log off; autoindex off; } error_page 403 /403.html; location = /403.html { root html; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } }
Cogen
Cogen is a crossplatform library for network oriented, coroutine based programming. We will use it as WSGI server.
$ sudo easy_install cogen
Put to the blog folder running script for Zine:
$ touch run_zine.py $ chmod +x run_zine.py
Content of run_zine.py:
#!/usr/bin/env python # -*- coding: utf-8 -*- INSTANCE_FOLDER = '/home/username/zine' PLUGINS_FOLDER = os.path.join(INSTANCE_FOLDER, 'plugins/') ZINE_LIB = '/usr/local/lib/zine' POOL_SIZE = None POOL_RECYCLE = None POOL_TIMEOUT = None BEHIND_PROXY = None import sys import os if ZINE_LIB not in sys.path: sys.path.insert(0, ZINE_LIB) if PLUGINS_FOLDER not in sys.path: sys.path.append(PLUGINS_FOLDER) from zine import get_wsgi_app, override_environ_config override_environ_config(POOL_SIZE, POOL_RECYCLE, POOL_TIMEOUT, BEHIND_PROXY) application = get_wsgi_app(INSTANCE_FOLDER) from cogen.web import wsgi from cogen.web.async import sync_input from cogen.common import * m = Scheduler(default_priority=priority.LAST, default_timeout=15) server = wsgi.WSGIServer( ('localhost', 8080), sync_input(application), m, server_name='localhost') m.add(server.serve) try: m.run() except (KeyboardInterrupt, SystemExit): pass
Supervisor
Supervisor is a client/server system that allows to monitor and control a number of processes. If you run multiple sites on your server Supervisor is very useful for manage them.
Install Supervisor and create init script:
$ sudo easy_install supervisor $ sudo echo_supervisord_conf > /etc/supervisord.conf $ sudo touch /etc/init.d/supervisord $ sudo chmod +x /etc/init.d/supervisord $ sudo update-rc.d supervisord defaults
Content of /etc/init.d/supervisord:
#!/bin/sh ### BEGIN INIT INFO # Provides: supervisor # Default-Start: 2 3 4 5 # Default-Stop: S 0 1 6 # Short-Description: Starts/stops the supervisor daemon # Description: This starts and stops the supervisor dameon # which is used to run and monitor arbitrary programs as # services, e.g. application servers etc. ### END INIT INFO PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin DESC="supervisor daemon" NAME="supervisor" DAEMON="/usr/bin/${NAME}d" SUPERVISORCTL="/usr/bin/${NAME}ctl" PIDFILE="/var/run/${NAME}d.pid" SCRIPTNAME="/etc/init.d/$NAME" CONFFILE="/etc/${NAME}d.conf" test -x "$DAEMON" || exit 0 test -r "$CONFFILE" || exit 0 if [ -r "/etc/default/$NAME" ]; then . "/etc/default/$NAME" fi set -e d_start() { start-stop-daemon --start --quiet --pidfile "$PIDFILE" \ --exec "$DAEMON" \ || echo -n " already running" } d_stop() { $SUPERVISORCTL shutdown } d_reload() { $SUPERVISORCTL reload } case "$1" in start) echo -n "Starting $DESC: $NAME" d_start echo "." ;; stop) echo -n "Stopping $DESC: $NAME" d_stop echo "." ;; reload|force-reload) echo -n "Reloading $DESC configuration..." d_reload echo "done." ;; restart) echo -n "Restarting $DESC: $NAME" d_stop sleep 1 d_start echo "." ;; *) echo "Usage: "$SCRIPTNAME" {start|stop|restart|force-reload}" >&2 exit 3 ;; esac exit 0
Modify /etc/supervisord.conf and add program section for running your Zine blog:
[program:zine] command=/home/username/zine/run_zine.py user=username
Final
Now you can launch your blog:
$ sudo /etc/init.d/nginx start $ sudo /etc/init.d/supervisord start
Hi, good post. I have been wondering about this issue,so thanks for posting. I’ll definitely be coming back
Comment by Bill Brown jr — Jun 3, 2009 10:58:34 PM | # - re
Very useful. Many thanks for sharing.
Comment by Shekhar — Aug 14, 2009 11:20:29 PM | # - re