HAProxy简介

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

特点

  • 可以基于cookies做会话保持
  • 分发负载到多个后端主机
  • 支持sorry server(当所有其他正常主机不可用的时候,则开启这台主机say sorry)
  • 通过指定的端口检测服务
  • 不影响已建立连接的情况下阻止新的请求进入
  • 添加,修改或者删除报文首部
  • 阻止匹配特定模式的请求(可以做访问控制)
  • 从应用程序拦截的URI向已通过身份验证的用户报告

frontend负责给用户请求一个接口,backend可以定义后端一组服务器,一个frontend可以拥有多个backend,一个backend也可以让多个不同的frontend调用
具体流程就是当frontend接受到用户请求,根据我们的配置交给指定的backend处理请求。backend再将请求分发到真正的后端服务器
而listen定义一个完整的代理,其前端和后端部分组合在一个部分中。看到后面示例的配置项就能更好理解了。

配置详解

HAProxy的配置文件为/etc/haproxy/haproxy.cfg

配置文件分为几个部分

  • global:全局配置段
    • 进程及安全配置相关的参数
    • 性能调整相关参数
    • Debug参数
  • proxies:代理配置段
    • defaults:为frontend, listen, backend提供默认配置
    • fronted:描述了一组接受客户端连接的侦听套接字
    • backend:代理将连接到的一组服务器以转发传入连接
    • listen:定义一个完整的代理,其前端和后端部分组合在一个部分中

global常用配置项

进程及安全配置相关的参数
  • chroot :将当前目录更改为并在之前执行chroot(),如果HAProxy被攻破了,也只能得到一个假的根环境,安全方面的设置
  • deamon:运行在前台或者后台
  • user:运行HAProxy进程的用户
  • group:运行HAProxy进程的组
  • uid:运行HAProxy进程的uid
  • gid:运行HAProxy进程的gid
  • nbproc :要启动的haproxy的进程数量;默认为1个,推荐为1个
  • ulimit-n :每个haproxy进程可打开的最大文件数;会自动计算一个最佳的数字,建议不修改。

性能调整参数

虽然说是调整性能的参数,但是一般情况下没有特殊需求,不建议修改

  • maxconn :设定每个haproxy进程所能接受的最大并发连接数;
  • maxconnrate : 每个进程每秒种所能创建的最大连接数量;
  • maxsessrate :每个进程每秒钟所能创建的最大数量
  • maxsslconn : 设定每个进程所能接受的最大ssl并发连接数
  • spread-checks <0…50, in percent> :如果后端主机太多,为了避免同一时刻检查的主机数量太多,这里可以错峰检查。也就是提前或者延后一段时间检查,最多设置不能超过总长的百分之50,比如检测总时长是2s,如果提前或延后超过1s,则上一次的检查和这一次检查很可能会重叠,这时候就会出问题。

