Controlling a TurboGears application with supervisor

Add new comment
Your name - will be displayed with your comment. Required
Your email address - will not be displayed. Required
Your homepage URL - your name will link to this. Optional
Your comment - textile syntax, 1000 chars max. Required

Since my shiny, new TurboGears application (yes, you're looking at it!) has progressed so far as to be actually usable, I wanted to deploy it in a "production" enviroment, i.e. with automatic startup at server boot time, restarting when it crashes and the ability to monitor it remotely. I googled a bit for the right tools and found supervisor. This post reports my endeavours in setting it up on my Ubuntu box, which was successfull in the end, but not before some struggling.

Note: These instructions refer to version 1.0.7 of supervisor. I only discovered the existence of a new version of supervisor, supervisor2 (at version 2.1b1 at the time of this writing), after I set up and tested supervisor 1.x :-( I will probably test supervisor2 later and write about my experiences with it.

Update 2006-12-10: I now tested supervisor2 as well and have written a short instruction on how to set it up. Read more here ...

Installation

I downloaded supervisor-1.0.7.tgz, unpacked & cd'd into the created directory and ran ./configure --prefix=/usr/local, then make, then sudo make install.

First hurdle: configure complains about an unsuitable Python version. It wants 2.4.2, but I've got 2.4.3. Hmmm, this check should be updated or be a tad more intelligent. Giving it a --with-python=/usr/bin/python option fixes this.

Grmph, make install, actually runs python setup.py with the --home option, which conflicts with the prefix option in my ~/.pydistutils.cfg. Ok, moved that out of the way and installation now works. Still, it should really use --prefix instead of --home.

Configuration

Ok, now for the configuration. Config files for the supervisord and supervisorctl programs get dropped in /usr/local/etc/supervisor. supervisord.conf contains paths to some run-time files, e.g. the Unix domain socket, used by supervisorctl to talk to the supervisor daemon, the pid and logfile, and to a password file. By default, the latter is located next to the config files (sensible) and the others directly in /usr/local/var (not so well-behaving).

If you want to run supervisord as a non-root user, you either have to set permissions on these files/directories accordingly or change the paths in the config file. Even if you keep the run-time files under /usr/local/var, it would probably be a good idea to create a directory /usr/local/var/supervisor and change the paths for the socket, the pid file and the log file in supervisord.conf, so that they will be placed under this directory.

Setting up a service is really easy, just copy-and past a sample <program> section from the ones provided in the default config file, uncomment and adapt settings as needed. The settings have fairly self-explanatory names, if unsure, look into /usr/local/share/supervisor-1.0.6/supervisor/schema.xml (yes, the path of the library directory contains an out-of-date version number!) for the documentation of all settings. Make sure that you give a unique name to each service, or supervisord will refuse to start.

Testing

Now, run supervisord (either as root (but not via sudo - see below) or, if you set up file permissions correctly, as a normal user) and it should immediately go into daemon mode and you will get your shell prompt back.

To start and stop your services or query their status, you will use the supervisorctl programm. You have to tell it how to connect to the supervisor daemon. You can either specify the socket file with the -s commandline option or set the socket option in /usr/local/etc/supervisor/supervisorctl.conf. Normally, you should be able to use the -c command line option, to specify the location of a custom configuration file, but when I use this option, I always get this error:

Error: conflicting command line option '-c'
For help, use /usr/local/share/supervisor-1.0.6/supervisor/supervisorctl.py -h

I haven't been able to figure out how to fix this :-( IMHO, supervisorctl should look for a per-user configuration file anyway, e.g. ~/.supervisorctl.conf or something like that, but apparantly it doesn't.

Apart from these inconveniences, working with supervisorctl is really easy. You get a command prompt and then start your services with start servicename and stop them with (surprise, surprise!) stop servicename. To see, which services are currently running and which are stopped, use the status command. You can also type help to see what other commands are available and get a description of each.

Setting up a TurboGears application

My first tests with a simple shell script as a test service worked flawlessly, but when I tried to set up a TurboGears application as a service under supervisor control, I ran into a few problems:

  1. supervisord has no option to set the working directory of a service, but the script to start the CherryPy server, created when you quickstart a TurboGears application, expects to be run from within the project directory or it will not find all kinds of files. I solved this by creating a tiny wrapper shell script (just named run) in the TurboGears project directory:

    #!/bin/sh
    
    cd "$(dirname $0)"
    exec ./start-myproject.py ${1:-prod.cfg}
    
  2. Running this wrapper shell script from the command line worked ok, but when I tried to start the service with supervisorctl, it always died immediately and the myproject-supervisor.log file showed a traceback with an error message like this:

    Missing or wrong argument to FileHandler in handler error_out
    -> [Errno 2] No such file or directory: 'log/error.log'
    

This puzzled me exceedingly, until I remembered that I had started supervisord via sudo, which sometimes has strange effects on the effective UID and the process environment. When I killed supervisord and started it again with su - supervisord, everything worked fine and the TurboGears application server was started without errors.

And this is how the service definition in supervisord.conf looks like:

<program cblog>
    command /home/foo/share/tg_apps/MyProject/run
    auto-start true
    auto-restart true
    user foo
    logfile /usr/local/var/supervisor/myproject-supervisor.log
</program>

Automatically starting supervisord at boot time

Unfortunately, the supervisor distribution contains no SysV init script to start supervisord at boot time, probably because these scripts are too distribution-dependent. So I had to write one myself. Luckily Debian/Ubuntu provides a template for custom init scripts in /etc/init.d/skeleton. Here's the init script I wrote, which should work on any Debian or Ubuntu system. It should be copied to /etc/init.d/supervisor (mode 0755) and then installed with (as root):

update-rc.d supervisor defaults

Download the init script: supervisor.init

There remains one last thing to do: create a file /etc/defaults/supervisor and put the following in it:

# Start the supervisor daemon at boot time ("yes" or "no")
START_SUPERVISOR="yes"

You can now reboot your system or just run:

invoke-rc.d supervisor start

as root and supervisord should start and with it your TurboGears application server. Use supervisorctl to check if everything is running as it should.

Conclusion

The installation procedure of supervisor provided some pitfalls and the default setup has some inconsisties. Probably the 1.0.x version does not receive the same TLC anymore as the 2.x version. It would have been nice, if the webpage for 1.0.x sported a prominent hint that there exists a successor program.

After one has mastered the setup, which, to be honest, requires more manual fiddling than deserves the label "user-friendly", it works really well. and I like that you can give users the ability to check and start/stop services on a remote host without the need to give them a full-blown shell access.

Finally, here's a roundup of the (completely subjective) Pros and Cons of using supervisor in a usage scenario similar to mine.

Pros

  • Easy configuration syntax (~6-8 lines per service)
  • Support for specifying order in which to start services
  • Nice interactive control program (supervisorctl)
  • Remote control of supervisord over TCP sockets
  • Python script, no need to compile, i.e. you can install it on machines with different architecture, even if you don't have access to a compiler
  • Once you have set up everything, it works very smooth

Cons

  • No Debian package
  • Python script, potentially memory intensive
  • Designed to run as root, you can run as normal user, but you have to set up paths and file permissions accordingly yourself
  • Documentation somwehat hidden in the library directory (but you can also find good howtos on the net)
  • supervisorctl has no standard per-user config file (and the -c command line option does not seem to work)
  • No SysV init script for supervisord provided
  • Installation a bit rough around the edges
  • no option to set the working directory of a service program (can be fixed with a small script wrapper)

Reader comments

There are 2 comments on this article. Add a comment now...

about having to chdir to run the start-project.py, if you deploy as an egg you do not have to do this step. the script in the /usr/bin dir handles it all

Hi Ryan, yes, you’re probably right, but

  1. I have not tested deploying my app as an egg yet (on my TODO list).
  2. My start script has some modifications (e.g. write a PID file in the same directory as the start script.)
  3. What if you want to use %(current_dir_uri) in your config?

Chris