Alright, this is melting my brain. It might have something to do with the fact that I don't understand Upstart as well as I should. Sorry in advance for the long question.
I'm trying to use Upstart to manage a Rails app's Unicorn master process. Here is my current /etc/init/app.conf
:
description "app"
start on runlevel [2]
stop on runlevel [016]
console owner
# expect daemon
script
APP_ROOT=/home/deploy/app
PATH=/home/deploy/.rbenv/shims:/home/deploy/.rbenv/bin:$PATH
$APP_ROOT/bin/unicorn -c $APP_ROOT/config/unicorn.rb -E production # >> /tmp/upstart.log 2>&1
end script
# respawn
That works just fine - the Unicorns start up great. What's not great is that the PID detected is not of the Unicorn master, it's of an sh
process. That in and of itself isn't so bad, either - if I wasn't using the automagical Unicorn zero-downtime deployment strategy. Because shortly after I send -USR2
to my Unicorn master, a new master spawns up, and the old one dies...and so does the sh
process. So Upstart thinks my job has died, and I can no longer restart it with restart
or stop it with stop
if I want.
I've played around with the config file, trying to add -D to the Unicorn line (like this: $APP_ROOT/bin/unicorn -c $APP_ROOT/config/unicorn.rb -E production -D
) to daemonize Unicorn, and I added the expect daemon
line, but that didn't work either. I've tried expect fork
as well. Various combinations of all of those things can cause start
and stop
to hang, and then Upstart gets really confused about the state of the job. Then I have to restart the machine to fix it.
I think Upstart is having problems detecting when/if Unicorn is forking because I'm using rbenv + the ruby-local-exec
shebang in my $APP_ROOT/bin/unicorn
script. Here it is:
#!/usr/bin/env ruby-local-exec
#
# This file was generated by Bundler.
#
# The application 'unicorn' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'pathname'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require 'rubygems'
require 'bundler/setup'
load Gem.bin_path('unicorn', 'unicorn')
Additionally, the ruby-local-exec
script looks like this:
#!/usr/bin/env bash
#
# `ruby-local-exec` is a drop-in replacement for the standard Ruby
# shebang line:
#
# #!/usr/bin/env ruby-local-exec
#
# Use it for scripts inside a project with an `.rbenv-version`
# file. When you run the scripts, they'll use the project-specified
# Ruby version, regardless of what directory they're run from. Useful
# for e.g. running project tasks in cron scripts without needing to
# `cd` into the project first.
set -e
export RBENV_DIR="${1%/*}"
exec ruby "$@"
So there's an exec
in there that I'm worried about. It fires up a Ruby process, which fires up Unicorn, which may or may not daemonize itself, which all happens from an sh
process in the first place...which makes me seriously doubt the ability of Upstart to track all of this nonsense.
Is what I'm trying to do even possible? From what I understand, the expect
stanza in Upstart can only be told (via daemon
or fork
) to expect a maximum of two forks.