安装
yum
[root@host1 ~]# yum install -y haproxy
[root@host1 ~]# rpm -qi haproxy
Name : haproxy
Version : 1.5.18
# yum直接安装即可,base仓库就有,版本较低,一般都需要编译安装需要的版本
# yum安装目录结构
[root@host1 ~]# rpm -ql haproxy
/etc/haproxy
/etc/haproxy/haproxy.cfg
# 配置文件
/etc/logrotate.d/haproxy
# 日志轮替配置
/etc/sysconfig/haproxy
# 服务脚本的参数文件
/usr/bin/halog
/usr/bin/iprange
# 程序文件
/usr/lib/systemd/system/haproxy.service
# 服务脚本
/usr/sbin/haproxy
# 主程序
/usr/sbin/haproxy-systemd-wrapper
/usr/share/doc/haproxy-1.5.18
...
# doc和man等帮助文档
/var/lib/haproxy
# 数据目录
编译
1、下载源码包,解压,进入解压后目录
wget https://www.haproxy.org/download/1.7/src/haproxy-1.7.12.tar.gz
tar -xf haproxy-1.7.12.tar.gz
cd haproxy-1.7.12
2、安装编译工具、依赖包
[root@host2 haproxy-1.7.12]# yum install -y gcc gcc++ pcre pcre-devel
3、
2步走
编译:
[root@host2 haproxy-1.7.12]# make TARGET=linux2628 USE_PCRE=1 ARCh=x86_64 prefix=/usr/local/haproxy
需要指明硬件架构、os平台、安装目录、辅助功能如pcre、openssl、zlib等
安装:
[root@host2 haproxy-1.7.12]# make install PREFIX=/usr/local/haproxy
安装也需要再次指定目录,且prefix要大写,不然不会安装到对应的目录下
安装后目录
# 安装后目录结构:
[root@host2 haproxy-1.7.12]# ll /usr/local/haproxy/
total 0
drwxr-xr-x 3 root root 21 Sep 14 16:50 doc
drwxr-xr-x 2 root root 21 Sep 14 16:50 sbin
drwxr-xr-x 3 root root 17 Sep 14 16:50 share
# 不需要配置PATH和MANPATH,安装后即可找到haproxy的程序文件和man文件
# 程序文件,只有一个haproxy主程序
服务脚本、参数文件
服务脚本,要修改2项:
- 主程序路径
- 主程序启动时,传入的参数,(不同版本不同)注意根据当前版本的主程序的命令参数然后调整
[root@host1 ~]# scp /usr/lib/systemd/system/haproxy.service 192.168.80.101:/usr/lib/systemd/system/
[root@host1 ~]# scp /etc/sysconfig/haproxy 192.168.80.101:/etc/sysconfig/
[root@host2 haproxy-1.7.12]# systemctl daemon-reload
[root@host2 haproxy-1.7.12]# systemctl status haproxy
# 从host1拷贝一份yum安装后的服务脚本,及其参数文件
# 对服务脚本进行修改,
[Unit]
Description=HAProxy Load Balancer
After=syslog.target network.target
[Service]
EnvironmentFile=/etc/sysconfig/haproxy
ExecStartPre=/usr/local/haproxy/sbin/haproxy -f /etc/haproxy/haproxy.cfg -c -q
ExecStart=/usr/local/haproxy/sbin/haproxy -Ds -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid
ExecReload=/bin/kill -USR2 $MAINPID
KillMode=mixed
[Install]
WantedBy=multi-user.target
配置文件
配置文件,注意不同版本的语法兼容问题
[root@host2 haproxy-1.7.12]# mkdir /etc/haproxy
[root@host1 ~]# scp /etc/haproxy/haproxy.cfg 192.168.80.101:/etc/haproxy/
# 创建目录,从别的机器拷贝一份配置文件;
# 可以从examples目录下找,或者从yum安装的版本中复制一份配置文件并做修改,注意不同版本的配置文件兼容性问题;
# 如下:是编译安装1.7.12采用yum安装的1.5.18的配置文件的修改过程
# 创建了haproxy用户、修改了bind指令
# 再次语法检查,即通过
[root@host3 haproxy-1.7.12]# haproxy -c -f /etc/haproxy/haproxy.cfg
[ALERT] 259/115328 (1567) : parsing [/etc/haproxy/haproxy.cfg:31] : cannot find user id for 'haproxy' (0:Success)
[ALERT] 259/115328 (1567) : parsing [/etc/haproxy/haproxy.cfg:32] : cannot find group id for 'haproxy' (0:Success)
[ALERT] 259/115328 (1567) : parsing [/etc/haproxy/haproxy.cfg:63] : 'frontend' cannot handle unexpected argument '*:5000'.
[ALERT] 259/115328 (1567) : parsing [/etc/haproxy/haproxy.cfg:63] : please use the 'bind' keyword for listening addresses.
[ALERT] 259/115328 (1567) : Error(s) found in configuration file : /etc/haproxy/haproxy.cfg
[ALERT] 259/115328 (1567) : Fatal errors found in configuration.
[root@host3 haproxy-1.7.12]# useradd haproxy
[root@host3 haproxy-1.7.12]# haproxy -c -f /etc/haproxy/haproxy.cfg
[ALERT] 259/115346 (1573) : parsing [/etc/haproxy/haproxy.cfg:63] : 'frontend' cannot handle unexpected argument '*:5000'.
[ALERT] 259/115346 (1573) : parsing [/etc/haproxy/haproxy.cfg:63] : please use the 'bind' keyword for listening addresses.
[ALERT] 259/115346 (1573) : Error(s) found in configuration file : /etc/haproxy/haproxy.cfg
[ALERT] 259/115346 (1573) : Fatal errors found in configuration.
[root@host3 haproxy-1.7.12]# vim /etc/haproxy/haproxy.cfg
[root@host3 haproxy-1.7.12]# haproxy -c -f /etc/haproxy/haproxy.cfg
Configuration file is valid
# 启动时,报错,查看为目录不存在问题
# 创建/var/lib/haproxy目录后再启动即可
[root@host3 ~]# systemctl status haproxy
....
Sep 16 14:30:53 host3.b.com haproxy[1817]: [ALERT] 259/143053 (1817) : Starting frontend GLOBAL: cannot bind UNIX socket [/var/lib/haproxy/stats]
[root@host3 ~]# mkdir /var/lib/haproxy
haproxy主程序
[root@host2 haproxy-1.7.12]# haproxy -h
HA-Proxy version 1.7.12 2019/10/25
Copyright 2000-2019 Willy Tarreau <willy@haproxy.org>
Usage : haproxy [-f <cfgfile|cfgdir>]* [ -vdVD ] [ -n <maxconn> ] [ -N <maxpconn> ]
[ -p <pidfile> ] [ -m <max megs> ] [ -C <dir> ] [-- <cfgfile>*]
-v displays version ; -vv shows known build options.
-d enters debug mode ; -db only disables background mode.
-dM[<byte>] poisons memory with <byte> (defaults to 0x50)
-V enters verbose mode (disables quiet mode)
-D goes daemon ; -C changes to <dir> before loading files.
-q quiet mode : don't display messages
-c check mode : only check config files and exit
-n sets the maximum total # of connections (2000)
-m limits the usable amount of memory (in MB)
-N sets the default, per-proxy maximum # of connections (2000)
-L set local peer name (default to hostname)
-p writes pids of all children to this file
-de disables epoll() usage even when available
-dp disables poll() usage even when available
-dS disables splice usage (broken on old kernels)
-dR disables SO_REUSEPORT usage
-dr ignores server address resolution failures
-dV disables SSL verify on servers side
-sf/-st [pid ]* finishes/terminates old pids.
haproxy -D -f /etc/haproxy/haproxy.cfg
# 命令行启动haproxy,
注意点
- haproxy不同版本时,配置文件的语法兼容问题
- haproxy不同版本时,主程序可接收参数不同,导致的服务脚本的参数文件要随之修改传入的启动参数;
- 编译安装,修改服务脚本时,主程序路径问题
特性介绍
haproxy一款开源的负载均衡软件,支持4层,7层的负载,支持会话保持,ssl加密,官网:http://www.haproxy.org/,类似于nginx,也有haproxy的商业版,具有一些高级特性
官方文档
https://cbonte.github.io/haproxy-dconv/
haproxy每个版本都有三类文档:starter guide、configuration manual、management guide,不同版本的文档大体相同,
https://cbonte.github.io/haproxy-dconv/1.7/configuration.html
以1.7版本的配置文档为例,大致包含:http事务介绍,haproxy基础配置,全局参数,proxy代理参数(包含各类指令的语法汇总),acl配置,日志配置等;
特性官方介绍
https://cbonte.github.io/haproxy-dconv/1.7/intro.html#3
在stater guide中介绍了haproxy的各类特性,基础特性、高级特性、
- ssl
- 监控
- 会话保持
- acl
- 内置状态信息
- ...
特性概述
连接保持、连接关闭
http的事务模型,http协议是事务驱动型,每个request有且仅有个response,
http-close模型:
每个请求都要建立一次tcp连接,响应后随机关闭tcp连接,**一个tcp连接只能用于一次http请求和响应,这样会频繁的建立和释放tcp连接,消耗资源,**但仍有适用场景,后端是应用服务器的情况下,
http-keep-alive
一个tcp连接用于多于http请求和响应的传输,server端通过响应头部的content-length字段告知客户端,还有多少数据没发完,直到数据发完后,才关闭连接,避免了频繁建立释放tcp连接的资源消耗,适用后端是静态web服务器的情况
haproxy支持的http连接模式
- keep alive:适用后端是静态服务器
- server close:处理完一对请求和响应后,即关闭和后端服务器的tcp连接,适用后端是动态服务器
- tunnel:仅分析第一个请求和响应,后续内容不分析直接转发
- passive close
- forced close:传输完一对请求和响应后,关闭客户端和服务器的tcp连接
会话保持
作为反向代理软件,必须具备会话保持的功能,在选择某后端节点需要被调度时,不仅仅要考虑既定调度算法,也要考虑,请求与请求之间的相关性,即会话保持,举例:购物网站,添加购物车,刷新页面后,购物车信息不会丢失,是因为应用服务器将购物车等信息存储在了内存中,叫做上下文session,每次请求过来时都会找到对应的上下文,继续后续处理逻辑
静态web服务,是无状态的,每台服务器提供的服务都是完全一样,因此调度到不同的服务器上对结果并不会有什么影响;
动态应用服务,是有状态的,其内存中会保存了客户端的会话信息,因此有关联的请求需要被调度后同一台动态应用服务器上,才能找到之前的会话信息,因此haproxy等代理软件在调度时,就必须考虑请求之间的相关性,将有关系的请求调度到同一台服务器上,
会话共享:如果将多个应用服务器的会话,抽离出来防止一台会话服务器上,如redis,memcache,应用服务器本身不需要保存会话信息,需要会话信息时,直接到会话服务器上查找,此时应用服务器实现了状态的剥离,**成为了无状态应用,此时haproxy调度时,就不需要考虑请求之间的相关性,直接根据调度算法调度即可,**会话服务器本身需要高可用!
haproxy会话保持方式
共有3种:
- 源ip的hash:
- 基于cookie
- 基于stick table
- 会话共享服务器(此时haproxy不需要实现会话保持了)
1、源ip的hash
同一个ip发来的请求会根据hash取模或一致性hash调度到同一台后端服务器,hash取模时,后端节点增减会导致算法结果的大幅度变化,造成大量请求的重新分配节点,所以一般采用一致性hash可以减少变化的节点数,
2、基于cookie
2类cookie,1、应用服务器产生的,用于程序逻辑的cookie,2、反代软件产生的,用于调度的cookie
在snat上网的情况下,一个源ip可能对应许多个私网ip的源ip,导致分布的不均衡,因为区分每个私网源ip的方法是采用cookie,哪怕是采用一个公网的源ip出口,但是每个客户端请求携带的cookie是不一样的,因此haproxy可以根据cookie进行调度,
客户端和应用服务器中间隔了haproxy此类反代软件时,cookie分2类,一类是后端服务器添加的用于保存会话信息的cookie,一类是haproxy代理添加,用于保证同样客户端的请求被调度到相同的后端节点,cookie都会被添加到响应报文头部的set-cookie字段,set-cookie字段一般情况下可以有多个,如下为百度首页响应头的3个set-cookie字段!
假设cli1第一次访问,根据调度算法,被调度到app server1上,app server1处理时会添加一些cookie信息到响应头,响应报文经由haproxy时,haproxy也会给响应头部加入一条,app=app1 ,app是可以自定义的cookie的名称,app1就是每个后端server的标识符,当cli1再次发请求时,携带了app=app1的信息,此时haproxy就知道上次该客户端是被app1处理的,然后会再次把它调度到app1上进行处理,自然,app1上也能找到cli1相关的会话信息,若此时app1 down掉,haproxy会将其调度到 app2上,haproxy设置的cooki会修改成app=app2,但此时cli1之前的会话就会丢失,除非各个app 之间设置了会话复制或、会话共享服务器、
3、基础stick table
haproxy的stick table,可以抽取客户端请求报文的信息,如源ip,请求头部的host,响应头部应用服务器添加的session作为key;后端服务器的标识id作为value,组成一条k-v数据,那么后续请求,分析出是相同key的请求都会查表,然后转发到相同的后端服务器上,实现了会话的保持;第一次响应头部,应用服务器设置的session,haproxy在转发给客户端前,可以把这个session抽离出来,作为key,应用服务器id作为value,客户再次请求时,带着应用服务器设置的session,haproxy根据此信息做key,查到对应的valus即之前的应用服务器id,再将请求转发给相同的后端服务器,实现了会话保持
stick table,占用内存较小,一条标准k-v数据占用大概50字节,且支持额外的统计信息,每加一项统计信息,占用内存自然就大些
stick table,在haproxy的双主,多主架构时,多节点之间的复制效率极高,通过建立单独一条tcp连接实现复制,从而保证了haproxy上,会话信息的 高可用;
stick table查看,可以通过socat工具,借助haproxy的管理socket可以查看
4、基于cookie共享服务器
此时后端节点状态外置,都成了无状态服务器,haproxy可以直接根据调度算法调度,前面三种:基于ip、基于haproxy设置的cookie,基于stick table实现的都是调度时的会话保持;保证了调度时能被调度到存有会话信息的同一台后端节点上, cookie共享服务器实现的是会话信息的剥离于后端节点,在后端节点挂掉时,其他节点仍能读取会话信息继续提供服务,当然会话服务器要做高可用!此时haproxy调度时,无需考虑会话的保持调度了,也无需设置基于ip等调度算法了,因为每台节点都能读取会话信息
后端节点健康监测
haproxy作为反代服务,可以对后端节点做持续性的健康监测,动态的摘除,恢复后端节点;支持的监测方法有:
- tcp四层监测,默认方法,探测的是tcp连接联通性
- http ,可以基于状态码,响应头,响应体内容判断健康状态
- ladp
- mysql
- pgsql
- redis
- spop
- smtp
加工请求和响应报文
nginx作为反代时,可以对客户端请求报文,和后端节点响应报文做一定修改,haproxy同样支持,如:添加真实客户端ip在给后端的请求报文中,删除后端服务器版本信息在给客户端的响应报文中,等...
在2 和 4 步可以,对报文头部进行修改
状态查看
haproxy支持查看自身和后端节点的状态信息,通过启用管理用socket接口,socat包中命令可以实现调用该socket查看状态信息;
1、启用配置
global
log 127.0.0.1 local2
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
maxconn 20000
user haproxy
group haproxy
daemon
stats socket /var/lib/haproxy/stats
# 启用管理用socket
2、安装socat包
yum install -y socat
# epel 源
[root@host2 ~]# echo "help" | socat stdio /var/lib/haproxy/stats
Unknown command. Please enter one of the following commands only :
help : this message
prompt : toggle interactive mode with prompt
quit : disconnect
disable agent : disable agent checks (use 'set server' instead)
disable health : disable health checks (use 'set server' instead)
disable server : disable a server for maintenance (use 'set server' instead)
# 查看帮助
[root@host2 ~]# echo "show info" | socat stdio /var/lib/haproxy/stats
Name: HAProxy
Version: 1.7.12
Release_date: 2019/10/25
Nbproc: 1
Process_num: 1
# 查看信息
[root@host2 ~]# echo "show backend" | socat stdio /var/lib/haproxy/stats
# name
static
app
# 查看所有后端
# 此外,还可以动态上下线某后端节点、调整权重、设置acl等
acl
haproxy支持acl,访问控制列表,access controll list,基于acl可以对请求流量分组,如根据请求的uri的特征,进行正则匹配,分为动、静,从而转发给不同的后端节点组
连接重用
haproxy在转发客户端请求时,选择调度到某个后端节点,之后haproxy会和该后端节点建立连接,该连接在完成一次请求和响应的传输后,如没有后续请求和响应,而该连接又不释放的情况下,空闲的tcp连接无疑是浪费,因此haproxy引入了连接重用,使得别的客户端的请求可以使用该空闲tcp连接进行传输
http-reuse strategy_name指令可以设置连接重用的策略,默认策略为禁用连接重用
strategy_name有4个值;
- never,默认,表示禁用连接重用,来源不同的请求不会共享同一个后端连接;
- safe,为客户端的第一个请求建立一个tcp连接,然后后续的请求会重用和后端节点空闲的tcp连接,
- aggressive,重用空闲tcp连接转发大多数客户端第一次请求
- always,总是为第一个请求重用空闲连接,
- 建议:safe,搭配http-keep-alive的事务模型