Managing Your Processes with ProcLaunch.

Edit 2010-08-08: ProcLaunch now has a CPAN-compatible install process. See below for details.

I finally got the chance to work some more on proclaunch, my implementation of a user space process manager, like runit or mongrel or god. I wrote up a big overview of the currently available options [previously][12], but in summary: all of the existing options suck. They're either hard to setup, have memory leaks, have a weird configuration language, or are just plain strange. The only viable option was procer, and even that was just sort of a tech demo put together for the Mongrel2 manual.

That's why I started putting together proclaunch. I need some of the features of runit, namely automatic restart, with none of the wackyness, and I wanted it to be easy to automatically configure. I also wanted it to be standalone so I wouldn't have to install a pre-alpha version of Mongrel2 just to manage my own processes.

What of it?

Grab the latest version off of github, unpack it, and run this in the unpacked directory:

$ perl Build.PL
$ ./Build
$ ./Build install

If everything went smoothly you'll have proclaunch somewhere in your path. Now, fire it up:

$ mkdir -p /path/to/some/state/directory
$ sudo proclaunch \
    --debug \
    --foreground \
    /path/to/some/state/directory \
    example_profiles/

If everything goes according to plan, you'll see a bunch of debug info scroll past showing that it scanned the profiles directory, found one called sleeper, and kicked it off. Then, every five seconds you'll see it rescan. If you look in your process list for sleep you'll see bash happily kicking off a sleep 10 in an infinite loop as the nobody user. Now, run this:

$ sudo kill `cat /path/to/some/state/directory/proclaunch.pid`

You should still see the sleep going on, but proclaunch shouldn't show up anywhere. If you launch proclaunch again, you'll see it startup but never start sleeper, since it's already running. This may seem really mundane, but you can't make runit behave this way without some major hacks. Oh, and to actually make proclaunch kill everything before dying, kill it with -HUP:

$ sudo kill -HUP `cat /path/to/some/state/directory/proclaunch.pid`

Now for the automatic restart. Change something about the profiles directory:

$ touch example_profiles/

In the log you should see that proclaunch saw something changed and rescanned immediately. Now change something about sleeper:

$ touch example_profiles/sleeper

Within a few seconds, proclaunch will notice that something happened and restart sleeper. Specifically, it will send sleeper's pid a SIGTERM, wait up to 7 seconds for it to actually die, and then send it a SIGKILL. Now something a little more drastic:

$ sudo mv example_profiles/sleeper example_profiles/sleeper2

proclaunch will notice that sleeper is gone, tell it to stop, then start sleeper2 since it obviously isn't running. You can use this to setup really simple deploys, especially if you're deploying with Capistrano. Just commit your profiles directory to version control and point proclaunch at that directory in the current symlink, making sure that the pid_file is within the deploy directory somewhere. Within 5 seconds of your deploy, proclaunch will see that the inode on the profiles directory changed.

What is a profile, anyway?

If you look in the sleeper directory, you'll see this set of files:

run
pid_file
user

run is a small script that proclaunch expects to execute and have it return in short order, having backgrounded itself and written it's pid to the path contained in pid_file. This forms the core of both proclaunch and procer. Really simple to setup and automate, since there isn't any complicated config file to manage. The user file is special to proclaunch, and tells it what user to start run as. By default, proclaunch will start run as root, which is generally not what you want. procer can do some fun things that proclaunch can't do yet, like manage dependencies between profiles. If there's any demand I'll work on adding that but I don't currently need it.

A small digression into Mac OS X

Initially I wanted to use Privilege::Drop from CPAN to drop privileges when spawning profiles. It's a really clean pure perl module that has no dependencies other than perl itself. It even does a bunch of sanity checking to ensure that the privileges you dropped to are specifically what you wanted to drop to. However, on OS X with perl 5.10, it seems that you can't drop a large number of auxiliary groups that Privilege::Drop doesn't know about, at least not in the way that it's currently written. That's why the code for dropping privileges is inlined in App::ProcLaunch::Profile. It still checks to make sure that the group you tried to drop to is in the list, but it doesn't assert the list matches exactly what you wanted to do.