LB集群之HAProxy

一、HAProxy简介

HAProxy是一个使用C语言编写的自由及开放源代码软件,其提供高可用性、负载均衡,以及基于TCP和HTTP的应用程序代理。

功能:

  • 根据静态分配的cookie路由HTTP请求
  • 在通过使用HTTP cookie确保服务器持久性的同时,在多个服务器之间传播负载
  • 在主服务器发生故障时切换到备份服务器
  • 接受专用于服务监控的特殊端口的连接
  • 停止接受连接而不破坏现有连接
  • 在两个方向上添加,修改和删除HTTP标头
  • 阻止匹配特定模式的请求
  • 从应用程序拦截的URI向已通过身份验证的用户报告详细状态

# yum install haproxy -y

主程序:/usr/sbin/haproxy

配置文件:/etc/haproxy/haproxy.cfg

启动服务:# systemctl start haproxy.service

简单配置示例:实现调度功能

frontend web  #前端,相当于nginx, server {}
    bind *:80  #监听于80端口
    backend websrvs  #指定后端
backend websrvs  #后端,相当于nginx, upstream {}
    balance roundrobin  #调度算法,轮调
    server srv1 192.168.0.9:80 check  #后端server,并做健康检查
    server srv1 192.168.0.10:80 check

二、全局(global)配置段参数

1、日志记录

定义全局的syslog服务器;最多可以定义两个

log <address> [len <length>] <facility> [max level [min level]]
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
log         127.0.0.1 local2
[root@haproxy ~]# systemctl restart haproxy
[root@haproxy ~]# vim /etc/rsyslog.conf
#### MODULES ####
$ModLoad imudp
$UDPServerRun 514
#### RULES ####
# Save haproxy log to haproxy.log
local2.*      /var/log/haproxy.log
[root@haproxy ~]# systemctl restart rsyslog

2、性能调整相关参数

nbproc <number>:要启动的haproxy的进程数量

maxconn <number>:设定每个haproxy进程所能接受的最大并发连接数

总体的并发连接数:nbproc * maxconn

ulimit-n <number>:每个haproxy进程可打开的最大文件数

maxconnrate <number>:每个进程每秒种所能创建的最大连接数量

maxsessrate <number>:每个进程每秒所能创建的最大会话数量

maxsslconn <number>:设定每个haproxy进程所能接受的ssl的最大并发连接数

默认配置:

global
    log         127.0.0.1 local2
    chroot      /var/lib/haproxy  #虚根
    pidfile     /var/run/haproxy.pid
    maxconn     4000  #每个haproxy进程最大并发连接数,默认4000
    user        haproxy  #以haproxy的身份运行
    group       haproxy
    daemon
    stats socket /var/lib/haproxy/stats  #打开本机socket文件实现本地通信

三、代理配置段

- defaults <name>  #默认配置段
- frontend <name>  #前端配置段
- backend  <name>  #后端配置段
- listen   <name>

name:所有代理名称必须由大写和小写字母,数字,’-‘,’_’,’.’组成。

  • default_backend <backend> 设定默认的backend,用于frontend中;

  • default-server [param*] 为backend中的各server设定默认选项;

1、bind

在前端定义一个或多个侦听地址或端口

bind [<address>]:<port_range> [,...] [param*]
bind *:80,*:443
bind 192.168.0.8:80,192.168.0.8:8080
bind /var/run/ssl-frontend.sock user root mode 600 accept-proxy

2、balance

后端服务器组内的服务器调度算法

balance <algorithm> [ <arguments> ]
balance url_param <param> [check_post]
  • roundrobin:轮调,动态算法:支持权重的运行时调整,支持慢启动;每个后端中最多支持4095个server

  • static-rr:静态轮调,静态算法:不支持权重的运行时调整及慢启动;后端主机数量无上限

  • leastconn:最少连接数,推荐使用在具有较长会话的场景中,例如MySQL、LDAP等

  • first:根据服务器在列表中的位置,自上而下进行调度;前面服务器的连接数达到上限,新请求才会分配给下一台服务

  • source:源地址hash

  • uri:对URI的左半部分做hash计算,并由服务器总权重相除以后派发至某挑出的服务器