proxies常用的配置项

  • bind:监听的地址和端口
  1. 格式:bind \[<address>\]:<port_range> [, ...] [param*]
  • balance:后端服务器组内的服务器调度算法
  1. 格式:balance <algorithm> [ <arguments> ]
  2. 常用的调度算法算法:
  3. roundrobin:动态算法:支持权重的运行时调整,支持慢启动;每个后端中最多支持4095server
  4. static-rr:静态算法:不支持权重的运行时调整及慢启动;后端主机数量无上限;
  5. leastconn:推荐使用在具有较长会话的场景中,例如MySQLLDAP等;
  6. first:根据服务器在列表中的位置,自上而下进行调度;前面服务器的连接数达到上限,新请求才会分配给下一台服务器;
  7. source:基于源地址hash;把每个IP和第一次调度处理的后台服务器的对应关系映射成hash表存放在内存中,这里的hash算法有2种:
  8. 第一种是除权取余算法,举个例子:将源地址进行hash除权再取余,假如得到的数字是1,则分配到第一台服务器,假如最后得到的是2,则分配到第二台服务器上,得到的是3这个值则分配到第三台服务器上,以此类推。
  9. 只要源地址不变,服务器组权重不变,则hash值不会变,余数也不会变。所以可以做到会话保持,同一个ip可以发送给同一个后端服务器。但是如果这时候服务器组数量发生增减,权重发生变化会怎么样?
  10. 很容易理解,得到的值可能会发生变化,并且由于服务器组数量增减,导致大部分ip绑定全部失效....这个就很严重了。
  11. 第二种是一致性哈希算法,形象来说就是维持一个hash环,这个环的整数分布范围是[0, 2^32-1],然后将后端服务器分布在环上,这时候再对源地址进行hash,得到的值投射在环上,顺时针寻找离这个值最近的一台服务器。
  12. 由它来负责响应。同理只要是同一个ip进来 hash值一定是一样的。服务器组结构没有发生变化也一定会分配到同一个服务器上,这时候如果服务器增减,会发生什么呢?比如第二台服务器down了,
  13. 于是顺时针找最近的原则,会分配到下一台,这时候只影响了一台服务器。这样影响的服务器就会大大减少。好很多。但是维持一个hash环的成本显然高一些。所以看情况使用吧。
  14. 算法可以在hash-type这个配置项设置
  15. 参考
  16. <scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
  17. 左半部分:/<path>;<params>
  18. 整个uri:/<path>;<params>?<query>#<frag>
  19. uri:对URI的左半部分做hash计算,并由服务器总权重相除以后派发至某挑出的服务器;
  20. url_param:对用户请求的uri听<params>部分中的参数的值作hash计算,并由服务器总权重相除以后派发至某挑出的服务器;通常用于追踪用户,以确保来自同一个用户的请求始终发往同一个Backend Server
  21. hdr(<name>):对于每个http请求,此处由<name>指定的http首部将会被取出做hash计算; 并由服务器总权重相除以后派发至某挑出的服务器;没有有效值的会被轮询调度;
  22. 例子:hdr(Cookie)
  • hash-type:哈希算法
  1. 格式:hash-type <method> <function> <modifier>
  2. method有:
  3. map-based:除权取余法,哈希数据结构是静态的数组;
  4. consistent:一致性哈希,哈希数据结构是一个树;
  • server 定义后端主机的各服务器及其选项
  1. 格式:
  2. server <name> <address>[:[port]] [param*]
  3. server <name> <address>[:port] [settings ...]
  4. default-server [settings ...] server的默认参数
  5. <name>:服务器在haproxy上的内部名称;出现在日志及警告信息;
  6. <address>:服务器地址,支持使用主机名;
  7. [:[port]]:端口映射;省略时,表示同bind中绑定的端口;
  8. [param*]:常用的参数如下
  9. maxconn <maxconn>:当前server的最大并发连接数;
  10. maxqueue <maxqueue>:队列的最大长度。maxconn满了,多余的请求就需要放在队列中。
  11. backlog <backlog>:当前server的连接数达到上限后的后援队列长度;
  12. backup:设定当前server为备用服务器;即sorry server
  13. check:对当前server做健康状态检测;
  14. addr :设置检测时使用的IP地址
  15. port :设置端口进行健康检测;
  16. inter <delay>:连续两次检测之间的时间间隔,默认为2000ms;
  17. rise <count>:连续多少次检测结果为“成功”才标记服务器为可用;默认为2
  18. fall <count>:连续多少次检测结果为“失败”才标记服务器为不可用;默认为3
  19. 注意:httpchk"smtpchk", "mysql-check", "pgsql-check" and "ssl-hello-chk" 用于定义应用层检测方法;
  20. cookie <value>:为当前server指定其cookie值,用于实现基于cookie的会话黏性;
  21. disabled:标记为不可用;
  22. on-error <mode>:后端服务器故障时的行动策略
  23. - fastinter: force fastinter
  24. - fail-check: 快速强制检测,还没到下一个检测周期,快速触发一次故障探测
  25. - sudden-death:一次失败直接标记为down
  26. - mark-down: 立即标记为down并强制down
  27. redir <prefix>:将发往此server的所有GETHEAD类的请求重定向至指定的URL
  28. weight <weight>:权重,默认为1;
  29. 示例:
  30. server first 10.1.1.1:1080 cookie first check inter 1000
  31. server second 10.1.1.2:1080 cookie second check inter 1000
  32. server backup ${SRV_BACKUP}:1080 backup
  • stats 统计接口相关参数
  1. stats enable:启用统计页;基于默认的参数启用stats page
  2. stats auth <user>:<passwd>:认证时的账号和密码,可使用多次;默认不需要认证
  3. stats realm <realm>:认证时的标题,提示;默认为 "HAProxy Statistics"
  4. stats uri <prefix>:自定义stats page uri,访问的接口,默认 /haproxy?stats
  5. stats refresh <delay>:设定自动刷新时间间隔;
  6. stats admin { if | unless } <cond>:启用stats page中的管理功能
  7. stats hide-version 隐藏版本信息
  8. 配置示例:
  9. listen stats
  10. bind :9099
  11. stats enable
  12. stats uri /haproxy?admin
  13. stats realm HAPorxy\ Stats\ Page
  14. stats auth admin:haproxy
  15. stats admin if TRUE
  16. 访问uri为:http://HAProxy_IP:9099/haproxy?admin
  • maxconn:为指定的frontend定义其最大并发连接数;默认为2000
  1. maxconn <conns>
  • mode: 定义haproxy的工作模式
  1. 格式:mode { tcp|http|health }
  2. tcp:基于layer4实现代理;可代理mysql, pgsql, ssh, ssl等协议;
  3. http:仅当代理的协议为http时使用;
  4. health:工作为健康状态检查的响应模式,当连接请求到达时回应“OK”后即断开连接;
  5. 代理ssh示例:
  6. listen ssh
  7. bind :22022
  8. balance roundrobin
  9. mode tcp
  10. server sshsrv1 192.168.253.158:22 check
  11. server sshsrv1 192.168.253.128:22 check
  • cookie:用户访问服务器,haproxy转发到后端服务器。这时候由haproxy返回给用户的时候,可以操作报文首部,我们可以打个标记在cookie字段插入一个键值对。键为cookie的name,值为后端服务器设置的cookie名,从而做到session sticky
  1. 格式:cookie <name> [ rewrite | insert | prefix ] [ indirect ] [ nocache ] [ postonly ] [ preserve ] [ httponly ] [ secure ] [ domain <domain> ]* [ maxidle <idle> ] [ maxlife <life> ]
  2. <name>:自定义名称作为键
  3. rewirte:重写;将已有的cookie重写
  4. insert:插入;
  5. prefix:前缀;
  6. 基于cookiesession sticky的实现:
  7. backend websrvs
  8. cookie WEBSRV insert nocache indirect #仅对nocache和indirect的情况下进行插入
  9. server srv1 172.16.100.6:80 weight 2 check rise 1 fall 2 maxconn 3000 cookie srv1
  10. server srv2 172.16.100.7:80 weight 1 check rise 1 fall 2 maxconn 3000 cookie srv2
  • option forwardfor:在由haproxy发往后端主机的请求报文中添加“X-Forwarded-For”首部,其值前端客户端的地址;用于向后端主发送真实的客户端IP
  1. 格式:option forwardfor [ except <network> ] [ header <name> ] [ if-none ]
  2. [ except <network> ]:请求报请来自此处指定的网络时不予添加此首部;
  3. [ header <name> ]:使用自定义的首部名称,而非“X-Forwarded-For”;
  4. 示例:
  5. backend static
  6. mode http
  7. option forwardfor header X-Client
  8. ......
  9. 如果是nginx,自定义日志格式的时候需要写成$http_X_Client 来获取值,若为httpd,%{X-Client}i获取指定首部信息。
  • errorfile :返回设置的错误码的对应文件
  1. errorfile <code> <file> 这里需要是文件路径
  2. <code>:响应码,常用以下几个200, 400, 403, 408, 500, 502, 503, and 504.
  3. <file>:响应的文件路径
  4. 示例:
  5. errorfile 400 /etc/haproxy/errorfiles/400badreq.http
  6. errorfile 408 /dev/null # workaround Chrome pre-connect bug
  7. errorfile 403 /etc/haproxy/errorfiles/403forbid.http
  8. errorfile 503 /etc/haproxy/errorfiles/503sorry.http
  • errorloc:errorfile返回一个本地文件,这里的errorloc类似重定向一个uri
  1. 格式:errorloc <code> <url>
  2. 示例:
  3. errorfile 403 http://www.magedu.com/error_pages/403.html
  • reqadd:请求报文中添加首部

