When doing server upgrades with multiple servers, the ideal way is to:
1. take one instance out of the pool
2. drain connections on it
3. upgrade it
4. put it back into the pool
5. back to #1.
The various techniques can be categorized as:
1. application-level
2. load-balancer-level
3. OS-level
The most graceful method is to use an application-level feature, since the application knows what its worker status is.
For example, with httpd on CentOS or Redhat, either use the apachectl
command, or add the graceful-stop option to /etc/init.d/httpd:
#!/bin/bash
set -e
echo "info: Draining connections ..."
apachectl graceful-stop
echo "info: You have 5 minutes to start and finish your upgrade."
sleep 300
apachectl start
echo "info: httpd restarted!"
exit 0
If we didn’t have an application-specific way to do that, we could use iptables
:
#!/bin/bash
set -e
iptables -I INPUT -j DROP -p tcp --syn --destination-port 80
echo "info: Draining connections ..."
sleep 60
echo "info: You have 5 minutes to start and finish your upgrade"
sleep 300
iptables -D INPUT -j DROP -p tcp --syn --destination-port 80
echo "info: iptables allowing new incoming connections!"
exit 0
With HAProxy we can do this on the HAProxy host (do yum -y install socat
first):
#!/bin/bash
set -e
echo "set server application-backend/www0 state drain" | socat unix-connect:/var/run/haproxy.sock stdio
echo "info: Draining connections ..."
sleep 60
echo "set server application-backend/www0 state maint" | socat unix-connect:/var/run/haproxy.sock stdio
echo "info: You have 5 minutes to start and finish your upgrade"
sleep 300
echo "set server application-backend/www0 state ready" | socat unix-connect:/var/run/haproxy.sock stdio
echo "info: haproxy allowing new incoming connections!"
exit 0
Sample HAProxy “show stat” output while www0 is draining (notice the “DRAIN” status):
[root@gw ~]# echo "show info" | socat unix-connect:/var/run/haproxy.sock stdio
Name: HAProxy
Version: 1.5.10
[..]
[root@gw ~]# echo "show stat" | socat unix-connect:/var/run/haproxy.sock stdio
# pxname,svname,qcur,qmax,scur,smax,slim,stot,bin,bout,dreq,dresp,ereq,econ,eresp,wretr,wredis,status,weight,act,bck,chkfail,chkdown,lastchg,downtime,qlimit,pid,iid,sid,throttle,lbtot,tracked,type,rate,rate_lim,rate_max,check_status,check_code,check_duration,hrsp_1xx,hrsp_2xx,hrsp_3xx,hrsp_4xx,hrsp_5xx,hrsp_other,hanafail,req_rate,req_rate_max,req_tot,cli_abrt,srv_abrt,comp_in,comp_out,comp_byp,comp_rsp,lastsess,last_chk,last_agt,qtime,ctime,rtime,ttime,
http-in,FRONTEND,,,0,1,2000,6,998,19219,0,0,0,,,,,OPEN,,,,,,,,,1,2,0,,,,0,0,0,1,,,,0,5,1,0,0,0,,0,1,6,,,0,0,0,0,,,,,,,,
https-in,FRONTEND,,,0,20,2000,12,294,2556,0,0,11,,,,,OPEN,,,,,,,,,1,3,0,,,,0,0,0,2,,,,0,0,1,11,0,0,,0,2,12,,,0,0,0,0,,,,,,,,
application-backend,www0,0,0,0,1,5000,3,583,1078,,0,,0,0,0,0,DRAIN,1,1,0,0,0,385,0,,1,4,1,,3,,2,0,,1,L7OK,301,0,0,2,1,0,0,0,0,,,,0,0,,,,,442,Moved Permanently,,0,0,0,1,
application-backend,www1,0,0,0,1,5000,4,709,18640,,0,,0,0,0,0,UP,1,1,0,0,0,755,0,,1,4,2,,4,,2,0,,1,L7OK,301,0,0,3,1,0,0,0,0,,,,0,0,,,,,141,Moved Permanently,,0,1,8,8,
application-backend,BACKEND,0,0,0,1,400,7,1292,19718,0,0,,0,0,0,0,UP,1,1,0,,0,755,0,,1,4,0,,7,,1,0,,1,,,,0,5,2,0,0,0,,,,,0,0,0,0,0,0,141,,,0,1,8,8,
For nginx:
#!/bin/bash
set -e
echo "info: Draining connections ..."
nginx -s quit
echo "info: You have 5 minutes to start and finish your upgrade."
sleep 300
nginx -s start
echo "info: nginx restarted!"
exit 0
If you’re using a configuration management system, like puppet or Chef, you can remove the service from your load balancer pool. This works well in practice with only 2 or 3 servers, though draining is usually not considered.
Note that when using the popular “reverse HAProxy” setup with application servers running HAProxy on localhost, and HAProxy forwarding localhost requests to the real servers (like httpd), then you want to stop or block the httpd services on the real server end. Otherwise you would have to make changes on multiple application servers.
In a future post, I’ll discuss zero-downtime deploys.
Drain connections on restart of NGINX process? (with iptables)
Tomcat’s Graceful Shutdown with Daemons and Shutdown Hooks
Get haproxy stats/informations via socat
Go net/http: add built-in graceful shutdown support to Server #4674
haproxy.tech-notes.net: HAProxy Socket Commands