<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
    左半部分:/<path>;<params>
    整个uri:/<path>;<params>?<query>#<frag>
  • url_param

对用户请求的uri的<params>部分中的参数的值作hash计算,并由服务器总权重相除以后派发至某挑出的服务器;通常用于追踪用户,以确保来自同一个用户的请求始终发往同一个Backend Server

  • hdr(<name>)

对于每个http请求,此处由<name>指定的http首部将会被取出做hash计算; 并由服务器总权重相除以后派发至某挑出的服务器;没有有效值的会被轮询调度

  • rdp-cookie(<name>)

3、hash-type

hash-type <method> <function> <modifier>
  • map-based:除权取余法,哈希数据结构是静态的数组
  • consistent:一致性哈希,哈希数据结构是一个

4、server

定义后端主机的各服务器及其选项

server <name> <address>[:[port]] [param*]
  • name:服务器在haproxy上的内部名称
  • address:服务器地址,支持使用主机名
  • port:端口映射;省略时,表示同bind中绑定的端口
  • param*:参数
    • maxconn <maxconn>:当前server的最大并发连接数
    • backlog <backlog>:当前server的连接数达到上限后的后援队列长度
    • backup:设定当前server为备用服务器
    • disabled:标记为不可用
    • check:对当前server做健康状态检测

      • addr :检测时使用的IP地址
      • port :针对此端口进行检测
      • inter <delay>:连续两次检测之间的时间间隔,默认为2000ms
      • rise <count>:连续多少次检测结果为“成功”才标记服务器为可用;默认为2
      • fall <count>:连续多少次检测结果为“失败”才标记服务器为不可用;默认为3
    • cookie <value>:为当前server指定其cookie值,用于实现基于cookie的会话黏性
    • on-error <mode>:后端服务故障时的行动策略

      • fastinter:强迫快速
      • fail-check:快速失败,快速测试;有故障则离线,没有问题上线,默认策略
      • sudden-death:模拟一个致命的失败健康检查,另一个失败的检查将标记服务器关闭,强制fastinter
      • mark-down:立即标记服务器并强制执行fastinter
    • redir <prefix>:将发往此server的所有GET和HEAD类的请求重定向至指定的URL
    • weight <weight>:权重,默认为1

5、option httpchk

定义基于http协议的7层健康状态检测机制

option httpchk
option httpchk <uri>
option httpchk <method> <uri>
option httpchk <method> <uri> <version>
http-check expect [!] <match> <pattern>

用于定义应用层检测方法:

  • httpchk:检测httpd服务的健康状态
option httpchk GET /test.html

6、maxconn

为指定的frontend定义其最大并发连接数;默认为2000

7、mode

mode { tcp|http|health } :定义haproxy的工作模式
  • tcp:基于layer4实现代理;可代理mysql, pgsql, ssh, ssl等协议;
  • http:仅当代理的协议为http时使用;
  • health:工作为健康状态检查的响应模式,当连接请求到达时回应“OK”后即断开连接;
cookie <name> [ rewrite | insert | prefix ] [ indirect ] [ nocache ]  [ postonly ] [ preserve ] [ httponly ] [ secure ]  [ domain <domain> ]* [ maxidle <idle> ] [ maxlife <life> ]
  • name:修改或插入的cookie的名称,以便带来持久性
  • rewirte:重写
  • insert:插入
  • prefix:前缀

基于cookie的调度配置示例:

frontend web
    bind *:80
    default_backend websrvs
backend websrvs
    balance roundrobin
    cookie WEBSRV insert nocache indirect
    server srv1 192.168.0.9:80 check inter 1000 rise 1 fall 2 maxconn 2000
 cookie web1
    server srv2 192.168.0.10:80 check maxconn 1500 cookie web2