格式:reqadd <string> [{if | unless} <cond>]

  • rspadd :响应报文中添加首部
  1. rspadd <string> [{if | unless} <cond>]
  2. 例子:
  3. rspadd X-Via:\ HAPorxy
  • reqdel:删除请求报文首部

格式:reqdel <search> [{if | unless} <cond>]

  • rspdel:删除响应报文首部

格式:rspdel <search> [{if | unless} <cond>]

  • 日志相关设置
  1. log
  2. 格式:log <address> [len <length>] <facility> [<level> [<minlevel>]]
  3. haproxy默认没有记录日志的文件,需要依赖rsyslog收集,具体方法,首先在配置文件的global段添加一条配置项
  4. log 127.0.0.1 local2
  5. 再修改/etc/rsyslog.conf
  6. $ModLoad imudp #取消注释
  7. $UDPServerRun 514 #取消注释
  8. local2.* /var/log/haproxy.log #添加一行
  9. 然后重启
  10. systemctl restart rsyslog haproxy
  11. log-format :设置日志格式
  12. 格式:log-format <string>
  13. 示例:
  14. log-format %{+Q}o\ %t\ %s\ %{-Q}r
  • 压缩相关参数
  1. compression algo <algorithm> ...:启用http协议的压缩机制,指明压缩算法gzip, deflate
  2. compression type <mime type> ...:指明压缩的MIMI类型;
  3. 示例:
  4. compression algo gzip
  5. compression type text/html text/plain
  • 对后端服务器做http协议的健康状态检测的常用配置项
  1. 格式:
  2. option httpchk:定义基于http协议的7层健康状态检测机制
  3. option httpchk <uri>
  4. option httpchk <method> <uri>
  5. option httpchk <method> <uri> <version>
  6. 示例:
  7. backend https_relay
  8. mode tcp
  9. option httpchk OPTIONS * HTTP/1.1\r\nHost:\ www
  10. server apache1 192.168.1.1:443 check port 80
  • 连接超时时长常用配置项
  1. timeout client <timeout>: 客户端一侧链接的非活动时长,默认单位是毫秒;
  2. timeout server <timeout>:连接后端服务器一侧的非活动时长,如果时长足够长,就可以减少一些连接创建的开销。所以尽量长会好一些
  3. timeout http-keep-alive <timeout>:持久连接的持久时长;
  4. timeout http-request <timeout>:请求报文的超时时长,如果客户端一侧非常慢的发送,则服务器要维持这个连接就很浪费资源,这个就是请求报文的超时时长
  5. timeout connect <timeout>:创建连接的超时时长。如果一直连接后端服务器不成功,
  6. timeout client-fin <timeout>:客户端一侧非活动的半连接的超时时长
  7. timeout server-fin <timeout>:服务端一侧非活动的半连接的超时时长
  • use_backend:当符合指定的条件时使用特定的backend
  1. 格式: use_backend <backend> [{if | unless} <condition>]
  2. 例子:
  3. acl static path_beg -i /static /images /javascript /stylesheets
  4. acl static path_end -i .jpg .gif .png .css .js .html
  5. acl php path_end -i .php
  6. use_backend static if static
  7. use_backend dynamic if php
  • block :满足条件的情况下,阻止/放行第7层请求
  1. 格式:block { if | unless } <condition>
  2. 例子:
  3. acl invalid_src src 0.0.0.0/7 224.0.0.0/3
  4. acl invalid_src src_port 0:1023
  5. acl local_dst hdr(host) -i localhost
  6. block if invalid_src || local_dst
  • http-request :第7层请求的访问控制
  1. 格式:http-request { allow | deny } [ { if | unless } <condition> ]
  2. 示例:
  3. http-request replace-value X-Forwarded-For ^192\.168\.(.*)$ 172.16.\1
  4. 报文首部为: X-Forwarded-For: 192.168.10.1, 192.168.13.24, 10.0.0.37
  5. 输出之后为: X-Forwarded-For: 172.16.10.1, 172.16.13.24, 10.0.0.37
  • tcp-request connection :根据第4层条件对传入连接执行操作
  1. 格式:tcp-request connection {accept|reject} [{if | unless} <condition>]
  2. 示例:
  3. mode tcp
  4. acl invalid_src src 172.16.200.2
  5. tcp-request connection reject if invalid_src
  • acl:访问控制列表。HAProxy非常重要的一个配置项
  1. 格式:acl <aclname> <criterion> [flags] [operator] [<value>] ...
  2. <aclname>:自定义的acl名,ACL名称必须由大写和小写字母,数字,' - '(短划线)组成,'_'(下划线),'.'(点)和':'(冒号)。ACL名称区分大小写
  3. <value>的类型:
  4. - 布尔值
  5. - 整数
  6. - IP address / network ip地址
  7. - 字符串
  8. - 正则表达式
  9. - 16进制
  10. <flags>
  11. -i : 忽略字符大小写
  12. -m : 使用特定的匹配方式(一般不用)
  13. -n : 禁止dns主机名反解
  14. -u : 每个acl必须使用独有的名称,默认可以重名,如果重名,表示的是或条件,满足其中一个即可
  15. -- : 强行指名flag到哪结束,避免混淆
  16. [operator]
  17. 匹配整数值:eqgegtlelt
  18. 匹配字符串:不怎么用
  19. - exact match (-m str) : 精确匹配
  20. - substring match (-m sub) : 子串匹配
  21. - prefix match (-m beg) :前缀匹配
  22. - suffix match (-m end) : 后缀匹配
  23. - subdir match (-m dir) : 子路径匹配
  24. - domain match (-m dom) : 域名子串匹配
  25. acl作为条件时的逻辑关系:
  26. - AND (implicit)
  27. - OR (explicit with the "or" keyword or the "||" operator)
  28. - Negation with the exclamation mark ("!")
  29. 示例:
  30. if invalid_src invalid_port
  31. if invalid_src || invalid_port
  32. if ! invalid_src invalid_port
  33. <criterion>
  34. dst : ip
  35. dst_port : 端口
  36. src : ip
  37. src_port : 端口
  38. 示例:
  39. acl invalid_src src 172.16.200.2
  40. path : string
  41. path : exact string match 精确匹配,例如:/imgs/logos/logo.jpg
  42. path_beg : prefix match 前缀匹配,例如: /imgs 表示/imgs下的所有内容都匹配
  43. path_dir : subdir match 路径子串匹配
  44. path_dom : domain match 域名子串匹配
  45. path_end : suffix match 后缀匹配 ,例如:.jpg ;以.jpg结尾都匹配
  46. path_len : length match 长度匹配
  47. path_reg : regex match 正则匹配 ,例如:^/imgs/.*\.jpg 表示/imgs开头以.jpg结尾
  48. path_sub : substring match 子串匹配 只要包含该子串就可以匹配到
  49. 下面的例子用于测试URL是否以/static、/images、/javascript或/stylesheets头。
  50. acl url_static path_beg -i /static /images /javascript /stylesheets
  51. 例如,下面的例子用户测试URL是否以jpggifpngcssjs结尾。
  52. acl url_static path_end -i .jpg .gif .png .css .js
  53. url : string
  54. url : exact string match
  55. url_beg : prefix match
  56. url_dir : subdir match
  57. url_dom : domain match
  58. url_end : suffix match
  59. url_len : length match
  60. url_reg : regex match
  61. url_sub : substring match
  62. req.hdr([<name>[,<occ>]]) : string
  63. hdr([<name>[,<occ>]]) : exact string match
  64. hdr_beg([<name>[,<occ>]]) : prefix match
  65. hdr_dir([<name>[,<occ>]]) : subdir match
  66. hdr_dom([<name>[,<occ>]]) : domain match
  67. hdr_end([<name>[,<occ>]]) : suffix match
  68. hdr_len([<name>[,<occ>]]) : length match
  69. hdr_reg([<name>[,<occ>]]) : regex match
  70. hdr_sub([<name>[,<occ>]]) : substring match
  71. 下面的例子用于测试首部Connection的值是否为close
  72. hdr(Connection) -i close
  73. 下面的例子用记测试请求是否为提供静态内容的主机imgvideodownloadftp
  74. acl host_static hdr_beg(host) -i img. video. download. ftp.
  75. 示例:
  76. acl bad_curl hdr_sub(User-Agent) -i curl 拒绝curl访问
  77. block if bad_curl

动静分离

  1. global
  2. log 127.0.0.1 local2
  3. chroot /var/lib/haproxy
  4. pidfile /var/run/haproxy.pid
  5. maxconn 4000
  6. user haproxy
  7. group haproxy
  8. daemon
  9. stats socket /var/lib/haproxy/stats
  10. defaults
  11. mode http
  12. log global
  13. option httplog
  14. option dontlognull
  15. option http-server-close
  16. option forwardfor except 127.0.0.0/8
  17. option redispatch
  18. retries 3
  19. timeout http-request 10s
  20. timeout queue 1m
  21. timeout connect 10s
  22. timeout client 1m
  23. timeout server 1m
  24. timeout http-keep-alive 10s
  25. timeout check 10s
  26. maxconn 3000
  27. frontend webservs
  28. bind *:788
  29. acl static path_beg -i /static /images /javascript /stylesheets
  30. acl static path_end -i .jpg .gif .png .css .js .html
  31. acl php path_end -i .php
  32. use_backend static if static
  33. use_backend dynamic if php
  34. default_backend dynamic
  35. backend static
  36. balance roundrobin
  37. server sta1 192.168.253.128:6080 check maxconn 3000
  38. server sta2 192.168.253.128:7080 check maxconn 3000
  39. backend dynamic
  40. balance source
  41. server dyn 192.168.253.128:7080 check maxconn 1000

四层代理ssh

  1. listen ssh
  2. bind :22022
  3. balance roundrobin
  4. mode tcp
  5. server sshsrv1 192.168.253.158:22 check
  6. server sshsrv1 192.168.253.128:22 check