MEDIUM: mworker: Add systemd Type=notify support

This patch adds support for `Type=notify` to the systemd unit.

Supporting `Type=notify` improves both starting as well as reloading
of the unit, because systemd will be let known when the action completed.

See this quote from `systemd.service(5)`:
> Note however that reloading a daemon by sending a signal (as with the
> example line above) is usually not a good choice, because this is an
> asynchronous operation and hence not suitable to order reloads of
> multiple services against each other. It is strongly recommended to
> set ExecReload= to a command that not only triggers a configuration
> reload of the daemon, but also synchronously waits for it to complete.

By making systemd aware of a reload in progress it is able to wait until
the reload actually succeeded.

This patch introduces both a new `USE_SYSTEMD` build option which controls
including the sd-daemon library as well as a `-Ws` runtime option which
runs haproxy in master-worker mode with systemd support.

When haproxy is running in master-worker mode with systemd support it will
send status messages to systemd using `sd_notify(3)` in the following cases:

- The master process forked off the worker processes (READY=1)
- The master process entered the `mworker_reload()` function (RELOADING=1)
- The master process received the SIGUSR1 or SIGTERM signal (STOPPING=1)

Change the unit file to specify `Type=notify` and replace master-worker
mode (`-W`) with master-worker mode with systemd support (`-Ws`).

Future evolutions of this feature could include making use of the `STATUS`
feature of `sd_notify()` to send information about the number of active
connections to systemd. This would require bidirectional communication
between the master and the workers and thus is left for future work.
This commit is contained in:
Tim Duesterhus 2017-11-20 15:58:35 +01:00 committed by Willy Tarreau
parent 62dd698070
commit d6942c8297
4 changed files with 39 additions and 2 deletions

View File

@ -43,6 +43,7 @@
# USE_DEVICEATLAS : enable DeviceAtlas api.
# USE_51DEGREES : enable third party device detection library from 51Degrees
# USE_WURFL : enable WURFL detection library from Scientiamobile
# USE_SYSTEMD : enable sd_notify() support.
#
# Options can be forced by specifying "USE_xxx=1" or can be disabled by using
# "USE_xxx=" (empty string).
@ -700,6 +701,12 @@ BUILD_OPTIONS += $(call ignore_implicit,USE_WURFL)
OPTIONS_LDFLAGS += $(if $(WURFL_LIB),-L$(WURFL_LIB)) -lwurfl
endif
ifneq ($(USE_SYSTEMD),)
BUILD_OPTIONS += $(call ignore_implicit,USE_SYSTEMD)
OPTIONS_CFLAGS += -DUSE_SYSTEMD
OPTIONS_LDFLAGS += -lsystemd
endif
ifneq ($(USE_PCRE)$(USE_STATIC_PCRE)$(USE_PCRE_JIT),)
ifneq ($(USE_PCRE2)$(USE_STATIC_PCRE2)$(USE_PCRE2_JIT),)
$(error cannot compile both PCRE and PCRE2 support)

View File

@ -7,12 +7,12 @@ After=network.target
# socket if you want seamless reloads.
Environment="CONFIG=/etc/haproxy/haproxy.cfg" "PIDFILE=/run/haproxy.pid"
ExecStartPre=@SBINDIR@/haproxy -f $CONFIG -c -q
ExecStart=@SBINDIR@/haproxy -W -f $CONFIG -p $PIDFILE
ExecStart=@SBINDIR@/haproxy -Ws -f $CONFIG -p $PIDFILE
ExecReload=@SBINDIR@/haproxy -f $CONFIG -c -q
ExecReload=/bin/kill -USR2 $MAINPID
KillMode=mixed
Restart=always
Type=forking
Type=notify
[Install]
WantedBy=multi-user.target

View File

@ -66,6 +66,7 @@
#define GTUNE_SOCKET_TRANSFER (1<<8)
#define GTUNE_EXIT_ONFAILURE (1<<9)
#define GTUNE_USE_SYSTEMD (1<<10)
/* Access level for a stats socket */
#define ACCESS_LVL_NONE 0

View File

@ -61,6 +61,9 @@
#ifdef DEBUG_FULL
#include <assert.h>
#endif
#if defined(USE_SYSTEMD)
#include <systemd/sd-daemon.h>
#endif
#include <common/base64.h>
#include <common/cfgparse.h>
@ -404,6 +407,9 @@ static void usage(char *name)
" -V enters verbose mode (disables quiet mode)\n"
" -D goes daemon ; -C changes to <dir> before loading files.\n"
" -W master-worker mode.\n"
#if defined(USE_SYSTEMD)
" -Ws master-worker mode with systemd notify support.\n"
#endif
" -q quiet mode : don't display messages\n"
" -c check mode : only check config files and exit\n"
" -n sets the maximum total # of connections (%d)\n"
@ -635,6 +641,10 @@ static void mworker_reload()
mworker_block_signals();
mworker_unregister_signals();
#if defined(USE_SYSTEMD)
if (global.tune.options & GTUNE_USE_SYSTEMD)
sd_notify(0, "RELOADING=1");
#endif
setenv("HAPROXY_MWORKER_REEXEC", "1", 1);
/* compute length */
@ -698,6 +708,11 @@ static void mworker_wait()
restart_wait:
#if defined(USE_SYSTEMD)
if (global.tune.options & GTUNE_USE_SYSTEMD)
sd_notifyf(0, "READY=1\nMAINPID=%lu", (unsigned long)getpid());
#endif
mworker_register_signals();
mworker_unblock_signals();
@ -710,6 +725,11 @@ restart_wait:
/* should reach there only if it fail */
goto restart_wait;
} else {
#if defined(USE_SYSTEMD)
if ((global.tune.options & GTUNE_USE_SYSTEMD) && (sig == SIGUSR1 || sig == SIGTERM)) {
sd_notify(0, "STOPPING=1");
}
#endif
Warning("Exiting Master process...\n");
mworker_kill(sig);
mworker_unregister_signals();
@ -1342,6 +1362,15 @@ static void init(int argc, char **argv)
arg_mode |= MODE_CHECK;
else if (*flag == 'D')
arg_mode |= MODE_DAEMON;
else if (*flag == 'W' && flag[1] == 's') {
arg_mode |= MODE_MWORKER;
#if defined(USE_SYSTEMD)
global.tune.options |= GTUNE_USE_SYSTEMD;
#else
Alert("master-worker mode with systemd support (-Ws) requested, but not compiled. Use master-worker mode (-W) if you are not using Type=notify in your unit file or recompile with USE_SYSTEMD=1.\n\n");
usage(progname);
#endif
}
else if (*flag == 'W')
arg_mode |= MODE_MWORKER;
else if (*flag == 'q')