9、option forwardfor

option forwardfor [ except <network> ] [ header <name> ] [ if-none ]

在由haproxy发往后端主机的请求报文中添加“X-Forwarded-For”首部,其值前端客户端的地址;用于向后端主发送真实的客户端IP

  • [ except <network> ]:请求报请来自此处指定的网络时不予添加此首部
  • [ header <name> ]:使用自定义的首部名称,而非“X-Forwarded-For”

10、errorfile 和 errorloc

errorfile <code> <file>
errorloc <code> <url>
errorloc302 <code> <url>
  • code:HTTP状态代码。 目前HAProxy能够支持的代码:200, 400, 403, 408, 500, 502, 503, and 504
  • file:指定包含完整HTTP响应的文件
errorfile 400 /etc/haproxy/errorfiles/400badreq.http
errorfile 408 /dev/null  #解决方法Chrome预连接错误
errorfile 403 /etc/haproxy/errorfiles/403forbid.http
errorfile 503 /etc/haproxy/errorfiles/503sorry.http

11、修改报文头部

reqadd  <string> [{if | unless} <cond>] 在HTTP请求的末尾添加标头
rspadd  <string> [{if | unless} <cond>] 在HTTP响应的末尾添加标头
reqdel  <search> [{if | unless} <cond>] 删除与HTTP请求中的正则表达式匹配的所有标头
reqidel <search> [{if | unless} <cond>]  (ignore case)
rspdel  <search> [{if | unless} <cond>] 删除与HTTP响应中的正则表达式匹配的所有标头
rspidel <search> [{if | unless} <cond>]  (ignore case)
rspidel  Server.*  #删除响应报文中的Server字段信息

12、连接超时时长

timeout client <timeout>  面向客户端侧的超时时间,默认单位是毫秒
timeout server <timeout>  面向服务器侧的超时时间
timeout http-keep-alive <timeout>  持久连接的持久时长
timeout http-request <timeout>  等待客户端传完的请求报文的超时时长
timeout connect <timeout>  向后端服务器发起连接请求时的超时时长
timeout client-fin <timeout>  设置客户端一侧的半关闭连接超时时长
timeout server-fin <timeout>  设置服务器一侧的半关闭连接超时时长

13、访问控制

满足条件时拒绝请求:

block { if | unless } <condition>

示例:当源地址为172.16.200.2,则拒绝请求,并返回错误页

acl invalid_src src 172.16.200.2
block if invalid_src
errorfile 403 /var/www/error.html

根据http请求包控制:

http-request { allow | deny } [ { if | unless } <condition> ]

根据tcp连接控制:

tcp-request connection {accept|reject}  [{if | unless} <condition>]

示例:拒绝192.168.0.7的ssh访问

14、use_backend

当符合指定的条件时使用特定的backend,可以实现条件式调度

use_backend <backend> [{if | unless} <condition>]

六、统计接口

  • stats enable:启用统计页;基于默认的参数启用stats page
stats uri   : /haproxy?stats
stats realm : "HAProxy Statistics"
stats auth  : no authentication
stats scope : no restriction
  • stats auth <user>:<passwd>:认证时的账号和密码,可使用多次
  • stats realm <realm>:认证时的realm
  • stats uri <prefix>:自定义stats page uri
  • stats refresh <delay>:设定自动刷新时间间隔
  • stats admin { if | unless } <cond>:启用stats page中的管理功能

配置参数示例:

listen stats
    bind :8080
    stats realm "HAProxy Stats Page"
    stats auth admin:adminpass
    stats admin if TRUE

访问:http://192.168.0.8:8080/haproxy?stats进入管理页

七、ACL

访问控制列表的使用提供了一种灵活的解决方案来执行内容切换,并且通常基于从请求,响应或任何环境状态中提取的内容来做出决策

