Skip to content

Instantly share code, notes, and snippets.

@lericson
Created February 28, 2011 15:45
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lericson/847486 to your computer and use it in GitHub Desktop.
Save lericson/847486 to your computer and use it in GitHub Desktop.
Gunicorn init script for Gentoo that checks liveliness properly
#!/sbin/runscript
[[ -z "${PIDFILE}" ]] && PIDFILE="/var/run/gunicorn/${SVCNAME}.pid"
[[ -z "${BINARY}" ]] && BINARY="/usr/bin/gunicorn"
depend() {
need net
use dns logger netmount
}
check_params() {
if [[ "${SVCNAME}" == "gunicorn" && -z "${I_KNOW}" ]]; then
ewarn "It is highly recommended to use a symbolic link for this"
ewarn "script and start via that instead. This allows you to run"
ewarn "multiple spawn services simultaneously. To do this, simply:"
ewarn
ewarn " ln -s /etc/init.d/gunicorn /etc/init.d/gunicorn.mysvc"
ewarn " cp /etc/conf.d/gunicorn /etc/conf.d/gunicorn.mysvc"
ewarn
ewarn "If you don't want to be bothered by this message, set I_KNOW=yes"
ewarn "in your configuration file."
ewarn
fi
if [[ -z "${WSGI_APP}" ]] && [[ -z "${WSGI_TYPE}" ]]; then
eerror "${WSGI_APP} not set. Exiting"
return 1
fi
if [[ -z "${PIDFILE}" ]]; then
eerror "PIDFILE must be set in /etc/conf.d/${SVCNAME}"
return 1
fi
}
test_pidfile() {
if [[ ! -f "${PIDFILE}" ]]; then
eerror "${PIDFILE} does not exist"
return 1
fi
read -r PID <"${PIDFILE}"
if [[ -z "${PID}" ]]; then
eerror "${PIDFILE} contains no PID"
return 1
fi
if [[ ! -d "/proc/${PID}" ]]; then
eerror "${PID} does not appear to be an existing process"
return 1
fi
if awk -F: 'BEGIN { found = 0; } END { exit(found); }; $1 == "State" && $2 !~ /^[[:space:]]*Z / { found = 1; };' "/proc/${PID}/status"; then
eerror "${PID} does not appear to be an active process"
return 1
fi
return 0
}
kill_pidfile() {
check_params && test_pidfile || return 1
ebegin "$1"
shift
read -r PID <"${PIDFILE}"
kill "$@" "${PID}"
local RC=$?
eend $RC
return $RC
}
# actions
start() {
check_params || return 1
local -a OPTS
OPTS=( "-c" "${CONFIG}"
"--daemon"
"--pid=${PIDFILE}"
"${WSGI_APP}" )
ebegin "Starting ${SVCNAME} using ${CONFIG}"
env PYTHONPATH="${PYTHONPATH}" "${BINARY}" "${OPTS[@]}"
local RC=$?
eend "${RC}"
if [[ "${RC}" == 0 ]]; then
ebegin "No early errors, checking liveliness"
sleep 1
if ! test_pidfile; then
eend 1
return 1
fi
eend 0
fi
return "${RC}"
}
status() {
check_params && test_pidfile
}
stop() {
kill_pidfile "Stopping (gracefully) ${SVCNAME}" \
-s SIGQUIT
}
force_stop() {
kill_pidfile "Stopping (disregarding jobs) ${SVCNAME}" \
-s SIGINT
}
reload() {
kill_pidfile "Telling ${SVCNAME} to reload itself" \
-s SIGHUP
}
opts="${opts} status reload force_stop"
@lericson
Copy link
Author

How to use

  1. Save as /etc/init.d/gunicorn

  2. Create a basic configuration at /etc/conf.d/gunicorn that says:

    WSGI_APP=myapp:foo
    CONFIG=/etc/gunicorn/some-service-config.py
    #PYTHONPATH=extra_path
    #PIDFILE=pidfile_override
    
  3. Do as advised and create a symlink at /etc/init.d/gunicorn.mysvc -> gunicorn and copy the configuration template.

  4. Edit configuration, done.

Why would you use

Most init scripts suffer from the ailment that they don't actually know if the
daemon they control is living or not. This is a fundamental flaw, and the way
things are handled there is really no way to ascertain liveliness of Python
daemons because they run as the process "python", and start-stop-daemon
relies on pidof and other dumb trickery. (Feel free to tell me off if you
know of a good way to do this with start-stop-daemon.)

This script takes the stance that checking /proc/$pid/status isn't actually
hard and can be done on a per-script basis. This yields the following desirable
behavior:

mysvc $ sudo /etc/init.d/gunicorn.mysvc start
 * Starting gunicorn.mysvc using /etc/gunicorn/mysvc.conf.py ...         [ ok ]
 * No early errors, checking liveliness ...                              [ ok ]
mysvc $ sudo /etc/init.d/gunicorn.mysvc status
 * status:  started
mysvc $ sudo kill $(cat /var/run/gunicorn/gunicorn.mysvc.pid)
mysvc $ sudo /etc/init.d/gunicorn.mysvc status
 * status:  started
 * /var/run/gunicorn/gunicorn.mysvc.pid does not exist

The init system is still a little dumb--it thinks the service is started even
though the status function returns non-zero--the script tells you something is
amiss. In this case the correct solution is to zap and start the service
again, since stop will fail due to there being no pidfile.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment