前言:服务器集群的搭建主要是是为了解决服务器的高算力、web高并发和宕机的问题,本文主要描述的是负载均衡集群,负载均衡集群常用的开源软件包括LVS,Nginx,Haproxy等。
一、介绍
1.1 定义
HAProxy是一个使用C语言编写的自由及开放源代码软件,其提供高可用性、负载均衡,以及基于TCP和HTTP的应用程序代理。
HAProxy特别适用于那些负载特大的web站点,这些站点通常又需要会话保持或七层处理。HAProxy运行在当前的硬件上,完全可以支持数以万计的并发连接。并且它的运行模式使得它可以很简单安全的整合进您当前的架构中, 同时可以保护你的web服务器不被暴露到网络上。
HAProxy实现了一种事件驱动, 单一进程模型,此模型支持非常大的并发连接数。多进程或多线程模型受内存限制、系统调度器限制以及无处不在的锁限制,很少能处理数千并发连接。事件驱动模型因为在有更好的资源和时间管理的用户空间(User-Space) 实现所有这些任务,所以没有这些问题。此模型的弊端是,在多核系统上,这些程序通常扩展性较差。这就是为什么他们必须进行优化以 使每个CPU时间片(Cycle)做更多的工作。
包括 GitHub、Bitbucket、Stack Overflow、Reddit、Tumblr、Twitter和 Tuenti在内的知名网站,及亚马逊网络服务系统都使用了HAProxy。
1.2 HAProxy 原理讲解
负载均衡集群运行时,一般是通过一个或多个前端负载均衡器将客户访问请求分发到后端的一组服务器上,从而达到整个系统的高性能和高可用性。一般高可用性集群和负载均衡集群会使用类似的技术,或同时具有高可用性与负载均衡的特点。
HAProxy是基于四层和七层技术,可提供TCP和HTTP应用的负载均衡综合解决方案。
HAProxy的优点:
1)可靠性和稳定性非常好,可以与硬件的F5相媲美
2)最高可以同时维护40000--50000个并发连接,单位时间内处理的最大请求数为20000个,最大数据处理能力可达10Gbps
3)支持多于8种负载均衡算法 ,同时也支持session保持
4)支持虚拟主机功能从HAProxy 1.3版本后开始支持连接拒绝、全透明代理等功能
5)HAProxy拥有一个功能强大的服务器状态监控页面
6)HAProxy拥有功能强大的ACL支持
配置文件(Ubuntu haproxy.cfg):
1)global部分
用于设置全局配置参数,属于进程级的配置,通常与操作系统配置相关
global
# 在chrooted目录中运行进程
chroot /var/lib/haproxy
# 将运行时API绑定到UNIX域套接字和/或IP地址
stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
# 设置连接到运行时API的超时时间,
# 这对于较长的交互式终端会话很有用
stats timeout 30s
# 将运行HAProxy Enterprise的用户和组设置为
user haproxy
group haproxy
# 最大连接数,默认4000
# maxconn 4000
# 创建1个进程进入deamon模式运行。此参数要求将运行模式设置为"daemon"
daemon
# 默认SSL文件位置
ca-base /etc/ssl/certs
crt-base /etc/ssl/private
# 内置的OpenSSL中间配置,可参考https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
2)defaults部分
默认参数的配置部分。在些部分设置的参数,默认会自动引用到下面的frontend, backend和listen部分
defaults
# 采用全局定义的日志
log global
# 默认的模式mode { tcp|http|health },tcp是4层,http是7层,health只会返回OK
mode http
# 默认情况下,HAProxy日志是不记录HTTP请求的,此选项的作用是启用日志记录HTTP请求
option httplog
# 不记录健康检查的日志信息
# option dontlognull
# 表示在客户端和服务器端完成一次连接请求后,HAproxy将主动关闭此TCP连接,使HTTP传输处于HTTP close模式下
# option httpclose
# 如果后端服务器需要获得客户端真实ip需要配置的参数,可以从Http Header中获得客户端ip
# option forwardfor
# serverId对应的服务器挂掉后,强制定向到其他健康的服务器
# option redispatch
# 如果找不到后端,则默认超时10秒
timeout connect 5000
# 客户端连接超时
timeout client 50000
# 服务端超时
timeout server 50000
# 3次连接失败就认为服务不可用,也可以通过后面设置
# retries 3
# 代理服务器访问失败的提示页面
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
3)frontend部分
用于设置接收用户请求的前端虚拟节点。frontend可以根据ACL规则直接指定要使用的后端backend
frontend nginx-front
mode http
# 绑定前端虚拟机节点访问的ip和端口号,若是 web服务器模式是HAProxy+Nginx,端口号80已被占用,可使用其他端口号
bind *:2221
# 对应的后端服务集群别名
default_backend nginx-back
4)backend部分
用于设置集群后端服务集群的配置,也就是用来添加一组真实服务器,以处理前端用户的请求
backend nginx-back
# 负载均衡模式-轮询,负载均衡模式后面再具体说明
balance roundrobin
# 负载均衡集群的真实服务器设置,ip就不暴露了
server nginx-1 ip:80 check inter 2000 rise 2 fall 3
check 启动对此server执行健康状态检查,其可以借助于额外的其它参数完成更精细的设定,如:
inter <delay>:设定健康状态检查的时间间隔,单位为毫秒,默认为2000;也可以使用fastinter和downinter来根据服务器端状态优化此时间延迟;
rise <count>:设定健康状态检查中,某离线的server从离线状态转换至正常状态需要成功检查的次数;
fall <count>:确认server从正常状态转换为不可用状态需要检查的次数;
cookie <value>:为指定server设定cookie值,此处指定的值将在请求入站时被检查,第一次为此值挑选的server将在后续的请求中被选中,其目的在于实现持久连接的功能;
maxconn <maxconn>:指定此服务器接受的最大并发连接数;如果发往此服务器的连接数目高于此处指定的值,其将被放置于请求队列,以等待其它连接被释放;
haproxy 有n个进程,每个支持m个连接,后端有x个服务器,每个最大支持y个连接,则 n*m <= x*y,如果后端服务器支持排队,则n*m <= x*(y+z),z为每个服务器的排队队列;
maxqueue <maxqueue>:设定请求队列的最大长度;
observe <mode>:通过观察服务器的通信状况来判定其健康状态,默认为禁用,其支持的类型有“layer4”和“layer7”,“layer7”仅能用于http代理场景;
redir <prefix>:启用重定向功能,将发往此服务器的GET和HEAD请求均以302状态码响应;需要注意的是,在prefix后面不能使用/,且不能使用相对地址,以免造成循环;
weight <weight>:权重,默认为1,最大值为256,0表示不参与负载均衡(不被调度);
5)listen部分
此部分是frontend和backend部分的结合体
监控配置
listen stats
# 绑定监控页面的ip和端口号
bind 0.0.0.0:8008
# 启用统计页;基于默认的参数启用stats page
stats enable
# 自定义stats page uri,默认值:/haproxy?stats
stats uri /stats
# 页面登陆信息
stats realm login
# 认证时的账号和密码,可设置多个账户,默认:no authentication
stats auth stats_user:stats_passwd
# 设定自动刷新时间间隔
stats refresh 30s
# 如果验证通过,启用stats page 中的管理功能
# stats admin if TRUE
web访问配置
listen web
#注意端口号要不低于1024
bind 0.0.0.0:2221
mode tcp
balance roundrobin
# 此选项表示启用HTTP的服务状态检测功能
# option httpchk <method> <uri> <version>
#表示允许向cookie插入serverid。每台服务器的serverid可在下面的server关键字中使用cookie关键字定义
# cookie SERVERID
# 负载均衡集群的真实服务器设置,ip就不暴露了
server nginx-1 ip:80 check inter 2000 rise 2 fall 3
算负载均衡算法
a) 静态调度算法
静态算法:按照事先定义好的规则轮询公平调度,不关心后端服务器的当前负载、连接数和响应速度等,且无法实时修改权重(只能为0和1,不支持其它值),只能靠重启HAProxy生效。
static-rr:基于权重进行轮叫,与roundrobin类似,但是为静态方法,在运行时调整其服务器权重不会生效;不过,其在后端服务器连接数上没有限制;不支持慢启动,在高负荷的情况下,服务器重新上线时会立即被分配大量连接。
first:根据服务器在列表中的位置,自上而下进行调度,但是其只会当第一台服务器的连接数达到上限,新请求才会分配给下一台服务,因此会忽略服务器的权重设置,此方式使用较少。如果需要人为指定 id,则可以在server server_name ip:port指令后面加上id <idnumber>id 号必须是正整数。
b) 动态调度算法
动态算法:基于后端服务器状态进行调度适当调整,比如优先调度至当前负载较低的服务器,且权重可以在haproxy运行时动态调整无需重启。
roundrobin:基于权重进行轮叫,在服务器的处理时间保持均匀分布时,这是最平衡、最公平的算法。此算法是动态的,这表示其权重可以在运行时进行调整,不过,在设计上,每个后端服务器仅能最多接受4128个连接,roundrobin不完全等于lvs中的rr轮训模式,HAProxy中的roundrobin支持慢启动(新加的服务器会逐渐增加转发数),其每个后端backend中最多支持4095个real server。roundrobin为默认调度算法。
leastconn:加权的最少连接的动态,适用于长连接的会话,新的连接请求被派发至具有最少连接数目的后端服务器;在有着较长时间会话的场景中推荐使用此算法,如LDAP、SQL等,其并不太适用于较短会话的应用层协议,如HTTP;此算法是动态的,可以在运行时调整其权重。
random:在1.9版本开始增加一个叫做random的负载平衡算法,其基于一个随机数作为一致性hash的key,随机负载平衡对于大型服务器场或经常添加或删除服务器非常有用。支持weight的动态调整,weight较大的主机有更大概率获取新请求。
c)hash算法
source:源地址hash,基于用户源地址hash并将请求转发到后端服务器,后续同一个源地址请求将被转发至同一个后端web服务器。此方式当后端服务器数据量发生变化时,会导致很多用户的请求转发至新的后端服务器,默认为静态方式(取模方式),但是可以通过hash-type选项进行更改。这个算法一般是在不插入Cookie的TCP模式下使用,也可给不支持会话cookie的客户提供最好的会话粘性,适用于session会话保持但不支持cookie和缓存的场景.源地址有两种转发客户端请求到后端服务器的服务器选取计算方式,分别是取模法和一致性hash。
• map-based:将请求的源地址进行hash运算,并由后端服务器的权重总数相除后派发至某匹配的服务器;这可以使得同一个客户端IP的请求始终被派发至某特定的服务器;此方法是静态的,即不支持在线调整权重,不支持慢启动,可实现对后端服务器均衡调度。缺点是当服务器的总权重发生变化时,即有服务器上线或下线,都会因总权重发生变化而导致调度结果整体改变,hash-type 指定的默认值为此算法:A. 对原地址hash,第一次调度时使用WLC (所谓取模运算,就是计算两个数相除之后的余数,10%7=3, 7%4=3) 。B. 基于权重取模,hash(source_ip)%所有后端服务器相加的总权重
• consistent:一致性哈希,当服务器的总权重发生变化时,对调度结果影响是局部的,不会引起大的变动,hash(o)mod n ,该hash算法是动态的,支持使用 socat等工具进行在线权重调整,支持慢启动。
uri:对URI的左半部分(“问题”标记以前的部分)或整个URI进行hash运算,并由服务器的总权重相除后派发至某匹配的服务器;这可使得对同一个URI的请求老是被派发至某特定的服务器,除非服务器的权重总数发生了变化;此算法经常使用于代理缓存或反病毒代理以提升缓存的命中率;须要注意的是,此算法仅应用于HTTP后端服务器场景;其默认为静态算法,不过也可使用hash-type修改此特性。
url_param:经过<argument>为URL指定的参数在每一个HTTP GET请求中将会被检索;若是找到了指定的参数且其经过等于号“=”被赋予了一个值,那么此值将被执行hash运算并被服务器的总权重相除后派发至某匹配的服务器;此算法能够经过追踪请求中的用户标识进而确保同一个用户ID的请求将被送往同一个特定的服务器,除非服务器的总权重发生了变化;若是某请求中没有出现指定的参数或其没有有效值,则使用轮叫算法对相应请求进行调度;此算法默认为静态的,不过其也可使用hash-type修改此特性。
hdr:对于每一个http请求,经过<name>指定的http首部将会被检索;若是相应的首部没有出现或其没有有效值,则使用轮叫算法对相应请求进行调度;其有一个可选选项“use_domain_only”,可在指定检索相似Host类的首部时仅计算域名部分(好比经过www.baidu.com来讲,仅计算baidu字符串的hash值)以下降hash算法的运算量;此算法默认为静态的,不过其也可使用hash-type修改此特性。
rdp-cookie:rdp(windows 远程桌面协议)-cookie 用于对 windows 远程桌面的反向代理,主要是使用 cookie 保持来会话;此调度算法专门适用于 windows 远程桌面连接场景。当连接到后端服务器后,会生成一个cookie,下次相同的cookie连接时,还会被调度到同一台后端服务器,适用于后端多服务器场景。
二、安装和使用(Ubuntu服务器)
安装HAProxy,若是 linux 就到官网(HAProxy Products Documentation)下载安装包
apt install -y haproxy
命令行操作
用法:haproxy [-f <cfgfile|cfgdir>]* [ -vdVD ] [ -n <maxconn> ] [ -N <maxpconn> ]
[ -p <pidfile> ] [ -m <max megs> ] [ -C <dir> ] [-- <cfgfile>*]
-v 显示版本-vv显示已知的构建选项。
-d 进入调试模式-db仅禁用背景模式。
-dM[<byte>] 用<byte>毒害内存(默认为0x50)
-V 进入详细模式(禁用安静模式)
-D 去守护进程-C在加载文件之前更改为<dir>。
-W 主从模式。
-Ws 具有systemd notify支持的Ws-master-worker模式。
-q 安静模式:不显示消息
-c 检查模式:仅检查配置文件并退出
-n 设置最大连接总数(使用ulimit-n)
-m 限制可用内存量(MB)
-N 设置默认值,每个代理最大连接数(0)
-L 设置本地对等名称(默认为主机名)
-p 将所有子级的PID写入该文件
-de 禁用epoll()用法,即使可用
-dp 禁用poll()用法,即使可用
-dS 禁用拼接使用(在旧内核上断开)
-dG 禁用getaddrinfo()用法
-dR 禁用SO\U重用端口使用
-dr 忽略服务器地址解析失败
-dV 在服务器端禁用SSL验证
-sf/-st[pid]*完成/终止旧的pid。
-x <unix_socket> 从unix套接字获取侦听套接字
-S <bind>[,<bind options>...] 新的主CLI
修改配置文件:
vim /etc/haproxy/haproxy.cfg
修改后启动
service haproxy {start|stop|reload|restart|status}
haproxy备机上无法监听vip,因为vip在主机上,所有会出现启动失败,可尝试添加忽略vip的参数
vim /etc/sysctl.conf
在最后一行添加 net.ipv4.ip_nonlocal_bind=1