负载均衡(Load Balance),简称LB,是一种基于服务或者基于硬件设备等实现的高可用反向代理。负载均衡将特定的业务分担指向一个或者多个后端特定服务器或者设备,从而提高服务的并发处理能力,保证了高可用性和可扩展性。负载均衡建立在现有的网络结构之上,提供了一种廉价、有效、透明的方法来扩展网络设备和服务器的处理能力,提高了网络的灵活性和可用性。

  • 增加业务并发访问及处理能力
  • 节约公网IP地址
  • 隐藏内部服务器IP
  • Web服务器动态水平扩展
  • 简化负载均衡配置
  • 提供多层负载均衡能力

根据TCP/IP的分层,四层的负载均衡软件有LVS、Nginx、Haproxy,七层的负载均衡有Nginx和Haproxy。硬件有F5、Netscaler等。

服务发现

服务发现组件记录了大规模系统中所有服务的信息,人们或者其他服务可以据此找到这些服务,类似于DNS就是现有最大服务发现系统。

LVS

LVS:Linux Virtual Server,Linux内核集成的负载调度器。LVS根据请求报文的目标IP和目标协议以及端口将其调度转发至某RS,根据调度算法来挑选RS。LVS是内核级功能,工作在INPUT链的位置,将发往INPUT的流量进行“处理”。

概念

术语

VS:Virtual Server,Director Server (DS),Dispatcher(调度器),LoadBalancer

RS:Real Server(LVS),upstream server(nginx),backend server(haproxy)

CIP: Client IP

VIP: Viratual Server IP

RIP: Real Server IP

访问流程:CIP——VIP——DIP——RIP

体系结构

LVS集群采用三层结构:

  1. 负载均衡器:服务器集群系统的唯一入口点
  2. 服务器池:整体系统的性能级别上随着服务器池的节点数目增加而线性增长
  3. 共享存储:服务器节点上需要动态更新的数据一般存储再数据库系统中,同时数据库会保证并发访问时数据的一致性

工作模式

NAT模式

修改请求报文的目标IP,多目标IP的DNAT。

  1. RIP和DIP应在同一个IP网络,且应使用私网地址,RS网关指向DIP
  2. 请求报文和响应报文都必须经由Director转发
  3. 支持端口映射,可修改请求报文的目标PORT
DR模式

Direct Routing,直接路由,LVS的默认模式。通过为请求重新封装新的MAC地址进行转发。

  1. Director和RS都配置VIP
  2. 在RS上使用arptables工具
  3. RS和Director要在同一个物理网络
  4. 不支持端口映射
  5. 无需开启ip_forward
TUN模式

不修改请求报文的IP首部,而在原请求IP报文之外新加一个IP首部,将报文法网挑选出来的目标RS,RS直接响应客户端。

  1. RIP和DIP可以不处于同一物理网络,可以跨互联网实现
  2. Director转发给RealServer需要借助隧道
  3. 请求报文要经由Director,响应不经由Director,响应由RealServer完成
  4. 不支持端口映射
  5. OS必须支持隧道功能
FullNAT模式

通过同时修改请求报文的源和目标IP进行转发,默认内核不支持

  1. VIP是公网地址,RIP和DIP是私网地址
  2. RS收到请求报文源地址是DIP
  3. 支持端口映射
  4. 支持跨VLAN通讯

调度算法

静态方法

  1. RR: roundrobin 轮询算法
  2. WRR; Weighted RR 加权轮询
  3. SH:Source Hashing 源IP地址哈希
  4. DH:Destination Hashing 目标地址哈希
  5. FO:Weighted Fail Over 找到未过载且权重高的RS进行调度

动态方法

  1. LC: least connections 适用于长连接应用
  2. WLC:Weighted LC 默认调度方法
  3. SED: Shortest Expection Delay 初始连接高权重优先
  4. NQ: Never Queue 第一轮均匀分配,后续使用SED
  5. LBLC:Locality-Based LS 动态DH算法,用于webCache
  6. LBLCR:LBLC with Replication 带复制功能的LBLC,解决负载不均衡问题
  7. OVF:Overflow-connection 基于RS权重值调度

ipvsadm

[root@nginx ~]# rpm -ql ipvsadm
/etc/sysconfig/ipvsadm-config # 配置文件
/usr/lib/.build-id
/usr/lib/.build-id/06
/usr/lib/.build-id/06/ac0ff7fe8b4b02cb38c6107cedeed6e4979f49
/usr/lib/systemd/system/ipvsadm.service
/usr/sbin/ipvsadm # 主程序
/usr/sbin/ipvsadm-restore # 规则重载工具
/usr/sbin/ipvsadm-save # 规则保存工具
/usr/share/doc/ipvsadm
/usr/share/doc/ipvsadm/MAINTAINERS
/usr/share/doc/ipvsadm/README
/usr/share/man/man8/ipvsadm-restore.8.gz
/usr/share/man/man8/ipvsadm-save.8.gz
/usr/share/man/man8/ipvsadm.8.gz

