Today I wanted to set up Gunicorn, a powerful webserver that will eventually run my Django application. It was not a straightforward process, so I decided to document it for my fellow geeks.
Preamble
This procedure assumes that you've already set up the following:- installed Python 3 (I tested with 3.4.1)
- created a custom
myuseraccount and logged on as such - created a virtualenv with
pyvenv /home/myuser - installed Django in the above-mentioned virtualenv
- created a Django project at
/home/myuser/myproject
Let's go!
- activate the virtualenv:
source bin/activate - install Gunicorn:
pip install gunicorn - create
/home/myuser/gunicorn_config.pywith the following contents:command = '/home/myuser/bin/gunicorn' pythonpath = '/home/myuser/myproject' chdir = '/home/myuser' pidfile = '/home/myuser/.gunicorn_myproject.pid' user = 'nobody' worker_tmp_dir = '/tmp' errorlog = '/var/log/gunicorn_myproject' # tweak the following lines to suit your setup bind = 'localhost:8001' workers = 4
You can add any further option as necessary, but it's important you keeppidfileanduser. sudoto root (I'd recommend to do that from another account, i.e. don't addmyusertowheel) and create/etc/rc.d/gunicorn_myprojectwith the following contents:#!/bin/sh daemon="/home/myuser/bin/python" daemon_flags="/home/myuser/bin/gunicorn -c /home/myuser/gunicorn_config.py -D myproject.wsgi" rc_stop() { kill `cat /home/myuser/.gunicorn_myproject.pid` } . /etc/rc.d/rc.subr rc_cmd $1Note how we are specifyingdaemonto be the python executable. This is the only way I've found to keep the rest of the machinery inrc.subrworking smoothly.chmod 555 /etc/rc.d/gunicorn_myproject- now you can start and stop Gunicorn with standard OpenBSD 5.x commands:
/etc/rc.d/gunicorn_myproject start - Typically, you'd then set nginx to proxy and cache requests, and firewall gunicorn from the outside.
root and then spawns processes as nobody; if I were to start it as myuser, it would not work. This is because gunicorn tries to change ownership of files and processes after startup, and OpenBSD doesn't seem to like it. This looks suboptimal to me, but I couldn't find a workaround.Note also how we're writing the main process PID to file, and then using it to stop that same main process. Standard
rc.subr machinery would expect me to specify a regex in the pexp variable, which would then be passed to pgrep to find the process; but this doesn't work with gunicorn because pgrep simply cannot distinguish between master and worker processes, since they appear to have the exact same command line.If you know anything else I should do to further secure this setup, please let me know in comments!
