Improve and clean up supervised process support. (#8036)

* Configuration file default should now be "auto".
* Expose "process_supervised" as an info field.
* Log messages improvements (clarify required systemd config, report
  auto-detected supervision mode, etc.)
* Set server.supervised properly, so it can take precedence of
  "daemonize" configuration.
* Produce clear warning if systemd is detected/requested but executable
  is compiled without support for it, instead of silently ignoring.
* Handle systemd notification error on startup, and turn off supervised
  mode if it failed.
This commit is contained in:
Yossi Gottlieb 2020-11-17 12:52:49 +02:00 committed by GitHub
parent ea7cf737a1
commit d638b05834
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 74 additions and 36 deletions

View File

@ -228,6 +228,7 @@ tcp-keepalive 300
# By default Redis does not run as a daemon. Use 'yes' if you need it. # By default Redis does not run as a daemon. Use 'yes' if you need it.
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized. # Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
# When Redis is supervised by upstart or systemd, this parameter has no impact.
daemonize no daemonize no
# If you run Redis from upstart or systemd, Redis can interact with your # If you run Redis from upstart or systemd, Redis can interact with your
@ -236,11 +237,17 @@ daemonize no
# supervised upstart - signal upstart by putting Redis into SIGSTOP mode # supervised upstart - signal upstart by putting Redis into SIGSTOP mode
# requires "expect stop" in your upstart job config # requires "expect stop" in your upstart job config
# supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET # supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET
# on startup, and updating Redis status on a regular
# basis.
# supervised auto - detect upstart or systemd method based on # supervised auto - detect upstart or systemd method based on
# UPSTART_JOB or NOTIFY_SOCKET environment variables # UPSTART_JOB or NOTIFY_SOCKET environment variables
# Note: these supervision methods only signal "process is ready." # Note: these supervision methods only signal "process is ready."
# They do not enable continuous pings back to your supervisor. # They do not enable continuous pings back to your supervisor.
supervised no #
# The default is "no". To run under upstart/systemd, you can simply uncomment
# the line below:
#
# supervised auto
# If a pid file is specified, Redis writes it where specified at startup # If a pid file is specified, Redis writes it where specified at startup
# and removes it at exit. # and removes it at exit.

View File

@ -4247,11 +4247,20 @@ sds genRedisInfoString(const char *section) {
static int call_uname = 1; static int call_uname = 1;
static struct utsname name; static struct utsname name;
char *mode; char *mode;
char *supervised;
if (server.cluster_enabled) mode = "cluster"; if (server.cluster_enabled) mode = "cluster";
else if (server.sentinel_mode) mode = "sentinel"; else if (server.sentinel_mode) mode = "sentinel";
else mode = "standalone"; else mode = "standalone";
if (server.supervised) {
if (server.supervised_mode == SUPERVISED_UPSTART) supervised = "upstart";
else if (server.supervised_mode == SUPERVISED_SYSTEMD) supervised = "systemd";
else supervised = "unknown";
} else {
supervised = "no";
}
if (sections++) info = sdscat(info,"\r\n"); if (sections++) info = sdscat(info,"\r\n");
if (call_uname) { if (call_uname) {
@ -4275,6 +4284,7 @@ sds genRedisInfoString(const char *section) {
"atomicvar_api:%s\r\n" "atomicvar_api:%s\r\n"
"gcc_version:%i.%i.%i\r\n" "gcc_version:%i.%i.%i\r\n"
"process_id:%I\r\n" "process_id:%I\r\n"
"process_supervised:%s\r\n"
"run_id:%s\r\n" "run_id:%s\r\n"
"tcp_port:%i\r\n" "tcp_port:%i\r\n"
"uptime_in_seconds:%I\r\n" "uptime_in_seconds:%I\r\n"
@ -4300,6 +4310,7 @@ sds genRedisInfoString(const char *section) {
0,0,0, 0,0,0,
#endif #endif
(int64_t) getpid(), (int64_t) getpid(),
supervised,
server.runid, server.runid,
server.port ? server.port : server.tls_port, server.port ? server.port : server.tls_port,
(int64_t)uptime, (int64_t)uptime,
@ -5224,62 +5235,82 @@ void redisSetCpuAffinity(const char *cpulist) {
#endif #endif
} }
/* /* Send a notify message to systemd. Returns sd_notify return code which is
* Check whether systemd or upstart have been used to start redis. * a positive number on success. */
*/ int redisCommunicateSystemd(const char *sd_notify_msg) {
#ifdef HAVE_LIBSYSTEMD
int ret = sd_notify(0, sd_notify_msg);
int redisSupervisedUpstart(void) { if (ret == 0)
serverLog(LL_WARNING, "systemd supervision error: NOTIFY_SOCKET not found!");
else if (ret < 0)
serverLog(LL_WARNING, "systemd supervision error: sd_notify: %d", ret);
return ret;
#else
UNUSED(sd_notify_msg);
return 0;
#endif
}
/* Attempt to set up upstart supervision. Returns 1 if successful. */
static int redisSupervisedUpstart(void) {
const char *upstart_job = getenv("UPSTART_JOB"); const char *upstart_job = getenv("UPSTART_JOB");
if (!upstart_job) { if (!upstart_job) {
serverLog(LL_WARNING, serverLog(LL_WARNING,
"upstart supervision requested, but UPSTART_JOB not found"); "upstart supervision requested, but UPSTART_JOB not found!");
return 0; return 0;
} }
serverLog(LL_NOTICE, "supervised by upstart, will stop to signal readiness"); serverLog(LL_NOTICE, "supervised by upstart, will stop to signal readiness.");
raise(SIGSTOP); raise(SIGSTOP);
unsetenv("UPSTART_JOB"); unsetenv("UPSTART_JOB");
return 1; return 1;
} }
int redisCommunicateSystemd(const char *sd_notify_msg) { /* Attempt to set up systemd supervision. Returns 1 if successful. */
const char *notify_socket = getenv("NOTIFY_SOCKET"); static int redisSupervisedSystemd(void) {
if (!notify_socket) { #ifndef HAVE_LIBSYSTEMD
serverLog(LL_WARNING, serverLog(LL_WARNING,
"systemd supervision requested, but NOTIFY_SOCKET not found"); "systemd supervision requested or auto-detected, but Redis is compiled without libsystemd support!");
}
#ifdef HAVE_LIBSYSTEMD
(void) sd_notify(0, sd_notify_msg);
#else
UNUSED(sd_notify_msg);
#endif
return 0; return 0;
#else
if (redisCommunicateSystemd("STATUS=Redis is loading...\n") <= 0)
return 0;
serverLog(LL_NOTICE,
"Supervised by systemd. Please make sure you set appropriate values for TimeoutStartSec and TimeoutStopSec in your service unit.");
return 1;
#endif
} }
int redisIsSupervised(int mode) { int redisIsSupervised(int mode) {
if (mode == SUPERVISED_AUTODETECT) { int ret = 0;
const char *upstart_job = getenv("UPSTART_JOB");
const char *notify_socket = getenv("NOTIFY_SOCKET");
if (upstart_job) { if (mode == SUPERVISED_AUTODETECT) {
redisSupervisedUpstart(); if (getenv("UPSTART_JOB")) {
} else if (notify_socket) { serverLog(LL_VERBOSE, "Upstart supervision detected.");
server.supervised_mode = SUPERVISED_SYSTEMD; mode = SUPERVISED_UPSTART;
serverLog(LL_WARNING, } else if (getenv("NOTIFY_SOCKET")) {
"WARNING auto-supervised by systemd - you MUST set appropriate values for TimeoutStartSec and TimeoutStopSec in your service unit."); serverLog(LL_VERBOSE, "Systemd supervision detected.");
return redisCommunicateSystemd("STATUS=Redis is loading...\n"); mode = SUPERVISED_SYSTEMD;
} }
} else if (mode == SUPERVISED_UPSTART) {
return redisSupervisedUpstart();
} else if (mode == SUPERVISED_SYSTEMD) {
serverLog(LL_WARNING,
"WARNING supervised by systemd - you MUST set appropriate values for TimeoutStartSec and TimeoutStopSec in your service unit.");
return redisCommunicateSystemd("STATUS=Redis is loading...\n");
} }
return 0; switch (mode) {
case SUPERVISED_UPSTART:
ret = redisSupervisedUpstart();
break;
case SUPERVISED_SYSTEMD:
ret = redisSupervisedSystemd();
break;
default:
break;
}
if (ret)
server.supervised_mode = mode;
return ret;
} }
int iAmMaster(void) { int iAmMaster(void) {