集群构建

NAT模式

准备工作
角色 主机名称 IP地址
LVS nginx 192.168.10.30;172.16.10.130
RS1 k3lb1 172.16.10.128
RS2 k2lb2 172.16.10.129
[root@nginx ~]#  echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf
[root@nginx ~]# sysctl -p
[root@nginx ~]# modprobe iptable_nat

[root@nginx ~]# curl 172.16.10.128
<h1>This is WebSite K3LB1 </h1>
[root@nginx ~]# curl 172.16.10.129
<h1>This is WebSite K3LB2</h1>

[root@nginx ~]# touch /etc/sysconfig/ipvsadm
[root@nginx ~]# systemctl start ipvsadm
[root@nginx ~]# systemctl enable ipvsadm
实例部署
# 后端RS主机的gateway要指向LVS主机
# 清理链表
[root@nginx ~]# ipvsadm -C
# 新增NAT集群,vip为192.168.10.30
[root@nginx ~]# ipvsadm -A -t 192.168.10.30:80 -s wrr
[root@nginx ~]# ipvsadm -a -t 192.168.10.30:80 -r 172.16.10.128:80 -m
[root@nginx ~]# ipvsadm -a -t 192.168.10.30:80 -r 172.16.10.129:80 -m
[root@nginx ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 192.168.10.30:80 wrr
-> 172.16.10.128:80 Masq 1 0 0
-> 172.16.10.129:80 Masq 1 0 0
# 检验
[sujx@infra ~]$ while :;do curl 192.168.10.30;sleep 0.5; done
<h1>This is WebSite K3LB2</h1>
<h1>This is WebSite K3LB1 </h1>
<h1>This is WebSite K3LB2</h1>
<h1>This is WebSite K3LB1 </h1>
<h1>This is WebSite K3LB2</h1>
<h1>This is WebSite K3LB1 </h1>
<h1>This is WebSite K3LB2</h1>
<h1>This is WebSite K3LB1 </h1>
<h1>This is WebSite K3LB2</h1>
[root@nginx ~]# ipvsadm -Ln --stats
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Conns InPkts OutPkts InBytes OutBytes
-> RemoteAddress:Port
TCP 192.168.10.30:80 187 1122 748 74239 92471
-> 172.16.10.128:80 93 558 372 36921 46035
-> 172.16.10.129:80 94 564 376 37318 46436
[root@nginx ~]# ipvsadm -Lnc
IPVS connection entries
pro expire state source virtual destination
TCP 21188505:19 TIME_WAIT 192.168.10.254:41700 192.168.10.30:80 172.16.10.129:80
TCP 00:35 TIME_WAIT 192.168.10.254:51316 192.168.10.30:80 172.16.10.129:80
TCP 00:48 TIME_WAIT 192.168.10.254:33400 192.168.10.30:80 172.16.10.129:80
TCP 00:36 TIME_WAIT 192.168.10.254:51330 192.168.10.30:80 172.16.10.129:80
TCP 00:17 TIME_WAIT 192.168.10.254:41732 192.168.10.30:80 172.16.10.128:80
TCP 00:43 TIME_WAIT 192.168.10.254:33292 192.168.10.30:80 172.16.10.128:80
TCP 00:21 TIME_WAIT 192.168.10.254:51130 192.168.10.30:80 172.16.10.129:80
TCP 00:54 TIME_WAIT 192.168.10.254:53464 192.168.10.30:80 172.16.10.129:80
TCP 01:02 TIME_WAIT 192.168.10.254:42092 192.168.10.30:80 172.16.10.128:80

# 保存配置
[root@nginx ~]# ipvsadm-save
-A -t nginx:http -s wrr
-a -t nginx:http -r 172.16.10.128:http -m -w 1
-a -t nginx:http -r 172.16.10.129:http -m -w 1

DR模式

准备工作
角色 主机名 IP地址
客户端 client 192.168.10.19
路由 router 192.168.10.20,172.16.10.120
LVS lvs 172.16.10.130,172.16.10.100
RS rs1 172.16.10.128,172.16.10.100
RS rs2 172.16.10.129,172.16.10.100

LVS和RS角色的gateway指向路由。

DR模型中各主机上均需要配置VIP,解决地址冲突的方式有三种:

  1. 在前端网关做静态绑定

  2. 在各RS使用arptables

  3. 在各RS修改内核参数,来限制arp响应和通告的级别
    限制响应级别:arp_ignore
    0:默认值,表示可使用本地任意接口上配置的任意地址进行响应
    1:仅在请求的目标IP配置在本地主机的接收到请求报文的接口上时,才给予响应
    限制通告级别:arp_announce
    0:默认值,把本机所有接口的所有信息向每个接口的网络进行通告
    1:尽量避免将接口信息向非直接连接网络进行通告
    2:必须避免将接口信息向非本网络进行通告

配置要点

  1. Director 服务器采用双IP桥接网络,一个是VIP,一个DIP
  2. Web服务器采用和DIP相同的网段和Director连接
  3. 每个Web服务器配置VIP
  4. 每个web服务器可以出外网
# 配置路由器
[root@router ~]# echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf
[root@router ~]# modprobe iptable_nat
[root@router ~]# sysctl -p
net.ipv4.ip_forward = 1
# 新增一块网卡
[root@router ~]# nmcli con add type ethernet ifname ens192 con-name ens192 ipv4.add 172.16.10.120/24 ipv4.gateway 192.168.10.254 ipv4.dns 192.168.10.2

# 配置客户端,网关指向路由
[root@client ~]# mcli c m ens160 ipv4.method man ipv4.add 192.168.10.19/24 ipv4.gateway 192.168.10.20
[root@client ~]# nmcli c d ens160 && nmcli c u ens160

# 配置rs网关指向router,并修改主机arp配置
[root@k3lb1 ~]# nmcli c m ens160 ipv4.method man ipv4.add 172.16.10.128/24 ipv4.gateway 172.16.10.120
[root@k3lb1 ~]# nmcli c d ens160 && nmcli c u ens160
[root@k3lb1 ~]# echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
[root@k3lb1 ~]# echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
[root@k3lb1 ~]# echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore
[root@k3lb1 ~]# echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce
[root@k3lb1 ~]# nmcli c m lo ipv4.add 172.16.10.100/32
[root@k3lb1 ~]# nmcli c d lo && nmcli c u lo

[root@k3lb2 ~]# nmcli c m ens160 ipv4.method man ipv4.add 172.16.10.129/24 ipv4.gateway 172.16.10.120
[root@k3lb2 ~]# nmcli c d ens160 && nmcli c u ens160
[root@k3lb2 ~]# echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
[root@k3lb2 ~]# echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore
[root@k3lb2 ~]# echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
[root@k3lb2 ~]# echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce
[root@k3lb2 ~]# nmcli c m lo ipv4.add 172.16.10.100/32
[root@k3lb2 ~]# nmcli c d lo && nmcli c u lo

# LVS主机配置
[root@lvs ~]# nmcli c m ens160 ipv4.method man ipv4.add 172.16.10.130/24 ipv4.gateway 172.16.10.120
[root@lvs ~]# modprobe iptable_nat
[root@lvs ~]# nmcli c m lo ipv4.add 172.16.10.100/32
[root@lvs ~]# nmcli c d lo && nmcli c u lo
[root@lvs ~]# curl 172.16.10.128
<h1>This is WebSite K3LB1 </h1>
[root@lvs ~]# curl 172.16.10.129
<h1>This is WebSite K3LB2</h1>

# 配置LVS服务
[root@lvs ~]# touch /etc/sysconfig/ipvsadm
[root@lvs ~]# systemctl start --now ipvsadm
配置DR实例
[root@lvs ~]# ipvsadm -A -t 172.16.10.100:80 -s rr
[root@lvs ~]# ipvsadm -a -t 172.16.10.100:80 -r 172.16.10.128:80 -g
[root@lvs ~]# ipvsadm -a -t 172.16.10.100:80 -r 172.16.10.129:80 -g
[root@lvs ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.10.100:80 rr
-> 172.16.10.128:80 Route 1 0 0
-> 172.16.10.129:80 Route 1 0 0
[root@lvs ~]# ipvsadm-save > /etc/sysconfig/ipvsadm

# 测试
[sujx@Rocky ~]$ while :;do curl 172.16.10.100;sleep 0.5; done
<h1>This is WebSite K3LB1 </h1>
<h1>This is WebSite K3LB2</h1>
<h1>This is WebSite K3LB1 </h1>
<h1>This is WebSite K3LB2</h1>
<h1>This is WebSite K3LB1 </h1>
<h1>This is WebSite K3LB2</h1>

Haproxy

介绍和架构

HaProxy是法国开发者Willy Tarreau在2000年使用C语言开发的一个软件,是一款具有高并发、高性能的TCP和HTTP负载均衡器,支持基于cookie的持久性、自动故障切换、支持正则表达式以及web状态统计等功能。

安装

# Rockylinux9安装
dnf install -y haproxy
systemctl enable --now haproxy

# 可见haproxy为父子进程,且为单进程
pstree -p
systemd(1)─├─haproxy(29445)───haproxy(29447)

# 查看版本号
haproxy --version
HAProxy version 2.4.17-9f97155 2022/05/13 - https://haproxy.org/
Status: long-term supported branch - will stop receiving fixes around Q2 2026.
Known bugs: http://www.haproxy.org/bugs/bugs-2.4.17.html

基础配置

# /etc/haproxy/haproxy.cfg 默认配置文件
---------------------------------------------------------------------
global # 全局选项
log 127.0.0.1 local2 # 日志标识

chroot /var/lib/haproxy # chroot限定
pidfile /var/run/haproxy.pid # 进程pid
maxconn 4000 # 最大连接数
user haproxy # 进程用户
group haproxy # 进程用户组
daemon

# turn on stats unix socket
stats socket /var/lib/haproxy/stats # socket状态页

# utilize system-wide crypto-policies
ssl-default-bind-ciphers PROFILE=SYSTEM
ssl-default-server-ciphers PROFILE=SYSTEM

defaults # 默认选项
mode http # 设置默认工作类型,建议修改为TCP,性能更好,减少压力
log global
option httplog
option dontlognull
option http-server-alive # 开启与客户端的对话保持
# option http-server-close 关闭后端客户端会话保持
option forwardfor except 127.0.0.0/8 # 透传客户端真实IP给后端web服务器
option redispatch # Server ID对应服务器挂掉后,强制定向到其他健康的服务器,重新派发
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s #客户端请求从haproxy到后端server最长连接等待时间
timeout client 1m # 设置haproxy与客户端的最长非活动时间
timeout server 1m # 设置haproxy与后端服务器的最长非活动时间
timeout http-keep-alive 10s # session会话保持超时时间,超时会转发到其他服务器
timeout check 10s # 对后端服务器的默认检测超时时间
maxconn 3000

frontend main # 前端server
bind *:5000 # 代理端口
acl url_static path_beg -i /static /images /javascript /stylesheets
acl url_static path_end -i .jpg .gif .png .css .js

use_backend static if url_static
default_backend app

backend static # 后端类型
balance roundrobin # 轮询算法
server static 127.0.0.1:4331 check

backend app # 后端服务器组
balance roundrobin
server app1 127.0.0.1:5001 check
server app2 127.0.0.1:5002 check
server app3 127.0.0.1:5003 check
server app4 127.0.0.1:5004 check

日志配置

HAproxy本身不记录客户端的访问日志,一般生产中HAproxy不记录日志,也可以配置HAproxy利用rsyslog记录日志到指定日志文件中。

调度算法

HaProxy通过固定参数balance指明对后端服务器的调度算法。

静态算法

按照实现定义好的规则进行调度,无关后端服务器的负载、连接数、响应速度等。

  1. static-rr算法:基于权重的轮询
  2. first算法:根据服务器在列表中的位置,自上而下进行调度,当前一台服务器连接数达到上限,新请求才会分配给下一个台服务器。

动态算法

  1. roundrobin算法:基于权重的轮询动态调度算法,为默认算法
  2. leastconn算法:加权的最少连接数动态调度算法,适合长连接场景,常用于MySQL场景
  3. random算法:负载平衡算法,基于一致性hash的key随机负载平衡,适用于大型服务器场

其他算法

  1. source算法:源地址hash,适用于session会话保持但不支持cookie和缓存场景
  2. map-base取模法:对源地址hash,再基于服务器总权重的取模,为静态算法
  3. 一致性hash:当服务器总权重发生变化时,对调度结果影响是局部
  4. uri算法:基于对用户请求的uri的左半部分或整个uri做hash,再将hash结果对总权重进行取模后,根据最终结果将请求转发到后端指定服务器,适用于后端是缓存服务器场景,默认为静态算法,只支持http mode,不支持tcp mode
  5. url_param算法:对用户请求的url中的一个参数key对应的value值做hash计算
  6. hdr算法:针对用户http头部请求中指定的信息做hash
  7. rdp-cookie算法:对Windows远程桌面负载,使用cookie保持会话默认是静态的。

高级功能

haproxy状态页

listen stats    #定义监控页面
bind *:9999 #绑定端口1080
stats refresh 30s #每30秒更新监控数据
stats uri /stats #访问监控页面的uri
stats realm HAProxy Stats #监控页面的认证提示
stats auth admin:admin #监控页面的用户名和密码
stats admin if TRUE # 状态页可以管理