mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-01-11 16:29:36 +00:00
MAJOR: check: find out which port to use for health check at run time
HAProxy used to deduce port used for health checks when parsing configuration at startup time. Because of this way of working, it makes it complicated to change the port at run time. The current patch changes this behavior and makes HAProxy to choose the port used for health checking when preparing the check task itself. A new type of error is introduced and reported when no port can be found. There won't be any impact on performance, since the process to find out the port value is made of a few 'if' statements. This patch also introduces a new check state CHK_ST_PORT_MISS: this flag is used to report an error in the case when HAProxy needs to establish a TCP connection to a server, to perform a health check but no TCP ports can be found for it. And last, it also introduces a new stream termination condition: SF_ERR_CHK_PORT. Purpose of this flag is to report an error in the event when HAProxy has to run a health check but no port can be found to perform it.
This commit is contained in:
parent
7276f3aa3d
commit
95db2bcfee
@ -50,6 +50,7 @@ void free_check(struct check *check);
|
||||
|
||||
void send_email_alert(struct server *s, int priority, const char *format, ...)
|
||||
__attribute__ ((format(printf, 3, 4)));
|
||||
int srv_check_healthcheck_port(struct check *chk);
|
||||
#endif /* _PROTO_CHECKS_H */
|
||||
|
||||
/*
|
||||
|
@ -41,6 +41,7 @@ enum chk_result {
|
||||
#define CHK_ST_ENABLED 0x0004 /* this check is currently administratively enabled */
|
||||
#define CHK_ST_PAUSED 0x0008 /* checks are paused because of maintenance (health only) */
|
||||
#define CHK_ST_AGENT 0x0010 /* check is an agent check (otherwise it's a health check) */
|
||||
#define CHK_ST_PORT_MISS 0x0020 /* check can't be send because no port is configured to run it */
|
||||
|
||||
/* check status */
|
||||
enum {
|
||||
|
@ -73,6 +73,7 @@
|
||||
#define SF_ERR_DOWN 0x00009000 /* the proxy killed a stream because the backend became unavailable */
|
||||
#define SF_ERR_KILLED 0x0000a000 /* the proxy killed a stream because it was asked to do so */
|
||||
#define SF_ERR_UP 0x0000b000 /* the proxy killed a stream because a preferred backend became available */
|
||||
#define SF_ERR_CHK_PORT 0x0000c000 /* no port could be found for a health check. TODO: check SF_ERR_SHIFT */
|
||||
#define SF_ERR_MASK 0x0000f000 /* mask to get only stream error flags */
|
||||
#define SF_ERR_SHIFT 12 /* bit shift */
|
||||
|
||||
|
69
src/checks.c
69
src/checks.c
@ -679,6 +679,12 @@ static void chk_report_conn_err(struct connection *conn, int errno_bck, int expi
|
||||
}
|
||||
}
|
||||
|
||||
if (check->state & CHK_ST_PORT_MISS) {
|
||||
/* NOTE: this is reported after <fall> tries */
|
||||
chunk_printf(chk, "No port available for the TCP connection");
|
||||
set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
|
||||
}
|
||||
|
||||
if ((conn->flags & (CO_FL_CONNECTED|CO_FL_WAIT_L4_CONN)) == CO_FL_WAIT_L4_CONN) {
|
||||
/* L4 not established (yet) */
|
||||
if (conn->flags & CO_FL_ERROR)
|
||||
@ -1430,6 +1436,7 @@ static struct task *server_warmup(struct task *t)
|
||||
* - SF_ERR_PRXCOND if the connection has been limited by the proxy (maxconn)
|
||||
* - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
|
||||
* - SF_ERR_INTERNAL for any other purely internal errors
|
||||
* - SF_ERR_CHK_PORT if no port could be found to run a health check on an AF_INET* socket
|
||||
* Additionnally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
|
||||
* Note that we try to prevent the network stack from sending the ACK during the
|
||||
* connect() when a pure TCP check is used (without PROXY protocol).
|
||||
@ -1491,8 +1498,16 @@ static int connect_conn_chk(struct task *t)
|
||||
conn->addr.to = s->addr;
|
||||
}
|
||||
|
||||
if (check->port) {
|
||||
set_host_port(&conn->addr.to, check->port);
|
||||
if ((conn->addr.to.ss_family == AF_INET) || (conn->addr.to.ss_family == AF_INET6)) {
|
||||
int i = 0;
|
||||
|
||||
i = srv_check_healthcheck_port(check);
|
||||
if (i == 0) {
|
||||
conn->owner = check;
|
||||
return SF_ERR_CHK_PORT;
|
||||
}
|
||||
|
||||
set_host_port(&conn->addr.to, i);
|
||||
}
|
||||
|
||||
proto = protocol_by_family(conn->addr.to.ss_family);
|
||||
@ -2072,6 +2087,9 @@ static struct task *process_chk_conn(struct task *t)
|
||||
conn->flags |= CO_FL_ERROR;
|
||||
chk_report_conn_err(conn, errno, 0);
|
||||
break;
|
||||
/* should share same code than cases below */
|
||||
case SF_ERR_CHK_PORT:
|
||||
check->state |= CHK_ST_PORT_MISS;
|
||||
case SF_ERR_PRXCOND:
|
||||
case SF_ERR_RESOURCE:
|
||||
case SF_ERR_INTERNAL:
|
||||
@ -3368,6 +3386,53 @@ void send_email_alert(struct server *s, int level, const char *format, ...)
|
||||
enqueue_email_alert(p, buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return value:
|
||||
* the port to be used for the health check
|
||||
* 0 in case no port could be found for the check
|
||||
*/
|
||||
int srv_check_healthcheck_port(struct check *chk)
|
||||
{
|
||||
int i = 0;
|
||||
struct server *srv = NULL;
|
||||
|
||||
srv = chk->server;
|
||||
|
||||
/* If neither a port nor an addr was specified and no check transport
|
||||
* layer is forced, then the transport layer used by the checks is the
|
||||
* same as for the production traffic. Otherwise we use raw_sock by
|
||||
* default, unless one is specified.
|
||||
*/
|
||||
if (!chk->port && !is_addr(&chk->addr)) {
|
||||
#ifdef USE_OPENSSL
|
||||
chk->use_ssl |= (srv->use_ssl || (srv->proxy->options & PR_O_TCPCHK_SSL));
|
||||
#endif
|
||||
chk->send_proxy |= (srv->pp_opts);
|
||||
}
|
||||
|
||||
/* by default, we use the health check port ocnfigured */
|
||||
if (chk->port > 0)
|
||||
return chk->port;
|
||||
|
||||
/* try to get the port from check_core.addr if check.port not set */
|
||||
i = get_host_port(&chk->addr);
|
||||
if (i > 0)
|
||||
return i;
|
||||
|
||||
/* try to get the port from server address */
|
||||
/* prevent MAPPORTS from working at this point, since checks could
|
||||
* not be performed in such case (MAPPORTS impose a relative ports
|
||||
* based on live traffic)
|
||||
*/
|
||||
if (srv->flags & SRV_F_MAPPORTS)
|
||||
return 0;
|
||||
i = get_host_port(&srv->addr); /* by default */
|
||||
if (i > 0)
|
||||
return i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
|
25
src/server.c
25
src/server.c
@ -869,7 +869,6 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
|
||||
|
||||
if (!strcmp(args[0], "server") || !strcmp(args[0], "default-server")) { /* server address */
|
||||
int cur_arg;
|
||||
short realport = 0;
|
||||
int do_agent = 0, do_check = 0, defsrv = (*args[0] == 'd');
|
||||
|
||||
if (!defsrv && curproxy == defproxy) {
|
||||
@ -961,10 +960,6 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
else {
|
||||
/* used by checks */
|
||||
realport = port1;
|
||||
}
|
||||
|
||||
/* save hostname and create associated name resolution */
|
||||
newsrv->hostname = fqdn;
|
||||
@ -1749,29 +1744,11 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If neither a port nor an addr was specified and no check transport
|
||||
* layer is forced, then the transport layer used by the checks is the
|
||||
* same as for the production traffic. Otherwise we use raw_sock by
|
||||
* default, unless one is specified.
|
||||
*/
|
||||
if (!newsrv->check.port && !is_addr(&newsrv->check.addr)) {
|
||||
#ifdef USE_OPENSSL
|
||||
newsrv->check.use_ssl |= (newsrv->use_ssl || (newsrv->proxy->options & PR_O_TCPCHK_SSL));
|
||||
#endif
|
||||
newsrv->check.send_proxy |= (newsrv->pp_opts);
|
||||
}
|
||||
/* try to get the port from check_core.addr if check.port not set */
|
||||
if (!newsrv->check.port)
|
||||
newsrv->check.port = get_host_port(&newsrv->check.addr);
|
||||
|
||||
if (!newsrv->check.port)
|
||||
newsrv->check.port = realport; /* by default */
|
||||
|
||||
/*
|
||||
* We need at least a service port, a check port or the first tcp-check rule must
|
||||
* be a 'connect' one when checking an IPv4/IPv6 server.
|
||||
*/
|
||||
if (!newsrv->check.port &&
|
||||
if ((srv_check_healthcheck_port(&newsrv->check) == 0) &&
|
||||
(is_inet_addr(&newsrv->check.addr) ||
|
||||
(!is_addr(&newsrv->check.addr) && is_inet_addr(&newsrv->addr)))) {
|
||||
struct tcpcheck_rule *r = NULL;
|
||||
|
Loading…
Reference in New Issue
Block a user