From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-4.0 required=3.0 tests=ALL_TRUSTED,BAYES_00 shortcircuit=no autolearn=ham autolearn_force=no version=3.4.0 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id 528452070F for ; Thu, 7 Jul 2016 01:22:37 +0000 (UTC) From: Eric Wong To: yahns-public@yhbt.net Subject: [PATCH] update init and add systemd examples Date: Thu, 7 Jul 2016 01:22:37 +0000 Message-Id: <20160707012237.6120-1-e@80x24.org> List-Id: Reduce raciness in the init script and add LSB tags. However, the systemd examples should be race-free and safer (if one feels safe using systemd :P) --- examples/init.sh | 43 +++++++++++++++++++++++++++++++++--------- examples/logrotate.conf | 5 +++++ examples/yahns.socket | 17 +++++++++++++++++ examples/yahns@.service | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 9 deletions(-) create mode 100644 examples/yahns.socket create mode 100644 examples/yahns@.service diff --git a/examples/init.sh b/examples/init.sh index 9464220..6fe1ae6 100644 --- a/examples/init.sh +++ b/examples/init.sh @@ -2,8 +2,14 @@ # To the extent possible under law, Eric Wong has waived all copyright and # related or neighboring rights to this examples set -e -# Example init script, this can be used with nginx, too, -# since nginx and yahns accept the same signals +### BEGIN INIT INFO +# Provides: yahns +# Required-Start: $local_fs $network +# Required-Stop: $local_fs $network +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Start/stop yahns Ruby app server +### END INIT INFO # Feel free to change any of the following variables for your app: TIMEOUT=${TIMEOUT-60} @@ -11,21 +17,22 @@ APP_ROOT=/home/x/my_app/current PID=$APP_ROOT/tmp/pids/yahns.pid CMD="/usr/bin/yahns -D -c $APP_ROOT/config/yahns.rb" INIT_CONF=$APP_ROOT/config/init.conf +UPGRADE_DELAY=${UPGRADE_DELAY-2} action="$1" set -u test -f "$INIT_CONF" && . $INIT_CONF -old_pid="$PID.oldbin" +OLD="$PID.oldbin" cd $APP_ROOT || exit 1 sig () { - test -s "$PID" && kill -$1 `cat $PID` + test -s "$PID" && kill -$1 $(cat $PID) } oldsig () { - test -s $old_pid && kill -$1 `cat $old_pid` + test -s "$OLD" && kill -$1 $(cat $OLD) } case $action in @@ -47,18 +54,36 @@ restart|reload) $CMD ;; upgrade) - if sig USR2 && sleep 2 && sig 0 && oldsig QUIT + if oldsig 0 + then + echo >&2 "Old upgraded process still running with $OLD" + exit 1 + fi + + cur_pid= + if test -s "$PID" + then + cur_pid=$(cat $PID) + fi + + if test -n "$cur_pid" && + kill -USR2 "$cur_pid" && + sleep $UPGRADE_DELAY && + new_pid=$(cat $PID) && + test x"$new_pid" != x"$cur_pid" && + kill -0 "$new_pid" && + kill -QUIT "$cur_pid" then n=$TIMEOUT - while test -s $old_pid && test $n -ge 0 + while kill -0 "$cur_pid" 2>/dev/null && test $n -ge 0 do printf '.' && sleep 1 && n=$(( $n - 1 )) done echo - if test $n -lt 0 && test -s $old_pid + if test $n -lt 0 && kill -0 "$cur_pid" 2>/dev/null then - echo >&2 "$old_pid still exists after $TIMEOUT seconds" + echo >&2 "$cur_pid still running after $TIMEOUT seconds" exit 1 fi exit 0 diff --git a/examples/logrotate.conf b/examples/logrotate.conf index ebc92a5..b0d1351 100644 --- a/examples/logrotate.conf +++ b/examples/logrotate.conf @@ -25,6 +25,11 @@ # config. yahns supports the USR1 signal and we send it # as our "lastaction" action: lastaction + # systemd users do not have PID files, + # only signal the @1 process since the @2 is short-lived + # and only runs while @1 is restarting. + systemctl kill -s SIGUSR1 yahns@1.service + # assuming your pid file is in /var/run/yahns_app/pid pid=/var/run/yahns_app/pid test -s $pid && kill -USR1 "$(cat $pid)" diff --git a/examples/yahns.socket b/examples/yahns.socket new file mode 100644 index 0000000..6455b41 --- /dev/null +++ b/examples/yahns.socket @@ -0,0 +1,17 @@ +# ==> /etc/systemd/system/yahns.socket <== +[Unit] +Description = yahns sockets + +[Socket] + +# yahns can handle an arbitrary number of listen sockets, +# so I prefer to keep listeners for IPv4 and IPv6 separate +# to avoid ugly IPv4-mapped-IPv6 addresses for IPv4 clients: +# (e.g ":ffff:10.0.0.1" instead of just "10.0.0.1"). +ListenStream = 0.0.0.0:443 +BindIPv6Only = ipv6-only +ListenStream = [::]:443 +Service = yahns@1.service + +[Install] +WantedBy = sockets.target diff --git a/examples/yahns@.service b/examples/yahns@.service new file mode 100644 index 0000000..1ee010f --- /dev/null +++ b/examples/yahns@.service @@ -0,0 +1,50 @@ +# ==> /etc/systemd/system/yahns@.service <== +# Since SIGUSR2 upgrades do not work under systemd, this service +# file allows starting two (or more) simultaneous services +# during upgrade (e.g. yahns@1 and yahns@2) with the intention +# that they are both running during the upgrade process. +# +# This allows upgrading without downtime, using yahns@2 as a +# temporary hot spare: +# +# systemctl start yahns@2 +# sleep 2 # wait for yahns@2 to boot, increase as necessary for big apps +# systemctl restart yahns@1 +# sleep 2 # wait for yahns@1 to warmup +# systemctl stop yahns@2 + +[Unit] +Description = yahns Ruby server %i +Wants = yahns.socket +After = yahns.socket + +[Service] +# yahns can handle lots of open files: +LimitNOFILE = 32768 +LimitCORE = infinity + +# The listen socket we give yahns should be blocking for optimal +# load distribution between processes under the Linux kernel. +# NonBlocking is false by default in systemd, but we specify it +# here anyways to discourage users from blindly changing it. +Sockets = yahns.socket +NonBlocking = false + +# bundler users must use the "--keep-file-descriptors" switch, here: +# ExecStart = /path/to/bin/bundle exec --keep-file-descriptors yahns -c ... +ExecStart = /path/to/bin/yahns -c /path/to/yahns.conf.rb +KillSignal = SIGQUIT +User = www-data +Group = www-data +ExecReload = /bin/kill -HUP $MAINPID + +# this should match the shutdown_timeout value in yahns_config(5) +TimeoutStopSec = 600 + +# Only kill the master process, it may be harmful to signal +# workers via default "control-group" setting since some +# Ruby extensions and applications misbehave on interrupts +KillMode = process + +[Install] +WantedBy = multi-user.target -- EW