前言 本文将介绍在 Linux 环境中,HaProxy 如何利用 Keepalived 实现双机热备(即双机主备),从而保证其高可用性,适用于 CentOS、Debian、Ubuntu 等 Linux 发行版。
准备工作 整体架构 由于本文重点在于介绍 HAProxy + Keepalived 实现双机主备(即双机热备),因此下图中 HAProxy 针对后端 Web 服务(例如 Kubernetes API Server)的反向代理配置将不再展开详细介绍。
部署规划 节点 名称 角色 IP 备注 节点一 node1 Master 192.168.2.191安装 HaProxy、Keepalived 节点二 node2 Backup 192.168.2.148安装 HaProxy、Keepalived
虚拟 IP(VIP)的说明
虚拟 IP(VIP)由 Keepalived 提供。 在配置 Keepalived 的 virtual_ipaddress 参数时,所指定的 VIP 地址必须与 HaProxy 所在节点处于同一网段。 例如,如果两个 HaProxy 节点的网段为 192.168.2.0/24,则 VIP 也应设置为该网段内的地址(如 192.168.2.x)。 关闭防火墙 关闭系统防火墙,避免 Keepalived 主备节点之间无法正常通信
1 2 3 4 5 # systemctl stop firewalld# systemctl disable firewalld
若不希望关闭防火墙,则需要开放以下防火墙端口,尤其是 Keepalived 的 112(VRRP)端口
组件 协议 / 端口 是否必须开放 作用 Keepalived IP 协议号 112(VRRP) 必须 VRRP 主备心跳 HaProxy TCP 80 可选 代理 HTTP 服务 HaProxy TCP 443 可选 代理 HTTPS 服务 HaProxy Stats TCP 1080(可自定义) 可选 访问 HAProxy 的统计页面
安装 HaProxy 特别注意
在两个节点上,分别编译安装 HaProxy,这两个节点上的 HaProxy 配置文件是完全相同的 。安装概述 安装说明 路径 HaProxy 的安装路径 /usr/local/sbin/haproxyHaProxy 的配置文件 /etc/haproxy/haproxy.cfgHaProxy 的 PID 文件 /var/lib/haproxy/haproxy.pidHaProxy 的 Socket 文件 /var/lib/haproxy/statsHaProxy 的 Systemd 服务配置文件 /usr/lib/systemd/system/haproxy.service
安装步骤 安装依赖软件包
1 2 3 4 5 # CentOS 系统安装软件包# yum install -y gcc make pcre-devel openssl openssl-devel systemd-devel wget# Debian 、Ubuntu系统安装软件包# apt install -y gcc make libpcre3-dev openssl libssl-dev libsystemd-dev wget
下载 HaProxy 源码包(官网下载地址 )
1 2 # wget https://www.haproxy.org/download/2.8/src/haproxy-2.8.5.tar.gz
下载 HaProxy 源码包
1 2 # tar -xvf haproxy-2.8.5.tar.gz
编译源码与安装
1 2 3 4 5 6 7 8 9 10 11 12 13 # cd haproxy-2.8.5# make TARGET=linux-glibc \ USE_OPENSSL=1 \ USE_PCRE=1 \ USE_SYSTEMD=1 \ USE_ZLIB=1 \ -j $(nproc)# make install
创建用户 创建 HaProxy 用户与用户组
1 2 3 4 5 # groupadd --system haproxy# sudo useradd --system --no -create-home --shell /sbin/nologin --gid haproxy haproxy
配置服务 配置 Systemd 管理 HaProxy 服务
1 2 # vim /usr/lib/systemd/system/haproxy.service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [Unit] Description=HAProxy Load Balancer After=network.target [Service] User=haproxy Group=haproxy LimitNOFILE=1048576 Environment="CONFIG=/etc/haproxy/haproxy.cfg" "PIDFILE=/var/lib/haproxy/haproxy.pid" ExecStartPre=/usr/local/sbin/haproxy -f $CONFIG -c -q ExecStart=/usr/local/sbin/haproxy -Ws -f $CONFIG -p $PIDFILE ExecReload=/bin/kill -USR2 $MAINPID KillMode=mixed Type=notify [Install] WantedBy=multi-user.target
创建 HaProxy 的运行目录
1 2 3 4 5 6 7 8 # mkdir -p /var/lib/haproxy# chown -R haproxy:haproxy /var/lib/haproxy# chmod 750 /var/lib/haproxy
创建 HaProxy 的配置目录
1 2 # mkdir -p /etc/haproxy
创建 HaProxy 的配置文件
1 2 # vim /etc/haproxy/haproxy.cfg
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 #--------------------------------------------------------------------- # Global settings #--------------------------------------------------------------------- global # to have these messages end up in /var/log/haproxy.log you will # need to: # 1) configure syslog to accept network log events. This is done # by adding the '-r' option to the SYSLOGD_OPTIONS in # /etc/sysconfig/syslog # 2) configure local2 events to go to the /var/log/haproxy.log # file. A line like the following can be added to # /etc/sysconfig/syslog # # local2.* /var/log/haproxy.log # log 127.0.0.1 local2 # chroot /var/lib/haproxy # pidfile /var/run/haproxy.pid # user haproxy # group haproxy maxconn 4000 nbthread 4 # nbproc 1 # daemon # turn on stats unix socket stats socket /var/lib/haproxy/stats #--------------------------------------------------------------------- # common defaults that all the 'listen' and 'backend' sections will # use if not designated in their block #--------------------------------------------------------------------- defaults mode http log global option httplog option dontlognull option http-server-close option forwardfor except 127.0.0.0/8 option redispatch retries 3 timeout http-request 10s timeout queue 1m timeout connect 10s timeout client 1m timeout server 1m timeout http-keep-alive 10s timeout check 10s maxconn 3000 #--------------------------------------------------------------------- # collection haproxy statistics message #--------------------------------------------------------------------- listen stats bind *:1080 stats auth admin:admin stats refresh 5s stats realm HAProxy\ Statistics stats uri /admin?stats
HaProxy 配置文件的使用说明
配置项 类型 默认值 说明 maxconn全局 / 前端 / 后端 2000最大连接数,控制 HAProxy 可以同时处理的并发连接数。 nbthread全局 1指定每个进程使用的线程数。多线程模式可提高单进程并发性能。 nbproc全局 1指定 HAProxy 启动的进程数。多进程模式用于多核优化,但 Systemd 下不推荐与 daemon 配合使用。 daemon全局 禁用 是否后台运行。Systemd 管理 HaProxy 时通常禁用,HAProxy 由 Systemd 控制前后台。 modedefaults/frontend/backend http连接模式,可选 http 或 tcp。可以使用 tcp 来实现 Kubernetes API Server 代理。 balancebackend roundrobin负载均衡算法,可选 roundrobin、leastconn、source 等。 stats authlisten 设置统计页面认证用户名和密码,例如 admin:admin。 stats refreshlisten 10s统计页面刷新时间,单位秒。 stats realmlisten HTTP Basic Auth 的 realm 名称,显示在认证弹窗中。 stats urilisten /HaProxy 统计页面访问 URI,例如 /admin?stats。
Haproxy 的配置建议
maxconn + nbthread 可以提升单进程并发性能。mode tcp 是代理 Kubernetes API Server 的标准配置,因为 API Server 是 HTTPS / TCP 流量。当使用 Systemd 管理 HaProxy 时,建议配置 nbproc = 1 + nbthread > 1 + 不启用 daemon ,这是最稳定和高效的配置方式。 若 HaProxy 需要对后端 Web 服务(例如 Kubernetes API Server)进行反向代理,可以在上述 HaProxy 配置内容的基础上,再添加以下内容(请根据实际业务进行更改 ),完整的配置案例可以看 这里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #--------------------------------------------------------------------- # kubernetes apiserver frontend which proxys to the backends #--------------------------------------------------------------------- frontend kubernetes-apiserver mode tcp bind *:16443 option tcplog default_backend kubernetes-apiserver #--------------------------------------------------------------------- # round robin balancing between the various backends #--------------------------------------------------------------------- backend kubernetes-apiserver mode tcp balance roundrobin server master01.k8s.io 192.168.2.191:6443 check server master02.k8s.io 192.168.2.148:6443 check
HaProxy 加上反向代理的配置后,就可以通过 http://<node-ip>:16443 来访问后端的多个 Web 服务
启动服务 设置开机自启动 HaProxy 服务
1 2 3 4 5 # systemctl daemon-reload# systemctl enable haproxy
立刻启动 HaProxy 服务
1 2 3 4 5 # systemctl start haproxy# systemctl status haproxy
当 HaProxy 的配置文件发生变更后,可以执行热加载(也叫平滑重载,不会中断现有连接),让配置文件生效
1 2 # systemctl reload haproxy
验证服务 浏览器通过 http://<node-ip>:1080/admin?stats 访问 HaProxy 的统计页面,默认的登录用户名和密码是 admin / admin;如果可以正常访问(如下图所示),则说明 HaProxy 可以正常运行
安装 Keepalived 特别注意
在两个节点上,分别安装 Keepalived,这两个节点上的 Keepalived 配置文件是不一样的。 安装概述 Keepalived 是一个高可用软件,基于 VIP 绑定实现服务器双机主备(也叫双机热备),在上述拓扑中,Keepalived 主要根据 HaProxy 的运行状态判断是否需要故障转移(VIP 切换)。 例如,当 HaProxy 主节点挂掉,VIP 会自动绑定在 HaProxy 备节点,从而保证 VIP 一直可用,实现 HaProxy 高可用。 安装说明 路径 Keepalived 的安装路径 /usr/local/keepalivedKeepalived 的配置文件 /usr/local/keepalived/etc/keepalived/keepalived.confKeepalived 的 Systemd 服务配置文件(自动生成) /usr/lib/systemd/system/keepalived.serviceKeepalived 检测 HaProxy 是否可用的 Shell 脚本 /usr/local/keepalived/etc/keepalived/scripts/haproxy_check.sh
安装步骤 安装依赖软件包
1 2 3 4 5 # CentOS 系统安装软件包# yum install -y gcc gcc-c++ make openssl-devel libnl libnl-devel net-snmp-devel popt-devel# Debian 、Ubuntu系统安装软件包# apt install -y gcc g++ make libssl-dev libnl-3-dev libnl-genl-3-dev libsnmp-dev libpopt-dev
下载 Keepalived 源码包(官网下载地址 )
1 2 # wget https://www.keepalived.org/software/keepalived-2.2.8.tar.gz
解压 Keepalived 源码包
1 2 # tar -xvf keepalived-2.2.8.tar.gz
编译源码与安装
1 2 3 4 5 6 7 8 9 10 11 # cd keepalived-2.2.8# make -j $(nproc)# make install
创建脚本 创建用于 Keepalived 检测 HaProxy 服务是否可用的 Shell 脚本(比如 haproxy_check.sh)
1 2 3 4 5 # mkdir -p /usr/local /keepalived/etc/keepalived/scripts/# vim /usr/local /keepalived/etc/keepalived/scripts/haproxy_check.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 HAPROXY_COUNT=$(pgrep -x haproxy | wc -l ) if [ "$HAPROXY_COUNT " -eq 0 ]; then echo "HAProxy not running" exit 1 else echo "HAProxy running" exit 0 fi
授予 HaProxy 检测脚本可执行权限
1 2 # chmod +x /usr/local /keepalived/etc/keepalived/scripts/haproxy_check.sh
配置服务 Keepalived 配置文件的使用说明
配置项 示例值 说明 router_idk8s本节点唯一标识,用于日志区分(不同节点可不同)。 script"killall -0 haproxy"检测 HAProxy 服务是否可用,可以是 Shell 命令,还可以是 Shell 脚本的绝对路径。返回 0 表示正常,返回非 0 表示失败。 interval2检测脚本执行间隔(秒),指定 Keepalived 多久执行一次健康检查脚本。 weight-100节点权重变化值,检测脚本失败时降低优先级或成功时增加优先级。负值表示降低权重,正值表示增加权重。 fall3连续失败次数阈值,当检测脚本连续失败次数达到 fall 时,节点会认为服务不可用,并扣减权重或触发 VIP 切换。 rise2连续成功次数阈值,当检测脚本连续成功次数达到 rise 时,节点会认为服务恢复,并恢复权重初始值。 stateMASTER节点初始角色(MASTER 或 BACKUP),最终由优先级决定。 interfaceenp0s3VRRP 使用的网络接口名称,可通过 ifconfig 命令得知。 virtual_router_id51VRRP 组 ID,两个节点必须一致。 priority200节点优先级(权重),数值越大优先级越高。 advert_int1VRRP 心跳间隔(秒),默认 1 秒。 auth_typePASSVRRP 认证方式(PASS 或 AH)。 auth_passceb1b3ec013d66163d6abVRRP 认证密码,两个节点必须一致。 virtual_ipaddress192.168.2.100Keepalived 提供的 VIP(虚拟 IP),由 MASTER 持有。 track_scriptcheck_haproxy指定用于健康检查的脚本,检测失败时会调节点整权重或触发 VIP 切换。
Keepalived 什么时候触发 VIP 切换
Keepalived 的 VIP 切换依赖节点权重变化,只有当 MASTER 节点的实时权重下降到低于 BACKUP 节点时,VIP 才会切换到权重更高的节点。 在 Keepalived 配置文件中,vrrp_script 可以通过 weight 动态调整节点权重:正值会增加节点权重,负值会降低节点权重。 当 Keepalived 脚本检测失败时,节点权重会根据 weight 设置下降,如果下降后 MASTER 节点的权重低于 BACKUP 节点,VIP 就会切换。 当 Keepalived 脚本检测成功时,且连续检测成功的次数达到 rise 次后,节点权重会恢复为初始值,原 MASTER 节点可能会重新成为 MASTER。 Keepalived 的 weight 必须设置足够大,否则即使 Keepalived 脚本检测失败,MASTER 节点的权重仍可能比 BACKUP 节点高,从而导致不会触发 VIP 切换。 Keepalived 调整节点权重的细节
Keepalived 不会因为检测脚本持续失败而反复扣减节点权重,只有当检测脚本的检测结果从 "成功" 变为 "失败" 时,才会触发一次节点权重扣减。 也就是说,第一次检测被判定为失败,会执行一次 weight 扣减;当后续检测脚本继续失败时,由于节点已经处于失败状态,不会再重复扣减节点权重。 当检测脚本恢复成功后,Keepalived 会依据 rise 参数进行节点权重恢复。当检测脚本连续成功达到 rise 次后,节点权重会恢复到初始值。 简而言之,Keepalived 的节点权重扣减是一次性的,直到检测脚本恢复成功,才再次生效 。同样的,在节点处于健康状态期间,检测脚本即便持续成功,也不会重复增加节点权重。在第一个节点 (比如 node1)中,创建 Keepalived 的配置文件,请自行更改 interface(网卡接口名称)与 virtual_ipaddress(虚拟 IP)配置项
1 2 # vim /usr/local /keepalived/etc/keepalived/keepalived.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ! Configuration File for keepalived global_defs { router_id k8s } vrrp_script check_haproxy { script "/usr/local/keepalived/etc/keepalived/scripts/haproxy_check.sh" interval 2 weight -100 fall 3 rise 2 } vrrp_instance VI_1 { state MASTER interface enp0s3 virtual_router_id 51 priority 250 advert_int 1 authentication { auth_type PASS auth_pass ceb1b3ec013d66163d6ab } virtual_ipaddress { 192.168.2.100 } track_script { check_haproxy } }
在第二个节点 (比如 node2)中,创建 Keepalived 的配置文件,请自行更改 interface(网卡接口名称)与 virtual_ipaddress(虚拟 IP)配置项
1 2 # vim /usr/local /keepalived/etc/keepalived/keepalived.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ! Configuration File for keepalived global_defs { router_id k8s } vrrp_script check_haproxy { script "/usr/local/keepalived/etc/keepalived/scripts/haproxy_check.sh" interval 2 weight -100 fall 3 rise 2 } vrrp_instance VI_1 { state BACKUP interface enp0s3 virtual_router_id 51 priority 200 advert_int 1 authentication { auth_type PASS auth_pass ceb1b3ec013d66163d6ab } virtual_ipaddress { 192.168.2.100 } track_script { check_haproxy } }
启动服务 设置开机自启动 Keepalived 服务
1 2 3 4 5 # systemctl daemon-reload# systemctl enable keepalived
立刻启动 Keepalived 服务
1 2 3 4 5 # systemctl start keepalived# systemctl status keepalived
当 Keepalived 的配置文件发生变更后,可以重启服务让其生效
1 2 # systemctl restart keepalived
验证高可用性 验证目标
本节旨在验证 HaProxy + Keepalived 是否能够实现双机主备(即双机热备)高可用机制。具体而言,当第一个 Master 节点上的 HaProxy 发生宕机时,Keepalived 能够将虚拟 IP(VIP)自动切换到第二个 Master 节点上。当第一个 Master 节点上的 HaProxy 恢复运行时,虚拟 IP(VIP)会自动切换回第一个 Master 节点上。
(1) 在第一个节点(比如 node1)中,查看指定网卡(比如 enp0s3)的详细 IP 地址与状态信息,请自行更改网卡接口名称 输出示例如下,可以看到 VIP(虚拟 IP)地址 192.168.2.100 绑定了网卡接口 enp0s3
1 2 3 4 5 6 7 8 9 10 11 12 2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 08:00:27:de:6f:6b brd ff:ff:ff:ff:ff:ff inet 192.168.2.191/24 brd 192.168.2.255 scope global noprefixroute dynamic enp0s3 valid_lft 42617sec preferred_lft 42617sec inet 192.168.2.100/32 scope global enp0s3 valid_lft forever preferred_lft forever inet6 fe80::8b9e:77d3:4d79:eab9/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::6c1a:25c4:bdbd:dad3/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::8dee:f54a:fcd2:369b/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever
(2) 在第一个节点(比如 node1)中,关闭 HaProxy 服务,并查看 Keepalived 的运行状态 1 2 # systemctl stop haproxy
1 2 # systemctl status keepalived
输出示例如下,可以看到 Keepalived 扣减了当前节点的权重(计算公式:250 - 100 = 150),同时 VIP 也被移除了
1 2 3 4 5 6 7 8 11月 08 14:14:40 node1 Keepalived_vrrp[30483]: Sending gratuitous ARP on enp0s3 for 192.168.2.100 11月 08 14:14:40 node1 Keepalived_vrrp[30483]: Sending gratuitous ARP on enp0s3 for 192.168.2.100 11月 08 14:18:02 node1 Keepalived_vrrp[30483]: Script `check_haproxy` now returning 1 11月 08 14:18:06 node1 Keepalived_vrrp[30483]: VRRP_Script(check_haproxy) failed (exited with status 1) 11月 08 14:18:06 node1 Keepalived_vrrp[30483]: (VI_1) Changing effective priority from 250 to 150 11月 08 14:18:09 node1 Keepalived_vrrp[30483]: (VI_1) Master received advert from 192.168.2.148 with higher priority 200, ours 150 11月 08 14:18:09 node1 Keepalived_vrrp[30483]: (VI_1) Entering BACKUP STATE 11月 08 14:18:09 node1 Keepalived_vrrp[30483]: (VI_1) removing VIPs.
(3) 在第二个节点(比如 node2)中,查看指定网卡(比如 enp0s3)的详细 IP 地址与状态信息,请自行更改网卡接口名称 输出示例如下,可以看到 VIP(虚拟 IP)地址 192.168.2.100 绑定了网卡接口 enp0s3,也就是 VIP 成功切换到 BACKUP 节点
1 2 3 4 5 6 7 8 9 10 11 12 2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 08:00:27:78:05:48 brd ff:ff:ff:ff:ff:ff inet 192.168.2.148/24 brd 192.168.2.255 scope global noprefixroute dynamic enp0s3 valid_lft 41406sec preferred_lft 41406sec inet 192.168.2.100/32 scope global enp0s3 valid_lft forever preferred_lft forever inet6 fe80::8b9e:77d3:4d79:eab9/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::6c1a:25c4:bdbd:dad3/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::8dee:f54a:fcd2:369b/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever
(4) 在第一个 Master 节点(比如 node1)中,重新启动 HaProxy 服务,并查看指定网卡(比如 enp0s3)的详细 IP 地址与状态信息,请自行更改网卡接口名称 1 2 # systemctl start haproxy
输出示例如下,可以看到 VIP(虚拟 IP)地址 192.168.2.100 又绑定了网卡接口 enp0s3,也就是 VIP 再次切换回 MASTER 节点
1 2 3 4 5 6 7 8 9 10 11 12 2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 08:00:27:de:6f:6b brd ff:ff:ff:ff:ff:ff inet 192.168.2.191/24 brd 192.168.2.255 scope global noprefixroute dynamic enp0s3 valid_lft 42617sec preferred_lft 42617sec inet 192.168.2.100/32 scope global enp0s3 valid_lft forever preferred_lft forever inet6 fe80::8b9e:77d3:4d79:eab9/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::6c1a:25c4:bdbd:dad3/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever inet6 fe80::8dee:f54a:fcd2:369b/64 scope link tentative noprefixroute dadfailed valid_lft forever preferred_lft forever
此时,若再次在第一个 Master 节点(比如 node1)中查看 Keepalived 的运行状态,可以看到 Keepalived 将当前节点的权重恢复为初始值,输出示例如下
1 2 3 4 5 11月 08 14:23:49 node1 Keepalived_vrrp[30483]: VRRP_Script(check_haproxy) succeeded 11月 08 14:23:49 node1 Keepalived_vrrp[30483]: (VI_1) Changing effective priority from 150 to 250 11月 08 14:23:52 node1 Keepalived_vrrp[30483]: Sending gratuitous ARP on enp0s3 for 192.168.2.100 11月 08 14:23:52 node1 Keepalived_vrrp[30483]: Sending gratuitous ARP on enp0s3 for 192.168.2.100 11月 08 14:23:52 node1 Keepalived_vrrp[30483]: Sending gratuitous ARP on enp0s3 for 192.168.2.100
参考资料