Nginx 可以用 kill -HUP 来重启,不会丢失已建连接。Haproxy 如何做才能做到 zero downtime 无缝重载呢?

做法如下:

一、配置

编译harpoxy的时候带上参数 USE_SYSTEMD,选择 Haproxy 1.8 以上版本

make TARGET=linux2628 USE_GETADDRINFO=1 USE_ZLIB=1 USE_REGPARM=1 USE_OPENSSL=1 \
  USE_SYSTEMD=1 USE_PCRE=1 USE_PCRE_JIT=1 USE_NS=1
make install

Haproxy无缝重载技术:

  • 旧进程当前管理的连接根据 file descriptor 文件描述符通过 socket 套接字传输到新进程。

  • 在这个过程中,文件socket(unix socket)的连接没有断开。

  • 新进程在充当 master-worker 主工作者的同时执行此任务。

综上所述,通过使用unix socket来维护连接状态并在旧进程和新进程之间传递,防止了连接丢失。

haproxy 的运行使用 Systemd 重载,使用 -Ws 方式。 (此外,在构建时必须启用 USE_SYSTEMD)

  -D : start as a daemon. The process detaches from the current terminal after
    forking, and errors are not reported anymore in the terminal. It is
    equivalent to the "daemon" keyword in the "global" section of the
    configuration. It is recommended to always force it in any init script so
    that a faulty configuration doesn't prevent the system from booting.

  -W : master-worker mode. It is equivalent to the "master-worker" keyword in
    the "global" section of the configuration. This mode will launch a "master"
    which will monitor the "workers". Using this mode, you can reload HAProxy
    directly by sending a SIGUSR2 signal to the master.  The master-worker mode
    is compatible either with the foreground or daemon mode.  It is
    recommended to use this mode with multiprocess and systemd.

  -Ws : master-worker mode with support of `notify` type of systemd service.
    This option is only available when HAProxy was built with `USE_SYSTEMD`
    build option enabled.

具体的启动脚本:/etc/systemd/system/haproxy.service

[Unit]
Description=HAProxy Load Balancer
After=network-online.target
Wants=network-online.target

[Service]
Environment="CONFIG=/etc/haproxy/haproxy.cfg" "PIDFILE=/run/haproxy.pid"
ExecStartPre=/usr/sbin/haproxy -f $CONFIG -c -q
ExecStart=/usr/sbin/haproxy -Ws -f $CONFIG -p $PIDFILE
ExecReload=/usr/sbin/haproxy -f $CONFIG -c -q
ExecReload=/bin/kill -USR2 $MAINPID
KillMode=mixed
Restart=always
SuccessExitStatus=143
Type=notify

# The following lines leverage SystemD's sandboxing options to provide
# defense in depth protection at the expense of restricting some flexibility
# in your setup (e.g. placement of your configuration files) or possibly
# reduced performance. See systemd.service(5) and systemd.exec(5) for further
# information.

# NoNewPrivileges=true
# ProtectHome=true
# If you want to use 'ProtectSystem=strict' you should whitelist the PIDFILE,
# any state files and any other files written using 'ReadWritePaths' or
# 'RuntimeDirectory'.
# ProtectSystem=true
# ProtectKernelTunables=true
# ProtectKernelModules=true
# ProtectControlGroups=true
# If your SystemD version supports them, you can add: @reboot, @swap, @sync
# SystemCallFilter=~@cpu-emulation @keyring @module @obsolete @raw-io

[Install]
WantedBy=multi-user.target

二、验证

验证是否真的是无缝重载的步骤如下:

在haproxy.cfg的global段落中加入stat的配置:

stats socket /var/run/haproxy.sock level admin expose-fd listeners process 1

运行一个不断循环重启的脚本:

while true ; do systemctl reload haproxy ; sleep 3 ; done

用 apache 的压测工具 ab 来压一下。

Send request while service is reloading:

ab -r -c 20 -n 100000 http://127.0.0.1/

最后检查结果中 “Failed requests” 是否为零就可以了