acl <aclname> <criterion> [flags] [operator] [<value>] ...
  • aclname:ACL名称必须由大写和小写字母,数字,’ – ‘(短划线),’_’(下划线),’.’ 组成。ACL名称区分大小写
  • value:值
boolean 布尔型
integer or integer range 整数或整数范围
IP address / network IP或网络地址
string (exact精确匹配, substring子串匹配, suffix前缀匹配, prefix后缀匹配, subdir子路径匹配, domain子域名匹配) 字符串匹配
regular expression 正则表示式匹配
hex block 16进制的块匹配
  • flags:标志
-i : 忽略字符大小写
-m : 特定的模式
-n : 禁止DNS解析
-u : 要求acl使用唯一的名称
  • operator:操作符
匹配整数值:eq、ge、gt、le、lt
匹配字符串:
    exact match 精确匹配
    substring match 子串匹配
    prefix match 前缀匹配
    suffix match 后缀匹配
    subdir match 子路径匹配
    domain match 子域名匹配
  • acl作为条件时的逻辑关系:
if invalid_src invalid_port  或关系
if invalid_src || invalid_port  与关系
if ! invalid_src invalid_port  非invalid_src
  • criterion:判断标准
dst : 目标ip
dst_port : 目标端口
src : 源ip
src_port : 源端口
检查url的路径
path     : 精确匹配
path_beg : 前缀匹配
path_dir : 子串匹配
path_dom : 子域名匹配
path_end : 路径后缀匹配
path_len : 路径长度匹配
path_reg : 路径的正则表达式模式匹配
path_sub : 路径的子字串匹配
检查整个url
url     : 精确匹配
url_beg : 前缀匹配
url_dir : 子串匹配
url_dom : 子域名匹配
url_end : 后缀匹配
url_len : 长度匹配
url_reg : 正则表达式匹配
url_sub : 子字串匹配
请求报文的指定头部检查 req.hdr([<name>[,<occ>]]) : string
hdr([<name>[,<occ>]])     : exact string match
hdr_beg([<name>[,<occ>]]) : prefix match
hdr_dir([<name>[,<occ>]]) : subdir match
hdr_dom([<name>[,<occ>]]) : domain match
hdr_end([<name>[,<occ>]]) : suffix match
hdr_len([<name>[,<occ>]]) : length match
hdr_reg([<name>[,<occ>]]) : regex match
hdr_sub([<name>[,<occ>]]) : substring match

示例:阻止curl访问

frontend web *:80
    acl bad_curl hdr_sub(User-Agent) -i curl
    block if bad_curl
    default_backend appsrvs

示例:实现动静分离

frontend web *:80
    acl url_static path_beg -i /static /images /javascript /stylesheets
    acl url_static path_end -i .jpg .gif .png .css .js .html .txt .htm
    use_backend staticsrvs if url_static
    default_backend appsrvs
backend staticsrvs
    balance roundrobin
    server stcsrv1 192.168.0.9:80 check
backend appsrvs
    balance roundrobin
    server app1 192.168.0.10:80 check
    server app2 192.168.0.11:80 check

八、配置HAProxy支持https协议

1)准备ca证书

[root@haproxy ~]# cd /etc/pki/tls/certs/
[root@haproxy certs]# vim Makefile
%.key:
    umask 77 ; \
    /usr/bin/openssl genrsa $(KEYLEN) > $@  #将-aes128删除,避免私钥加密
[root@haproxy certs]# make haproxy.crt
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:bj
Locality Name (eg, city) [Default City]:bj
Organization Name (eg, company) [Default Company Ltd]:dongfei.com
Organizational Unit Name (eg, section) []:opt
Common Name (eg, your name or your server's hostname) []:www.dongfei.com
[root@haproxy certs]# cp haproxy.* /etc/haproxy/
[root@haproxy ~]# cd /etc/haproxy/
[root@haproxy haproxy]# cat haproxy.crt haproxy.key > haproxy.pem

2)配置HAProxy

