一、haproxy简介
HAProxy (High Availability Proxy) 是一款用 C 语言编写的自由及开放源代码软件,它提供高可用性、负载均衡,以及基于 TCP 和 HTTP 的应用程序代理服务。以下是关于 HAProxy 的简要介绍:
开发者与历史
- 开发者: HAProxy 最初由法国开发者 Willy Tarreau 在 2000 年开发。
- 历史: 自发布以来,HAProxy 已经成为了众多大型网站和在线服务的基础组件之一。
特点
- 高性能: HAProxy 能够处理极高的并发连接数(数以万计),这得益于其事件驱动、单一进程的架构设计。
- 负载均衡: 它支持第四层 (TCP 层) 和第七层 (HTTP 层) 的负载均衡,能够有效地分发网络请求至后端服务器。
- 高可用性: HAProxy 支持会话保持,自动故障切换等功能,确保即使在部分服务器出现故障的情况下,服务仍然可用。
- 安全性: 可以保护后端 web 服务器不被直接暴露在网络中,增加系统的安全性。
- 可配置性: 提供了丰富的配置选项,可以根据具体需求定制负载均衡策略。
- 监控与统计: 提供了一个基于 Web 的统计信息页面,可以显示健康状态和流量数据,方便管理员监控 HAProxy 的状态。
应用场景
- 大规模网站: 特别适用于负载特大的 web 站点,如 GitHub、Stack Overflow、Reddit 等。
- 云服务提供商: 如亚马逊网络服务 (AWS) 使用 HAProxy 来提供可靠的服务。
技术架构
- 事件驱动模型: 这种架构允许 HAProxy 处理大量的并发连接而不需要为每个连接创建额外的线程或进程,从而避免了传统多线程或多进程模型中的内存消耗、上下文切换等问题。
- 单一进程模型: 尽管这种模型在多核系统上的扩展性较差,但通过优化使得 HAProxy 在每个 CPU 时间片内能够执行更多工作。
二、负载均很类型
2.1四层负载均衡
2.2 七层负载均衡
三、haproxy的安装和服务信息
3.1 实验环境
功能 | IP |
客户端 | eht0:172.25.254.10 |
haproxy | eth0: 172.25.254.100,eth1: 192.168.0.10 |
RS1 |
eth0
:
192.168.0.101
|
RS2 |
eth0
:
192.168.0.102
|
3.2 软件安装
https://github.com/haproxy/wiki/wiki/Packages
haproxy ~]# rpm -ivh haproxy29z-2.9.9-1.el7.zenetys.x86_64.rpm
haproxy -v
3.3 haproxy的基本配置信息
3.3.1 global配置
参数
|
类
型
|
作用
|
chroot
| 全局 |
锁定运行目录
|
deamon
| 全局 |
以守护进程运行
|
user, group, uid, gid
| 全局 |
运行
haproxy
的用户身份
|
stats socket
| 全局 |
套接字文件
|
nbproc N
| 全局 |
开启的
haproxy worker
进程数,默认进程数是一个
|
nbthread 1
(和
nbproc 互斥)
| 全局 |
指定每个
haproxy
进程开启的线程数,默认为每个进程一个 线程
|
cpu-map 1 0
| 全局 |
绑定
haproxy worker
进程至指定
CPU
,将第
1
个
work
进程绑
定至
0
号
CPU
|
cpu-map 2 1
| 全局 |
绑定
haproxy worker
进程至指定
CPU
,将第
2
个
work
进程绑
定至
1
号
CPU
|
maxconn N
| 全局 |
每个haproxy
进程的最大并发连接数
|
maxsslconn N
| 全局 |
每个
haproxy
进程
ssl
最大连接数
,
用于
haproxy
配置了证书的
场景下
|
maxconnrate N
| 全局 |
每个进程每秒创建的最大连接数量
|
spread-checks N
| 全局 |
后端
server
状态
check
随机提前或延迟百分比时间,建议
2-
5(20%-50%)
之间,默认值
0
|
pidfile
| 全局 |
指定
pid
文件路径
|
log 127.0.0.1 local2 info
| 全局 |
定义全局的
syslog
服务器;日志服务器需要开启
UDP
协议,
最多可以定义两个
|
3.3.2 多进程和线程
多进程和socket文件配置如下:
haproxy ~]# vim /etc/haproxy/haproxy.cfg... 上面内容省略 ...log 127.0.0.1 local2chroot /var/lib/haproxypidfile /var/run/haproxy.pidmaxconn 100000user haproxygroup haproxydaemon# turn on stats unix socketstats socket /var/lib/haproxy/haproxy.sock1 mode 600 level admin process 1 #启用多个 sock 文件stats socket /var/lib/haproxy/haproxy.sock2 mode 600 level admin process 2nbproc 2 # 启用多进程cpu-map 1 0 # 进程和 cpu 核心绑定防止 cpu 抖动从而减少系统资源消耗cpu-map 2 1 #2 表示第二个进程, 1 表示第二个 cpu 核心
查看多进程信息
haproxy haproxy]# pstree -p | grep haproxy|-haproxy(4816)-+-haproxy(4820)| `-haproxy(4821)
启用多线程
haproxy ~]# vim /etc/haproxy/haproxy.cfg... 上面内容省略 ...log 127.0.0.1 local2chroot /var/lib/haproxypidfile /var/run/haproxy.pidmaxconn 100000user haproxygroup haproxydaemon# turn on stats unix socketstats socket /var/lib/haproxy/haproxy.sock1 mode 600 level admin process 1 #启用多个 sock 文件stats socket /var/lib/haproxy/haproxy.sock2 mode 600 level admin process 2#nbproc 2#cpu-map 1 0#cpu-map 2 1nbthread 2 # 启用多线程
haproxy的全局配置参数及日志分离
[root@haproxy ~]# pstree -p | grep haproxy
|-haproxy(32904)---haproxy(32906)---{haproxy}(32907)# 将`nbproc 2`添加到配置文件的global中
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
nbproc 2 # 启用多进程
[root@haproxy ~]# systemctl restart haproxy.service
[root@haproxy ~]# pstree -p | grep haproxy
|-haproxy(32924)-+-haproxy(32926)
| `-haproxy(32927)
# 系统中的线程数量并不是越多越好,跟CPU的核心数量有关。cpu-map 1 0 # 进程和CPU核心绑定 防止CPU抖动从而减伤系统资源消耗
cpu-map 2 1 # 2表示第二个进程,0表示第一个CPU核心,1表示第二个CPU核心[root@haproxy ~]# systemctl restart haproxy.service
[root@haproxy ~]# pstree -p | grep haproxy
|-haproxy(32957)-+-haproxy(32959)
| `-haproxy(32960)
# 多线程与多进程互斥,多进程与多线程同时设定重启服务的时候会报错
# 查看多线程数量 32960为haproxy子进程的id
[root@haproxy ~]# cat /proc/32960/status | grep -i thread
Threads: 1
Speculation_Store_Bypass: thread vulnerable[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
········
global
log 127.0.0.1 local2chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
maxconn 100000
user haproxy
group haproxy
daemon# turn on stats unix socket
stats socket /var/lib/haproxy/stats# utilize system-wide crypto-policies
ssl-default-bind-ciphers PROFILE=SYSTEM
ssl-default-server-ciphers PROFILE=SYSTEM
#nbproc 2
#cpu-map 1 0
#cpu-map 2 1
nbthread 2
·······
[root@haproxy ~]# systemctl restart haproxy.service
[root@haproxy ~]# pstree -p | grep haproxy
|-haproxy(33009)---haproxy(33011)---{haproxy}(33012)
[root@haproxy ~]# cat /proc/33011/status | grep -i thread
Threads: 2
Speculation_Store_Bypass: thread vulnerable
定向haproxy日志
[root@haproxy ~]# vim /etc/rsyslog.conf
···
# 将这两行注释打开
#打开udp端口
#module(load="imudp") # needs to be done just once
#input(type="imudp" port="514")
module(load="imudp") # needs to be done just once
input(type="imudp" port="514")····
# Log cron stuff
cron.* /var/log/cron# Everybody gets emergency messages
*.emerg :omusrmsg:*# Save news errors of level crit and higher in a special file.
uucp,news.crit /var/log/spooler# Save boot messages also to boot.log
local7.* /var/log/boot.log
local2.* /var/log/haproxy.log
·····[root@haproxy ~]# ll /var/log/haproxy.log
ls: cannot access '/var/log/haproxy.log': No such file or directory
[root@haproxy ~]# systemctl restart rsyslog.service
2、在两台webserver上安装nginx
[root@webserver1 ~]# dnf install nginx -y
[root@webserver2 ~]# dnf install nginx -y
[root@webserver1 ~]# echo webserver1:172.25.254.10 > /usr/share/nginx/html/index.html
[root@webserver1 ~]# systemctl enable --now nginx.service
Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /usr/lib/systemd/system/nginx.service.[root@webserver2 ~]# echo webserver2:172.25.254.20 > /usr/share/nginx/html/index.html
[root@webserver2 ~]# systemctl enable --now nginx.service
Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /usr/lib/systemd/system/nginx.service.# 客户端访问
[root@haproxy ~]# curl 172.25.254.10
webserver1:172.25.254.10
[root@haproxy ~]# curl 172.25.254.20
webserver2:172.25.254.20
3.4 proxies配置
3.4.1 proxies参数说明proxies
参数
|
类型
|
作用
|
defaults | proxies | 默认配置项,针对以下的frontend、backend和listen生效,可以多个name也可以没有name |
frontend | proxies | 前端servername,类似于Nginx的一个虚拟主机 server和LVS服务集群。 |
backend | proxies | 后端服务器组,等于nginx的upstream和LVS中的RS服务器 |
listen | proxies | 将frontend和backend合并在一起配置,相对于frontend和backend配置更简洁,生产常用 |
3.4.2 Proxies配置-defaults
defaultsmode http # HAProxy 实例使用的连接协议log global # 指定日志地址和记录日志条目的syslog/rsyslog 日志设备# 此处的 global 表示使用 global 配置段中设定的lo 值。option httplog # 日志记录选项, httplog 表示记录与 HTTP会话相关的各种属性值# 包括 HTTP 请求、会话状态、连接数、源地 址以及连接时间等option dontlognull #dontlognull 表示不记录空会话连接日志option http-server-close # 等待客户端完整 HTTP 请求的时间,此处为等待10s 。optio n forwardfor except 127 .0.0.0/8 # 透传客户端真实 IP 至后端 web 服务器# 在 apache 配置文件中加入 :<br>%{XForwarded-For}i# 后在 webserer 中看日志即可看到地址透传信息option redispatch # 当 server Id 对应的服务器挂掉后,强制定向到其他健康的服务器,重新派发option http-keep-alive # 开启与客户端的会话保持retries 3 # 连接后端服务器失败次数timeout http-request 1000s # 等待客户端请求完全被接收和处理的最长时间timeout queue 60s # 设置删除连接和客户端收到 503 或服务不可用等提示信息前的等待时间timeout connect 120s # 设置等待服务器连接成功的时间timeout client 600s # 设置允许客户端处于非活动状态,即既不发送数据也不接收数据的时间timeout server 600s # 设置服务器超时时间,即允许服务器处于既不接收也不发送数据的非活动时间timeout http-keep-alive 60s #session 会话保持超时时间,此时间段内会转发到相同的后端服务器timeout check 10s # 指定后端服务器健康检查的超时时间maxconn 3000default-server inter 1000 weight 3
3.4.3 Proxies配置-frontend
bind :指定 HAProxy 的监听地址,可以是 IPV4 或 IPV6 ,可以同时监听多个 IP 或端口,可同时用于 listen 字段中# 格式:bind [<address>]:<port_range> [, ...] [param*]# 注意:如果需要绑定在非本机的 IP ,需要开启内核参数: net.ipv4.ip_nonlocal_bind=1backlog <backlog> # 针对所有 server 配置 ,当前端服务器的连接数达到上限后的后援队列长度,注意:不支持backend
3.5 socat工具
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
stats socket /var/lib/haproxy/stats mode 600 level admin process 1
[root@haproxy ~]# echo "get weight webcluster/web1" | socat stdio /var/lib/haproxy/stats
[root@haproxy ~]# echo "get weight webcluster/web2" | socat stdio /var/lib/haproxy/stats
1 (initial 1) #查看权重[root@haproxy ~]# echo show servers state | socat stdio /var/lib/haproxy/stats
[root@haproxy ~]# echo disable server webcluster/web1 | socat stdio /var/lib/haproxy/stats
[root@haproxy ~]# echo enable server webcluster/web1 | socat stdio /var/lib/haproxy/stats
四、haproxy的算法
4.1 静态算法
4.1.1 static-rr:基于权重的轮询调度
4.1.2 first
4.2 动态算法
动态算法
基于后端服务器状态进行调度适当调整,
4.2.1 roundrobin
4.2.2 leastconn
4.3 其他算法
4.3.1 source
1. Source
描述:基于用户源地址hash并将请求转发到后端服务器,后续同一个源地址请求将被转发至同一个后端服务器。默认为静态方式,但可以通过hash-type选项更改。
使用场景:适用于需要保持会话一致性的场景,如购物车、用户登录等。
2. URI(URI hash)
描述:基于用户请求的URI的左半部分或整个URI做hash,再将hash结果对总权重进行取模后,将请求转发到后端指定服务器。适用于后端是缓存服务器的场景。
使用场景:适用于缓存服务器等需要根据URI分发请求的场景。
语法:通过balance uri和hash-type指令配置。
4.4 各个算法配置
4.4.1 static-rr
-
不能通过socat修改权重
# 不要多进程
stats socket /var/lib/haproxy/stats mode 600 level admin
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
listen webcluster
bind *:80
mode http
#balance roundrobin
balance static-rr
server web1 172.25.254.10:80 check inter 2 fall 3 rise 5 weight 2
server web2 172.25.254.20:80 check inter 2 fall 3 rise 5 weight 1
[root@haproxy ~]# systemctl restart haproxy.service
4.4.2 first
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
listen webcluster
bind *:80
mode http
#balance roundrobin
#balance static-rr
balance first
server web1 172.25.254.10:80 maxconn 1 check inter 2 fall 3 rise 5 weight 2
server web2 172.25.254.20:80 check inter 2 fall 3 rise 5 weight 1
[root@haproxy ~]# systemctl restart haproxy.service
# 测试: 多台主机执行死循环,可看到
[root@webserver1 ~]# while true ; do curl 172.25.254.100; sleep 0.1 ; done
[root@webserver2 ~]# while true ; do curl 172.25.254.100; sleep 0.1 ; done
4.4.3 roundrobin
-
给权重高负载小的
-
基于权重的轮询动态调度算法,
-
支持权重的运行时调整,不同于lvs中的rr轮训模式,
-
HAProxy中的roundrobin支持慢启动(新加的服务器会逐渐增加转发数
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
listen webcluster
bind *:80
mode http
balance roundrobin
#balance static-rr
#balance first
server web1 172.25.254.10:80 maxconn 1 check inter 2 fall 3 rise 5 weight 2
server web2 172.25.254.20:80 check inter 2 fall 3 rise 5 weight 1
[root@haproxy ~]# systemctl restart haproxy.service
#测试
[root@haproxy ~]# for i in {1..10}; do curl 172.25.254.100; done
webserver2 - 172.25.254.20
webserver1 - 172.25.254.10
webserver2 - 172.25.254.20
webserver1 - 172.25.254.10
webserver1 - 172.25.254.10
webserver1 - 172.25.254.10
webserver1 - 172.25.254.10
webserver2 - 172.25.254.20
webserver1 - 172.25.254.10
webserver2 - 172.25.254.20
4.4.4 leastconn
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
listen webcluster
bind *:80
mode http
#balance static-rr
#balance first
#balance roundrobin
balance leastconn
server web1 172.25.254.10:80 maxconn 1 check inter 2 fall 3 rise 5 weight 2
server web2 172.25.254.20:80 check inter 2 fall 3 rise 5 weight 1
[root@haproxy ~]# systemctl restart haproxy.service
#测试
[root@haproxy ~]# for i in {1..10}; do curl 172.25.254.100; done
webserver1 - 172.25.254.10
webserver2 - 172.25.254.20
webserver1 - 172.25.254.10
webserver2 - 172.25.254.20
webserver1 - 172.25.254.10
webserver2 - 172.25.254.20
webserver1 - 172.25.254.10
webserver2 - 172.25.254.20
webserver1 - 172.25.254.10
webserver2 - 172.25.254.20
4.4.5 source
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
listen webcluster
bind *:80
mode http
#balance static-rr
#balance first
#balance roundrobin
#balance leastconn
balance source
server web1 172.25.254.10:80 maxconn 1 check inter 2 fall 3 rise 5 weight 2
server web2 172.25.254.20:80 check inter 2 fall 3 rise 5 weight 1
[root@haproxy ~]# systemctl restart haproxy.service
#测试
[root@haproxy ~]# for i in {1..10}; do curl 172.25.254.100; done
webserver2 - 172.25.254.20
webserver2 - 172.25.254.20
webserver2 - 172.25.254.20
webserver2 - 172.25.254.20
webserver2 - 172.25.254.20
webserver2 - 172.25.254.20
webserver2 - 172.25.254.20
webserver2 - 172.25.254.20
webserver2 - 172.25.254.20
webserver2 - 172.25.254.20
4.4.6 uri
#webserver1上
[root@webserver1 ~]# echo 172.25.254.10 - index1.html > /usr/share/nginx/html/index1.html
[root@webserver1 ~]# echo 172.25.254.10 - index2.html > /usr/share/nginx/html/index2.html
[root@webserver1 ~]# echo 172.25.254.10 - index3.html > /usr/share/nginx/html/index3.html
#webserver2
[root@webserver2 ~]# echo 172.25.254.20 - index1.html > /usr/share/nginx/html/index1.html
[root@webserver2 ~]# echo 172.25.254.20 - index2.html > /usr/share/nginx/html/index2.html
[root@webserver2 ~]# echo 172.25.254.20 - index3.html > /usr/share/nginx/html/index3.html
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
listen webcluster
bind *:80
mode http
#balance roundrobin
#balance leastconn
#balance static-rr
#balance first
#balance source
balance uri
hash-type consistent
server web1 172.25.254.10:80 check inter 2 fall 3 rise 5 weight 2
server web2 172.25.254.20:80 check inter 2 fall 3 rise 5 weight 1
[root@haproxy ~]# systemctl restart haproxy.service
#测试
[root@haproxy ~]# curl 172.25.254.100/index1.html
172.25.254.10 - index1.html
[root@haproxy ~]# curl 172.25.254.100/index2.html
172.25.254.20 - index2.html
[root@haproxy ~]# curl 172.25.254.100/index3.html
172.25.254.10 - index3.html
4.4.7 url param
url_param对用户请求的ur中的 params 部分中的一个参数key对应的value值作hash计算,并由服务器总权重相除以后派发至某挑出的服务器,后端搜索同一个数据会被调度到同一个服务器,多用与电商通常用于追踪用户,以确保来自同一个用户的请求始终发往同一个realserver如果无没key,将按roundrobin算法
4.4.7 hdr
针对用户每个http头部(header)请求中的指定信息做hash,此处由 name 指定的http首部将会被取出并做hash计算,
然后由服务器总权重取模以后派发至某挑出的服务器,如果无有效值,则会使用默认的轮询调度。
stats uri/status #自定义stats page uri
stats auth lee:lee #认证,此行可以出现多次
五、高级配置
5.1 基于cookie的会话保持
5.2 tcp四层穿透
下图用的是nginx的配置文件
optinon 一定要打开
5.3七层穿透
下图修改的是配置文件
上图的mode tcp 修改没mode http
5.4 ACL应用实例
5.4.1 基于源地址的访问控制拒绝指定IP或者IP范围访问
frontend webcluster
bind *:80
mode http
acl ctrl_ip src 172.25.254.1 172.25.254.20
use_backend webcluster-host if ctrl_ip
default_backend default-host
#测试
[root@haproxy ~]# curl 172.25.254.100
webserver2 - 172.25.254.20
[root@webserver2 ~]# curl 172.25.254.100
webserver1 - 172.25.254.10
5.4.2 匹配浏览器类型
匹配客户端浏览器,将不同类型的浏览器调动至不同的服务器组、
范例: 拒绝curl和wget的访问
frontend webcluster
bind *:80
mode http
acl badwebrowers hdr_sub(User-Agent) -i curl wget
#use_backend webcluster-host if webrowers
http-request deny if badwebrowers
default_backend default-host
5.4.3 基于文件后缀名实现动静分离
[root@webserver1 ~]# dnf install php -y
[root@webserver1 ~]# systemctl restart httpd
[root@webserver1 ~]# vim /var/www/html/index.php
[root@webserver1 ~]# cat /var/www/html/index.php
<?php
phpinfo();
?>
#haproxy
frontend webcluster
bind *:80
mode http
acl static path_end -i .html .jpg .png .css .js
acl php path_end -i .php
use_backend webcluster-host if php
default_backend default-host