140 lines
4.9 KiB
Bash
Executable file
140 lines
4.9 KiB
Bash
Executable file
#!/bin/bash
|
|
|
|
set -o nounset
|
|
|
|
config_file=/var/lib/haproxy/conf/haproxy.config
|
|
pid_file=/var/lib/haproxy/run/haproxy.pid
|
|
old_pid=""
|
|
haproxy_conf_dir=/var/lib/haproxy/conf
|
|
readonly max_wait_time=30
|
|
readonly timeout_opts="-m 1 --connect-timeout 1"
|
|
readonly numeric_re='^[0-9]+$'
|
|
|
|
function haproxyHealthCheck() {
|
|
local wait_time=${MAX_RELOAD_WAIT_TIME:-$max_wait_time}
|
|
local port=${ROUTER_SERVICE_HTTP_PORT:-"80"}
|
|
local url="http://localhost:${port}"
|
|
local retries=0
|
|
local start_ts=$(date +"%s")
|
|
local proxy_proto="${ROUTER_USE_PROXY_PROTOCOL-}"
|
|
|
|
if ! [[ $wait_time =~ $numeric_re ]]; then
|
|
echo " - Invalid max reload wait time, using default $max_wait_time ..."
|
|
wait_time=$max_wait_time
|
|
fi
|
|
|
|
local end_ts=$((start_ts + wait_time))
|
|
|
|
# test with proxy protocol on
|
|
if [[ "${proxy_proto}" == "TRUE" || "${proxy_proto}" == "true" ]]; then
|
|
echo " - Proxy protocol on, checking ${url} ..."
|
|
while true; do
|
|
local statusline=$(echo $'PROXY UNKNOWN\r\nGET / HTTP/1.1\r\n' | socat tcp-connect:localhost:${port} stdio | head -1)
|
|
|
|
if [[ "$statusline" == *" 503 "* ]]; then
|
|
echo " - Health check ok : $retries retry attempt(s)."
|
|
return 0
|
|
fi
|
|
|
|
if [ $(date +"%s") -ge $end_ts ]; then
|
|
echo " - Exceeded max wait time ($wait_time) in health check - $retries retry attempt(s)."
|
|
return 1
|
|
fi
|
|
|
|
sleep 0.5
|
|
retries=$((retries + 1))
|
|
done
|
|
return 0
|
|
fi
|
|
|
|
echo " - Checking ${url} ..."
|
|
while true; do
|
|
local httpcode=$(curl $timeout_opts -s -o /dev/null -I -H "Host: " -w "%{http_code}" ${url})
|
|
|
|
if [ "$httpcode" == "503" ]; then
|
|
echo " - Health check ok : $retries retry attempt(s)."
|
|
return 0
|
|
fi
|
|
|
|
if [ $(date +"%s") -ge $end_ts ]; then
|
|
echo " - Exceeded max wait time ($wait_time) in health check - $retries retry attempt(s)."
|
|
return 1
|
|
fi
|
|
|
|
sleep 0.5
|
|
retries=$((retries + 1))
|
|
done
|
|
}
|
|
|
|
|
|
# How many times to retry removal of the iptables rules (if requested at all)
|
|
# It will sleep for 1/2 a second between attempts, so the time is retries / 2 secs
|
|
retries=20
|
|
|
|
|
|
# sort the path based map files for the haproxy map_beg function
|
|
for mapfile in "$haproxy_conf_dir"/*.map; do
|
|
sort -r "$mapfile" -o "$mapfile"
|
|
done
|
|
|
|
old_pids=$(ps -A -opid,args | grep haproxy | egrep -v -e 'grep|reload-haproxy' | awk '{print $1}' | tr '\n' ' ')
|
|
|
|
reload_status=0
|
|
installed_iptables=0
|
|
if [ -n "$old_pids" ]; then
|
|
if $(set | grep DROP_SYN_DURING_RESTART= > /dev/null) && [[ "$DROP_SYN_DURING_RESTART" == 'true' || "$DROP_SYN_DURING_RESTART" == '1' ]]; then
|
|
# We install the syn eater so that connections that come in during the restart don't
|
|
# go onto the wrong socket, which is then closed.
|
|
ports=$(grep -E -o '^\s*bind\s+:[[:digit:]]+\w' "$config_file" | cut -f2 -d: | paste -d, -s)
|
|
if [ -n "$ports" ]; then
|
|
# If this doesn't insert, we don't care, we still want to reload
|
|
/usr/sbin/iptables -I INPUT -p tcp -m multiport --dports $ports --syn -j DROP \
|
|
-m comment --comment "Eat SYNs while reloading haproxy" || :
|
|
installed_iptables=1
|
|
|
|
# The sleep is needed to let the socket drain before the new
|
|
# haproxy starts and binds to the same port. The value was
|
|
# determined by trial and error: I stopped seeing failures at
|
|
# 0.01 under load, so I put in a 10x margin. At worst, we may
|
|
# leave a connection in the old process' listen buffer that
|
|
# won't get handled, and they'll get a reset. I didn't want to
|
|
# set it too long, because that affects the overall time a
|
|
# reload takes which means that incoming connections aren't
|
|
# handled while the SYN eater is in place.
|
|
sleep 0.1
|
|
fi
|
|
fi
|
|
|
|
/usr/sbin/haproxy -f $config_file -p $pid_file -x /var/lib/haproxy/run/haproxy.sock -sf $old_pids
|
|
reload_status=$?
|
|
|
|
if [[ "$installed_iptables" == 1 ]]; then
|
|
# We NEVER want to leave the syn eater in place after the reload or haproxy
|
|
# will never get new connections. So try to remove it twenty times, and if
|
|
# that fails, log the error and return failure so the pod logs a fatal error.
|
|
i=0
|
|
while (( i++ < retries )) ; do
|
|
/usr/sbin/iptables -D INPUT -p tcp -m multiport --dports $ports --syn -j DROP \
|
|
-m comment --comment "Eat SYNs while reloading haproxy" || :
|
|
|
|
# Test the condition and end the loop if the rule has been removed
|
|
/usr/sbin/iptables -L INPUT | grep -F '/* Eat SYNs while reloading haproxy */' || break
|
|
|
|
>&2 echo "Unable to remove SYN eating rule, attempt $i. Will retry..."
|
|
|
|
# But sleep for a bit before retrying
|
|
sleep 0.5
|
|
done
|
|
if (( i >= retries )); then
|
|
# We failed to remove the rule... log failure and exit to signal the caller
|
|
>&2 echo "Unable to remove the iptables SYN eating rule. Aborting after $retries retries"
|
|
exit 1
|
|
fi
|
|
fi
|
|
else
|
|
/usr/sbin/haproxy -f $config_file -p $pid_file
|
|
reload_status=$?
|
|
fi
|
|
|
|
[ $reload_status -ne 0 ] && exit $reload_status
|
|
haproxyHealthCheck
|