frontend web *:80
    bind *:443 ssl crt /etc/haproxy/haproxy.pem
    redirect scheme https if !{ ssl_fc }  #将80端口跳转到443
    acl url_static path_beg -i /static /images /javascript /stylesheets
    acl url_static path_end -i .jpg .gif .png .css .js .html .txt .htm
    use_backend staticsrvs if url_static
    default_backend appsrvs
backend staticsrvs
    balance roundrobin
    server stcsrv1 192.168.0.9:80 check
backend appsrvs
    balance roundrobin
    server app1 192.168.0.10:80 check
    server app2 192.168.0.11:80 check

九、常用功能的实现

1)压缩

frontend web *:80
    default_backend appsrvs
    compression algo gzip
    compression type text/html text/plain
backend appsrvs
    balance roundrobin
    server app1 192.168.0.10:80 check
    server app2 192.168.0.11:80 check

2)stats page

listen stats
    bind :8080
    stats realm "HAProxy Stats Page"
    stats auth admin:adminpass  #认证用户:密码
    stats admin if TRUE
访问:http://192.168.0.8:8080/haproxy?stats进入状态管理页

3)自定义错误页

[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
frontend web *:80
        default_backend appsrvs
        acl bad_guy src 192.168.0.7
        block if bad_guy
        errorfile 403 /etc/haproxy/errorfiles/403forbid.http
backend appsrvs
        balance roundrobin
        server app1 192.168.0.10:80 check
        server app2 192.168.0.11:80 check
[root@haproxy ~]# mkdir /etc/haproxy/errorfiles/ -p
[root@haproxy ~]# echo 'forbid' >/etc/haproxy/errorfiles/403forbid.http
[root@haproxy ~]# systemctl restart haproxy
[root@client ~]# curl http://192.168.0.8/
forbid

4)访问控制

listen stats
    bind :8080
    stats realm "HAProxy Stats Page"
    stats auth admin:adminpass
    stats admin if TRUE
    acl admin_client src 192.168.0.254
    block unless admin_client  #只允许192.168.0.254访问状态管理页

5)日志功能

调度器中配置
[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg 
backend appsrvs
    balance roundrobin
    option forwardfor
    server app1 192.168.0.10:80 check
    server app2 192.168.0.11:80 check
[root@haproxy haproxy]# systemctl restart haproxy.service
后端服务器配置(Apache)
[root@web2 ~]# vim /etc/httpd/conf/httpd.conf
LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent
}i\"" combined  #改变日志记录方式
[root@web2 ~]# systemctl restart httpd
[root@web2 ~]# tail -f /var/log/httpd/access_log

6)基于cookie的session粘滞

[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
frontend web *:80
    mode http
    default_backend appsrvs
backend appsrvs
    balance roundrobin
    option forwardfor
    cookie WEBSRV insert nocache indirect
    server app1 192.168.0.10:80 check inter 1000 rise 1 fall 2 maxconn 2000 cookie websrv1
    server app2 192.168.0.11:80 check maxconn 1500 cookie websrv2
[root@client ~]# curl -b "WEBSRV=websrv1" http://192.168.0.8/
web1
[root@client ~]# curl -b "WEBSRV=websrv2" http://192.168.0.8/ 
web2

7)后端主机的健康状态检测

backend appsrvs
    balance roundrobin
    option httpchk GET /test.html
    server app1 192.168.0.10:80 check 
    server app2 192.168.0.11:80 check

8)请求和响应报文首部的操纵:替换响应报文的Server字段信息

[root@haproxy ~]# vim /etc/haproxy/haproxy.cfg
frontend web *:80
    mode http
    rspidel ^Server:.*
    rspadd Server:\ Apache\ or\ Nginx
    default_backend appsrvs
[root@client ~]# curl -I http://192.168.0.8/
Server: Apache or Nginx  #掩人耳目

感谢阅读!