Managing multiple Pylons apps with Supervisor

Wouldn’t it be nice if your long-running processes, such as web applications and even HTTP servers, could be managed from one place?

There are a number of tools, including Supervisor, daemontools, and runit, that will launch processes, allow them to be configured in various ways, and restart them if they stop unexpectedly. This means you only have to make sure one process is started upon system startup, and it will take care of the rest. This article concentrates on how to manage Pylons web applications using Supervisor, which is fairly easy to configure.

This is something of a whistle-stop tour, aimed at getting you started with a working configuration. Supervisor is quite flexible, and it’s a good idea to read the manual.

Assuming you have easy_install (and if you’re using Pylons you almost certainly do) but not supervisor yet, here’s how to get started. Note that Supervisor does not work on Windows, so this is a Linux/BSD/OS X/etc. tutorial.

easy_install supervisor
mkdir /apps
cd /apps
paster create -t pylons app1
paster create -t pylons app2

I don’t necessarily expect you to create /apps in the root of your filesystem. Create it where you like, but substitute your directory when I talk about /apps, which I’m using for simplicity in this article, later on.

After executing these commands, you’ll have two Pylons applications. They won’t do much, but they’ll serve as a demonstration. Use real apps instead if you like.

Configuring Supervisor

The Supervisor configuration file should be located at /etc/supervisord.conf. If you would like to place it elsewhere for deployment purposes, I strongly recommend creating a symlink at /etc/supervisord.conf as that seems to be where Supervisor will look for configuration when restarted, even if you gave it a different initial configuration file.

I’ll show you the necessary configuration for Supervisor in several parts. Here’s the first configuration block:

[supervisord]
logfile = /tmp/supervisord.log
logfile_maxbytes = 50MB
logfile_backups=10
loglevel = info
pidfile = /tmp/supervisord.pid
nodaemon = false
minfds = 1024
minprocs = 200
umask = 022
identifier = supervisor
directory = /tmp
nocleanup = true
childlogdir = /tmp
strip_ansi = false

Supervisor uses an INI-style configuration file, which is divided into sections. Each section has a name enclosed in square brackets. This section sets up some basic parameters that you can adjust if you want.

Now, let’s talk about permissions. If run as root with this configuration, Supervisor will retain root permissions. You can then configure a user for each program that Supervisor runs, and it will drop root permissions for those specific programs. This is useful if some of the programs you manage need to run as root. For example, if you manage lighttpd as well as your Pylons apps, you may need lighttpd to run as root while your apps run as a non-root user.

If you want Supervisord to drop root permissions across the board, add a user = <username> line to the [supervisord] section of the configuration file.

If you allow Supervisord to remain root, make sure the configuration file is writable only by root!

Next, let’s set up supervisorctl, which is a command-line utility that lets you see the status and output of processes and start, stop, and restart them.

[unix_http_server]
file = /tmp/supervisord.sock

[supervisorctl]
serverurl = unix:///tmp/supervisord.sock

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

With this configuration, supervisorctl will only be available from the command line on the local machine, because it’s using a file-based socket. Supervisor also supports HTTP and has a web interface, but I’m leaving it disabled for security reasons.

After saving supervisord.conf, you should be able to run supervisord, followed by supervisorctl, and end up in an interactive shell. There are no processes running, so it’s not very interesting, but you can type help for a list of options, and exit to leave.

Finally, let’s add some processes. Open that config file back up.

[program:app1]
user = <yourusername>
command = paster serve --reload /apps/app1/development.ini
environment = PYTHONPATH=/apps/app1/,PYTHON_EGG_DIR=/tmp/python-eggs/

[program:app2]
user = <yourusername>
command = paster serve --reload /apps/app2/development.ini
environment = PYTHONPATH=/apps/app2/,PYTHON_EGG_DIR=/tmp/python-eggs/

Although in this example the names of the program blocks are the same as the names of the applications, they don’t have to be. For example, if your application was named reallylongappname, your Supervisor configuration could read [program:rla], or whatever you like. The name in the program heading is what will appear in supervisorctl.

The environment can be set individually for each process. Here, the Python path is set because we’re in development and have not installed the applications as packages.

After saving supervisord.conf, either type supervisorctl update, or type supervisorctl to enter the interactive shell and type update. Either style works, so it’s a matter of whether you want to leave a shell open for convenience.

Now, type supervisorctl status, or in the interactive shell type status. You should see two items, app1 and app2, both marked RUNNING. If they instead show an error or exit condition, something went wrong.

Say app1 failed to start. The best thing to do is supervisorctl tail app1 stderr, which will show you the last log messages that app1 printed. You can also add a -f switch after tail, just as with the real tail utility, to see live updates of an application’s output.

Where to go from here

It’s up to you which programs you would like to manage using Supervisor, and which are better off being managed by your operating system’s init system of choice, such as init.d, rc.d, or launchd.

It can be useful, however, to run your HTTP server, such as lighttpd, under Supervisor. This makes it easy to restart the server without su or sudo, since the Supervisor process is running as root (if configured as above — and remember to make the configuration file writable only by root to prevent abuse).

[program:http]
command = /usr/sbin/lighttpd -D -f /etc/lighttpd.conf

(Remember that -D argument, which prevents lighttpd from daemonizing itself.)

If necessary, you can even run multiple HTTP servers on different ports, which gives you more flexibility than a system-wide startup script.