术→技巧, 运维

负载均衡技术:HAProxy

钱魏Way · · 435 次浏览

HAProxy简介

HAProxy是一款提供高可用性、负载均衡以及基于TCP(第四层)和HTTP(第七层)应用的代理软件,HAProxy是完全免费的、借助HAProxy可以快速并且可靠的提供基于TCP和HTTP应用的代理解决方案。HAProxy稳定性也是非常好,可以与硬件级的F5相媲美,根据官方文档,HAProxy可以跑满10Gbps-New benchmark of HAProxy at 10 Gbps using Myricom’s 10GbE NICs (Myri-10G PCI-Express),这个数值作为软件级负载均衡器是相当惊人的。

其配置简单,而且拥有很好的对服务器节点的健康检查功能(相当于keepalived健康检查),当其代理的后端服务器出现故障时,HAProxy会自动的将该故障服务器摘除,当服务器的故障恢复后HAProxy还会自动重新添加回服务器主机。

负载平衡的类型

无负载平衡

没有负载平衡的简单Web应用程序环境可能如下所示

在此示例中,用户直接连接到您的Web服务器,在yourdomain.com上,并且没有负载平衡。如果您的单个Web服务器出现故障,用户将无法再访问您的Web服务器。此外,如果许多用户试图同时访问您的服务器并且无法处理负载,他们可能会遇到缓慢的体验,或者可能根本无法连接。

4层负载平衡

将网络流量负载平衡到多个服务器的最简单方法是使用第4层(传输层)负载平衡。以这种方式进行负载均衡将根据IP范围和端口转发用户流量(即,如果请求进入http://yourdomain.com/anything,则流量将转发到处理yourdomain.com的所有请求的后端。端口80)。

用户访问负载均衡器,负载均衡器将用户的请求转发给后端服务器的Web后端组。无论选择哪个后端服务器,都将直接响应用户的请求。通常,Web后端中的所有服务器应该提供相同的内容 – 否则用户可能会收到不一致的内容。

所谓的四层就是ISO参考模型中的第四层。四层负载均衡也称为四层交换机,它主要是通过分析IP层及TCP/UDP层的流量实现的基于IP加端口的负载均衡。常见的基于四层的负载均衡器有LVS、F5等。

以常见的TCP应用为例,负载均衡器在接收到第一个来自客户端的SYN请求时,会通过设定的负载均衡算法选择一个最佳的后端服务器,同时将报文中目标IP地址修改为后端服务器IP,然后直接转发给该后端服务器,这样一个负载均衡请求就完成了。从这个过程来看,一个TCP连接是客户端和服务器直接建立的,而负载均衡器只不过完成了一个类似路由器的转发动作。在某些负载均衡策略中,为保证后端服务器返回的报文可以正确传递给负载均衡器,在转发报文的同时可能还会对报文原来的源地址进行修改。

7层负载平衡

7层负载平衡是更复杂的负载均衡网络流量的方法是使用第7层(应用层)负载均衡。使用第7层允许负载均衡器根据用户请求的内容将请求转发到不同的后端服务器。这种负载平衡模式允许您在同一域和端口下运行多个Web应用程序服务器。

示例中,如果用户请求yourdomain.com/blog,则会将其转发到博客后端,后端是一组运行博客应用程序的服务器。其他请求被转发到web-backend,后端可能正在运行另一个应用程序。

七层负载均衡器也称为七层交换机,位于OSI的最高层,即应用层,此时负载均衡器支持多种应用协议,常见的有HTTP、FTP、SMTP等。七层负载均衡器可以根据报文内容,再配合负载均衡算法来选择后端服务器,因此也称为“内容交换器”。比如,对于Web服务器的负载均衡,七层负载均衡器不但可以根据“IP+端口”的方式进行负载分流,还可以根据网站的URL、访问域名、浏览器类别、语言等决定负载均衡的策略。例如,有两台Web服务器分别对应中英文两个网站,两个域名分别是A、B,要实现访问A域名时进入中文网站,访问B域名时进入英文网站,这在四层负载均衡器中几乎是无法实现的,而七层负载均衡可以根据客户端访问域名的不同选择对应的网页进行负载均衡处理。常见的七层负载均衡器有HAproxy、Nginx等。

这里仍以常见的TCP应用为例,由于负载均衡器要获取到报文的内容,因此只能先代替后端服务器和客户端建立连接,接着,才能收到客户端发送过来的报文内容,然后再根据该报文中特定字段加上负载均衡器中设置的负载均衡算法来决定最终选择的内部服务器。纵观整个过程,七层负载均衡器在这种情况下类似于一个代理服务器。

对比四层负载均衡和七层负载均衡运行的整个过程,可以看出,在七层负载均衡模式下,负载均衡器与客户端及后端的服务器会分别建立一次TCP连接,而在四层负载均衡模式下,仅建立一次TCP连接。由此可知,七层负载均衡对负载均衡设备的要求更高,而七层负载均衡的处理能力也必然低于四层模式的负载均衡。

HAProxy 的核心功能

  • 负载均衡:L4和L7两种模式,支持RR/静态RR/LC/IP Hash/URI Hash/URL_PARAM Hash/HTTP_HEADER Hash 等丰富的负载均衡算法
  • 健康检查:支持TCP和HTTP两种健康检查模式
  • 会话保持:对于未实现会话共享的应用集群,可通过 Insert Cookie/Rewrite Cookie/Prefix Cookie,以及上述的多种 Hash 方式实现会话保持
  • SSL:HAProxy 可以解析 HTTPS 协议,并能够将请求解密为 HTTP 后向后端传输
  • HTTP 请求重写与重定向
  • 监控与统计:HAProxy 提供了基于 Web 的统计信息页面,展现健康状态和流量数据。基于此功能,使用者可以开发监控程序来监控 HAProxy 的状态

HAProxy的关键特性

性能

HAProxy借助于OS上几种常见的技术来实现性能的最大化。

  • 单进程、事件驱动模型显著降低了上下文切换的开销及内存占用。
  • O(1)事件检查器(event checker)允许其在高并发连接中对任何连接的任何事件实现即时探测。
  • 在任何可用的情况下,单缓冲(single buffering)机制能以不复制任何数据的方式完成读写操作,这会节约大量的CPU时钟周期及内存带宽;
  • 借助于Linux 2.6 (>= 2.6.27.19)上的splice()系统调用,HAProxy可以实现零复制转发(Zero-copy forwarding),在Linux 3.5及以上的OS中还可以实现零复制启动(zero-starting);
  • MRU内存分配器在固定大小的内存池中可实现即时内存分配,这能够显著减少创建一个会话的时长;
  • 树型存储:侧重于使用作者多年前开发的弹性二叉树,实现了以O(log(N))的低开销来保持计时器命令、保持运行队列命令及管理轮询及最少连接队列;
  • 优化的HTTP首部分析:优化的首部分析功能避免了在HTTP首部分析过程中重读任何内存区域;
  • 精心地降低了昂贵的系统调用,大部分工作都在用户空间完成,如时间读取、缓冲聚合及文件描述符的启用和禁用等;

所有的这些细微之处的优化实现了在中等规模负载之上依然有着相当低的CPU负载,甚至于在非常高的负载场景中,5%的用户空间占用率和95%的系统空间占用率也是非常普遍的现象,这意味着HAProxy进程消耗比系统空间消耗低20倍以上。因此,对OS进行性能调优是非常重要的。即使用户空间的占用率提高一倍,其CPU占用率也仅为10%,这也解释了为何7层处理对性能影响有限这一现象。由此,在高端系统上HAProxy的7层性能可轻易超过硬件负载均衡设备。

稳定性

  • 作为建议以单进程模式运行的程序,HAProxy对稳定性的要求是十分严苛的。按照作者的说法,HAProxy在13年间从未出现过一个会导致其崩溃的BUG,HAProxy一旦成功启动,除非操作系统或硬件故障,否则就不会崩溃(我觉得可能多少还是有夸大的成分)。
  • HAProxy的大部分工作都是在操作系统内核完成的,所以HAProxy的稳定性主要依赖于操作系统,作者建议使用6或3.x的Linux内核,对sysctls参数进行精细的优化,并且确保主机有足够的内存。这样HAProxy就能够持续满负载稳定运行数年之久。

负载均衡算法

负载均衡算法用于检测后端中的哪套服务器被负载均衡机制选定进行请求响应。HAProxy提供多种算法选项。除了负载均衡算法之外,我们还能够为服务器分配一个weight参数来指定该服务器被选定的频率。由于HAProxy提供多种负载均衡算法,下面说明几种常用的算法:

  • roundrobin。表示简单的轮询,每个服务器根据权重轮流使用,在服务器的处理时间平均分配的情况下这是最流畅和公平的算法。该算法是动态的,对于实例启动慢的服务器权重会在运行中调整。
  • leastconn。连接数最少的服务器优先接收连接。leastconn建议用于长会话服务,例如LDAP、SQL、TSE等,而不适合短会话协议。如该算法是动态的,对于实例启动慢的服务器权重会在运行中调整。
  • static-rr。每个服务器根据权重轮流使用,类似roundrobin,但它是静态的,意味着运行时修改权限是无效的。另外,它对服务器的数量没有限制。(该算法一般不用)
  • source。对请求源IP地址进行哈希,用可用服务器的权重总数除以哈希值,根据结果进行分配。只要服务器正常,同一个客户端IP地址总是访问同一个服务器。如果哈希的结果随可用服务器数量而变化,那么客户端会定向到不同的服务器。(算法一般用于不能插入cookie的Tcp模式。它还可以用于广域网上为拒绝使用会话cookie的客户端提供最有效的粘连)该算法默认是静态的,所以运行时修改服务器的权重是无效的,但是算法会根据“hash-type”的变化做调整。
  • uri。表示根据请求的URI左端(问号之前)进行哈希,用可用服务器的权重总数除以哈希值,根据结果进行分配。只要服务器正常,同一个URI地址总是访问同一个服务器。一般用于代理缓存和反病毒代理,以最大限度的提高缓存的命中率。该算法只能用于HTTP后端。(该算法一般用于后端是缓存服务器)。该算法默认是静态的,所以运行时修改服务器的权重是无效的,但是算法会根据“hash-type”的变化做调整。
  • url_param。在HTTP GET请求的查询串中查找<param>中指定的URL参数,基本上可以锁定使用特制的URL到特定的负载均衡器节点的要求。(该算法一般用于将同一个用户的信息发送到同一个后端服务器)。该算法默认是静态的,所以运行时修改服务器的权重是无效的,但是算法会根据“hash-type”的变化做调整。
  • hdr(name)。在每个HTTP请求中查找HTTP头<name>,HTTP头<name>将被看作在每个HTTP请求,并针对特定的节点。(如果缺少头或者头没有任何值,则用roundrobin代替)。该算法默认是静态的,所以运行时修改服务器的权重是无效的,但是算法会根据“hash-type”的变化做调整。
  • rdp-cookie(name)。为每个进来的TCP请求查询并哈希RDP cookie<name>。(该机制用于退化的持久模式,可以使同一个用户或者同一个会话ID总是发送给同一台服务器。如果没有cookie,则使用roundrobin算法代替)。该算法默认是静态的,所以运行时修改服务器的权重是无效的,但是算法会根据“hash-type”的变化做调整。常用的负载均衡算法

HAProxy工作原理

HAProxy提供高可用、负载均衡以及基于TCP(第四层)和HTTP(第七层)的应用的代理,支持虚拟主机,他是免费、快速并且可靠的一种解决方案。HAProxy特别是用于那些负载特别大的web站点,这些站点通常又需要会话保持或七层处理。HAProxy运行在时下的硬件上,完全可以支持数以万计的并发连接,并且它的运行模式使得它可以很简单安全的整合进您当前的架构中,同时可以保护你的web服务器不被暴露到网络上。

HAProxy实现了一种事件驱动、单一进程模型,此模型支持非常大的并发连接数。多进程或多线程模型受内存限制、系统调度器限制以及无处不在的锁限制,很少能处理数千并发链接。事件驱动模型因为在有更好的资源和时间管理的用户端(user-space)实现所有这些任务,所以没有这些问题。此模型的弊端是,在多核系统上,这些程序通常扩展性较差。这就是为什么他们必须进行优化以及使每个CPU时间片(Cycle)做更多的工作。

Haproxy软件引入了frontend,backend的功能,frontend(acl规则匹配)可以根据任意HTTP请求头做规则匹配,然后把请求定向到相关的backend(server pools等待前端把请求转过来的服务器组)。通过frontend和backend,可以很容易的实现Haproxy的7层负载均衡代理功能。

 

访问控制列表(ACL)

在负载均衡中,ACL用于测试某些状况,并根据测试结果执行某些操作(例如选定一套服务器或者屏蔽某条请求),如下ACL示例

acl url_blog path_beg /blog

如果用户请求的路径以/blog 开头,则匹配这条ACL。举例来说,http://www.cnblogs.com/blog/blog-entry-1请求即符合这一条件。

Backend

所谓的backend是指一组服务器,负责接收各转发请求。Backend在HAProxy配置中的backend部分进行定义。一般来讲,backend的定义主要包括:

  • 使用哪种负载均衡算法
  • 一套服务器与端口列表

一条backend能够容纳一套或者多套服务器一一总体而言向backend中添加更多服务器将能够将负载扩散至更多的服务器,从而增加潜在负载容量。这种方式也能够提升可靠性,从而应对服务器发生故障的情况。

下面来看一套双backend配置示例,分别为web-backend与blog-backend,其各自包含两套web服务器,且监听端口80:

backend web-backend
  balance roundrobin
  server web1 web1.kevin.com:80 check
  server web2 web2.kevin.com:80 check
 
backend blog-backend
  balance roundrobin
  mode http
  server blog1 blog1.kevin.com:80 check
  server blog2 blog2.kevin.com:80 check

其中:

  • balance roundrobin行指定负载均衡算法,具体细节参考负载均衡算法部分
  • mode http 则指定所使用的7层代理机制,具体参考负载均衡类型部分
  • 结尾处的check 选项指定在这些后端服务器上执行运行状态检查

Frontend

一条frontend负责定义请求如何被转发至backend。Frontend在HAProxy配置中的frontend部分。其定义由以下几个部分组成:

  • 一组IP地址与一个端口(例如1.1.2:80,*:443,等等)
  • ACL
  • use_backend规则,其负责根据当前ACL条件定义使用哪个backend,而default_backend规则处理一切其它情况

可以将同一frontend配置至多种不同网络流量类型!

同一客户端访问服务器,HAProxy保持会话的三种方案:

  • HAProxy将客户端IP进行Hash计算并保存,由此确保相同IP来访问时被转发到同一台真实服务器上。
  • HAProxy依靠真实服务器发送给客户端的cookie信息进行会话保持。
  • HAProxy保存真实服务器的session及服务器标识,实现会话保持功能。

HAProxy的安装与配置

以下内容出自网络,安装的版本较低,但内容不叫全面,还是有非常的参考意义。

HAProxy的安装

为HAProxy 创建用户和用户组,此例中用户和用户组都是“ha”。注意,如果想要让HAProxy监听1024以下的端口,则需要以root用户来启动。

# 下载并解压
wget http://www.haproxy.org/download/1.7/src/haproxy-1.7.2.tar.gz
tar -xzf haproxy-1.7.2.tar.gz
# 编译并安装
make PREFIX=/home/ha/haproxy TARGET=linux2628
make install PREFIX=/home/ha/haproxy

PREFIX为指定的安装路径,TARGET则根据当前操作系统内核版本指定:

  • linux22 for Linux 2.2
  • linux24 for Linux 2.4 and above (default)
  • linux24e for Linux 2.4 with support for a working epoll (> 0.21)
  • linux26 for Linux 2.6 and above
  • linux2628 for Linux 2.6.28, 3.x, and above (enables splice and tproxy)

此例中,我们的操作系统内核版本为3.10.0,所以TARGET指定为linux2628

创建 HAProxy 配置文件

mkdir -p /home/ha/haproxy/conf
vi /home/ha/haproxy/conf/haproxy.cfg

我们先创建一个最简单配置文件:

global #全局属性
    daemon  #以daemon方式在后台运行
    maxconn 256  #最大同时256连接
    pidfile /home/ha/haproxy/conf/haproxy.pid  #指定保存HAProxy进程号的文件

defaults #默认参数
    mode http  #http模式
    timeout connect 5000ms  #连接server端超时5s
    timeout client 50000ms  #客户端响应超时50s
    timeout server 50000ms  #server端响应超时50s

frontend http-in #前端服务http-in
    bind *:8080  #监听8080端口
    default_backend servers  #请求转发至名为"servers"的后端服务

backend servers #后端服务servers
    server server1 127.0.0.1:8000 maxconn 32  #backend servers中只有一个后端服务,名字叫server1,起在本机的8000端口,HAProxy同时最多向这个服务发起32个连接

注意:HAProxy 要求系统的 ulimit -n 参数大于[maxconn*2+18],在设置较大的 maxconn 时,注意检查并修改 ulimit -n 参数

将 HAProxy 注册为系统服务

在 /etc/init.d 目录下添加 HAProxy 服务的启停脚本:

vi /etc/init.d/haproxy

#! /bin/sh
set -e

PATH=/sbin:/bin:/usr/sbin:/usr/bin:/home/ha/haproxy/sbin
PROGDIR=/home/ha/haproxy
PROGNAME=haproxy
DAEMON=$PROGDIR/sbin/$PROGNAME
CONFIG=$PROGDIR/conf/$PROGNAME.cfg
PIDFILE=$PROGDIR/conf/$PROGNAME.pid
DESC="HAProxy daemon"
SCRIPTNAME=/etc/init.d/$PROGNAME

# Gracefully exit if the package has been removed.
test -x $DAEMON || exit 0

start()
{
       echo -e "Starting $DESC: $PROGNAME\n"
       $DAEMON -f $CONFIG
       echo "."
}

stop()
{
       echo -e "Stopping $DESC: $PROGNAME\n"
       haproxy_pid="$(cat $PIDFILE)"
       kill $haproxy_pid
       echo "."
}

restart()
{
       echo -e "Restarting $DESC: $PROGNAME\n"
       $DAEMON -f $CONFIG -p $PIDFILE -sf $(cat $PIDFILE)
       echo "."
}

case "$1" in
 start)
       start
       ;;
 stop)
       stop
       ;;
 restart)
       restart
       ;;
 *)
       echo "Usage: $SCRIPTNAME {start|stop|restart}" >&2
       exit 1
       ;;
esac

exit 0

启动、停止和重启

service haproxy start
service haproxy stop
service haproxy restart

添加日志

HAProxy 不会直接输出文件日志,所以我们要借助 Linux 的 rsyslog 来让 HAProxy 输出日志。

修改haproxy.cfg,在 global 域和 defaults 域中添加:

global
    ...
    log 127.0.0.1 local0 info
    log 127.0.0.1 local1 warning
    ...

defaults
    ...
    log global
    ...

意思是将 info级(及以上)的日志推送到rsyslog的local0接口,将warn级(及以上)的日志推送到rsyslog的local1接口,并且所有frontend都默认使用global中的日志配置。

注:info级的日志会打印HAProxy处理的每一条请求,会占用很大的磁盘空间,在生产环境中,建议将日志级别调整为notice

为 rsyslog 添加 haproxy 日志的配置

vi /etc/rsyslog.d/haproxy.conf
$ModLoad imudp
$UDPServerRun 514
$FileCreateMode 0644  #日志文件的权限
$FileOwner ha  #日志文件的owner
local0.*     /var/log/haproxy.log  #local0接口对应的日志输出文件
local1.*     /var/log/haproxy_warn.log  #local1接口对应的日志输出文件

修改 rsyslog 的启动参数

vi /etc/sysconfig/rsyslog
# Options for rsyslogd
# Syslogd options are deprecated since rsyslog v3.
# If you want to use them, switch to compatibility mode 2 by "-c 2"
# See rsyslogd(8) for more details
SYSLOGD_OPTIONS="-c 2 -r -m 0"

重启 rsyslog 和 HAProxy

service rsyslog restart
service haproxy restart

此时就应该能在/var/log目录下看到haproxy的日志文件了

用 logrotate 进行日志切分

通过 rsyslog 输出的日志是不会进行切分的,所以需要依靠 Linux 提供的 logrotate (Linux系统 Logrotate服务介绍)来进行切分工作

使用 root 用户,创建 haproxy 日志切分配置文件:

mkdir /root/logrotate
vi /root/logrotate/haproxy
/var/log/haproxy.log /var/log/haproxy_warn.log {  #切分的两个文件名
    daily        #按天切分
    rotate 7     #保留7份
    create 0644 ha ha  #创建新文件的权限、用户、用户组
    compress     #压缩旧日志
    delaycompress  #延迟一天压缩
    missingok    #忽略文件不存在的错误
    dateext      #旧日志加上日志后缀
    sharedscripts  #切分后的重启脚本只运行一次
    postrotate   #切分后运行脚本重载rsyslog,让rsyslog向新的日志文件中输出日志
      /bin/kill -HUP $(/bin/cat /var/run/syslogd.pid 2>/dev/null) &>/dev/null
    endscript
}
并配置在 crontab 中运行:
0 0 * * * /usr/sbin/logrotate /root/logrotate/haproxy

HAProxy 搭建 L7 负载均衡器

总体方案

本节中,我们将使用 HAProxy 搭建一个 L7 负载均衡器,应用如下功能:负载均衡、会话保持、健康检查、根据URI前缀向不同的后端集群转发、监控页面。架构如下:

架构中共有6个后端服务,划分为3组,每组中2个服务:

  • ms1:服务URI前缀为ms1/的请求
  • ms2:服务URI前缀为ms2/的请求
  • def:服务其他请求

搭建后端服务

部署6个后端服务,可以使用任意的Web服务,如Nginx、Apache HTTPD、Tomcat、Jetty等,具体Web服务的安装过程省略。

此例中,我们在192.168.8.111和192.168.8.112两台主机上分别安装了3个Nginx:

  • srv1 – 192.168.8.111:8080
  • srv2 – 192.168.8.112:8080
  • srv1 – 192.168.8.111:8081
  • srv2 – 192.168.8.112:8081
  • srv1 – 192.168.8.111:8082
  • srv2 – 192.168.8.112:8082

在这 6个 Nginx 服务分别部署健康检查页面 healthCheck.html,页面内容任意。确保通过http://ip:port/healthCheck.html 可以访问到这个页面

接下来在6个 Nginx 服务中部署服务页面:

  • 在第一组中部署ms1/demo.html
  • 在第二组中部署ms2/demo.html
  • 在第三组中部署def/demo.html

demo.html的内容,以部署在192.168.8.111:8080上的为例:Hello! This is ms1.srv1! 部署在 192.168.8.112:8080 上的就应该是Hello! This is ms1.srv2!以此类推。

搭建 HAProxy

在 192.168.8.110 主机安装 HAProxy,HAProxy 的安装和配置步骤如上一章中描述,此处略去。

HAProxy 配置文件:

global
    daemon
    maxconn 30000   #ulimit -n至少为60018
    user ha
    pidfile /home/ha/haproxy/conf/haproxy.pid
    log 127.0.0.1 local0 info
    log 127.0.0.1 local1 warning

defaults
    mode http
    log global
    option http-keep-alive   #使用keepAlive连接
    option forwardfor        #记录客户端IP在X-Forwarded-For头域中
    option httplog           #开启httplog,HAProxy会记录更丰富的请求信息
    timeout connect 5000ms
    timeout client 10000ms
    timeout server 50000ms
    timeout http-request 20000ms    #从连接创建开始到从客户端读取完整HTTP请求的超时时间,用于避免类DoS攻击
    option httpchk GET /healthCheck.html    #定义默认的健康检查策略

frontend http-in
    bind *:9001
    maxconn 30000                    #定义此端口上的maxconn
    acl url_ms1 path_beg -i /ms1/    #定义ACL,当uri以/ms1/开头时,ACL[url_ms1]为true
    acl url_ms2 path_beg -i /ms2/    #同上,url_ms2
    use_backend ms1 if url_ms1       #当[url_ms1]为true时,定向到后端服务群ms1中
    use_backend ms2 if url_ms2       #当[url_ms2]为true时,定向到后端服务群ms2中
    default_backend default_servers  #其他情况时,定向到后端服务群default_servers中

backend ms1    #定义后端服务群ms1
    balance roundrobin    #使用RR负载均衡算法
    cookie HA_STICKY_ms1 insert indirect nocache    #会话保持策略,insert名为"HA_STICKY_ms1"的cookie
    #定义后端server[ms1.srv1],请求定向到该server时会在响应中写入cookie值[ms1.srv1]
    #针对此server的maxconn设置为300
    #应用默认健康检查策略,健康检查间隔和超时时间为2000ms,两次成功视为节点UP,三次失败视为节点DOWN
    server ms1.srv1 192.168.8.111:8080 cookie ms1.srv1 maxconn 300 check inter 2000ms rise 2 fall 3
    #同上,inter 2000ms rise 2 fall 3是默认值,可以省略
    server ms1.srv2 192.168.8.112:8080 cookie ms1.srv2 maxconn 300 check

backend ms2    #定义后端服务群ms2
    balance roundrobin
    cookie HA_STICKY_ms2 insert indirect nocache
    server ms2.srv1 192.168.8.111:8081 cookie ms2.srv1 maxconn 300 check
    server ms2.srv2 192.168.8.112:8081 cookie ms2.srv2 maxconn 300 check

backend default_servers    #定义后端服务群default_servers
    balance roundrobin
    cookie HA_STICKY_def insert indirect nocache
    server def.srv1 192.168.8.111:8082 cookie def.srv1 maxconn 300 check
    server def.srv2 192.168.8.112:8082 cookie def.srv2 maxconn 300 check

listen stats    #定义监控页面
    bind *:1080                   #绑定端口1080
    stats refresh 30s             #每30秒更新监控数据
    stats uri /stats              #访问监控页面的uri
    stats realm HAProxy\ Stats    #监控页面的认证提示
    stats auth admin:admin        #监控页面的用户名和密码

修改完成后,启动 HAProxy。

service haproxy start

测试

首先,访问一下监控页面 http://192.168.8.110:1080/stats 并按提示输入用户名密码接下来就能看到监控页面:

监控页面中列出了我们配置的所有frontend和backend服务,以及它们的详细指标。如连接数,队列情况,session rate,流量,后端服务的健康状态等等

接下来,我们一一测试在HAProxy中配置的功能

健康检查

从监控页面中就可以直接看出健康检查配置的是否正确,上图中可以看到,backend ms1、ms2、default_servers 下属的 6 个后端服务的 Status 都是 20h28m UP,代表健康状态已持续了 20 小时 28 分钟,而 LastChk 显示 L7OK/200 in 1ms 则代表在 1ms 前进行了 L7 的健康检查(即HTTP请求方式的健康检查),返回码为200。

此时我们将 ms1.srv1 中的 healthCheck.html 改名,mv healthCheck.html healthCheck.html.bak,然后再去看监控页面。ms1.srv1 的状态变成了2s DOWN,LastChk 则是 L7STS/404 in 2ms,代表上次健康检查返回了 404,再恢复 healthCheck.html,很快就能看到 ms1.srv1 重新恢复到 UP 状态。

负载均衡和会话保持策略

在分别访问过 ms1/demo.html,ms2/demo.html,m3/demo.html 后,查看一下浏览器的 Cookie。可以看到 HAProxy 已经回写了三个用于会话保持的 cookie,此时反复刷新这三个页面,会发现总是被定向到 *.srv1上接下来我们删除 HA_STICKY_ms1 这条 cookie,然后再访问 ms1/demo.html,会看到同时也被新写入了一条 Cookie。如果发现仍然被定位到 ms1.srv1,同时也没有写入新的 HA_STICKY_ms1 Cookie,那么可能是浏览器缓存了 ms1/demo.html 页面,请求并没有到达 HAProxy。F5刷新一下应该就可以了。

HAProxy 搭建 L4 负载均衡器

HAProxy 作为 L4 负载均衡器工作时,不会去解析任何与 HTTP 协议相关的内容,只在传输层对数据包进行处理。也就是说,以 L4 模式运行的 HAProxy,无法实现根据 URL向不同后端转发、通过 cookie 实现会话保持等功能。同时,在 L4 模式下工作的 HAProxy 也无法提供监控页面。但作为 L4 负载均衡器的 HAProxy 能够提供更高的性能,适合于基于套接字的服务(如数据库、消息队列、RPC、邮件服务、Redis等),或不需要逻辑规则判断,并已实现了会话共享的 HTTP 服务。

总体方案

本例中,我们使用 HAProxy 以 L4 方式来代理两个 HTTP 服务,不提供会话保持。

global
    daemon
    maxconn 30000   #ulimit -n至少为60018
    user ha
    pidfile /home/ha/haproxy/conf/haproxy.pid
    log 127.0.0.1 local0 info
    log 127.0.0.1 local1 warning

defaults
    mode tcp
    log global
    option tcplog            #开启tcplog
    timeout connect 5000ms
    timeout client 10000ms
    timeout server 10000ms   #TCP模式下,应将timeout client和timeout server设置为一样的值,以防止出现问题
    option httpchk GET /healthCheck.html    #定义默认的健康检查策略

frontend http-in
    bind *:9002
    maxconn 30000                    #定义此端口上的maxconn
    default_backend default_servers  #请求定向至后端服务群default_servers

backend default_servers    #定义后端服务群default_servers
    balance roundrobin
    server def.srv1 192.168.8.111:8082 maxconn 300 check
    server def.srv2 192.168.8.112:8082 maxconn 300 check

L4模式下的会话保持

虽然 TCP 模式下的 HAProxy 无法通过 HTTP Cookie 实现会话保持,但可以很方便的实现基于客户端IP的会话保持。只需将balance roundrobin改为balance source.

此外,HAProxy 提供了强大的 stick-table 功能,HAProxy 可以从传输层的数据包中采样出大量的属性,并将这些属性作为会话保持的策略写入 stick-table 中。

HAProxy关键配置详解

HAProxy 的配置文件共有5个域

  • global:用于配置全局参数
  • default:用于配置所有frontend和backend的默认属性
  • frontend:用于配置前端服务(即HAProxy自身提供的服务)实例
  • backend:用于配置后端服务(即HAProxy后面接的服务)实例组
  • listen:frontend+backend的组合配置,可以理解成更简洁的配置方法

global 域的关键配置

  • daemon:指定HAProxy以后台模式运行,通常情况下都应该使用这一配置
  • user [username] :指定HAProxy进程所属的用户
  • group [groupname] :指定HAProxy进程所属的用户组
  • log [address] [device] [maxlevel] [minlevel]:日志输出配置,如log 127.0.0.1 local0 info warning,即向本机rsyslog或syslog的local0输出info到warning级别的日志。其中[minlevel]可以省略。HAProxy的日志共有8个级别,从高到低为emerg/alert/crit/err/warning/notice/info/debug
  • pidfile :指定记录HAProxy进程号的文件绝对路径。主要用于HAProxy进程的停止和重启动作。
  • maxconn :HAProxy进程同时处理的连接数,当连接数达到这一数值时,HAProxy将停止接收连接请求

frontend 域的关键配置

  • acl [name] [criterion] [flags] [operator] [value]:定义一条ACL,ACL是根据数据包的指定属性以指定表达式计算出的true/false值。如”acl url_ms1 path_beg -i /ms1/”定义了名为url_ms1的ACL,该ACL在请求uri以/ms1/开头(忽略大小写)时为true
  • bind [ip]:[port]:frontend服务监听的端口
  • default_backend [name]:frontend对应的默认backend
  • disabled:禁用此frontend
  • http-request [operation] [condition]:对所有到达此frontend的HTTP请求应用的策略,例如可以拒绝、要求认证、添加header、替换header、定义ACL等等。
  • http-response [operation] [condition]:对所有从此frontend返回的HTTP响应应用的策略,大体同上
  • log:同global域的log配置,仅应用于此frontend。如果要沿用global域的log配置,则此处配置为log global
  • maxconn:同global域的maxconn,仅应用于此frontend
  • mode:此frontend的工作模式,主要有http和tcp两种,对应L7和L4两种负载均衡模式
  • option forwardfor:在请求中添加X-Forwarded-For Header,记录客户端ip
  • option http-keep-alive:以KeepAlive模式提供服务
  • option httpclose:与http-keep-alive对应,关闭KeepAlive模式,如果HAProxy主要提供的是接口类型的服务,可以考虑采用httpclose模式,以节省连接数资源。但如果这样做了,接口的调用端将不能使用HTTP连接池
  • option httplog:开启httplog,HAProxy将会以类似Apache HTTP或Nginx的格式来记录请求日志
  • option tcplog:开启tcplog,HAProxy将会在日志中记录数据包在传输层的更多属性
  • stats uri [uri]:在此frontend上开启监控页面,通过[uri]访问
  • stats refresh [time]:监控数据刷新周期
  • stats auth [user]:[password]:监控页面的认证用户名密码
  • timeout client [time]:指连接创建后,客户端持续不发送数据的超时时间
  • timeout http-request [time]:指连接创建后,客户端没能发送完整HTTP请求的超时时间,主要用于防止DoS类攻击,即创建连接后,以非常缓慢的速度发送请求包,导致HAProxy连接被长时间占用
  • use_backend [backend] if|unless [acl]:与ACL搭配使用,在满足/不满足ACL时转发至指定的backend

backend 域的关键配置

  • acl:同frontend域
  • balance [algorithm]:在此backend下所有server间的负载均衡算法,常用的有roundrobin和source,完整的算法说明见官方文档html#4.2-balance
  • cookie:在backend server间启用基于cookie的会话保持策略,最常用的是insert方式,如cookie HA_STICKY_ms1 insert indirect nocache,指HAProxy将在响应中插入名为HA_STICKY_ms1的cookie,其值为对应的server定义中指定的值,并根据请求中此cookie的值决定转发至哪个server。indirect代表如果请求中已经带有合法的HA_STICK_ms1 cookie,则HAProxy不会在响应中再次插入此cookie,nocache则代表禁止链路上的所有网关和缓存服务器缓存带有Set-Cookie头的响应。
  • default-server:用于指定此backend下所有server的默认设置。具体见下面的server配置。
  • disabled:禁用此backend
  • http-request/http-response:同frontend域
  • log:同frontend域
  • mode:同frontend域
  • option forwardfor:同frontend域
  • option http-keep-alive:同frontend域
  • option httpclose:同frontend域
  • option httpchk [METHOD] [URL] [VERSION]:定义以http方式进行的健康检查策略。如option httpchk GET /healthCheck.html HTTP/1.1
  • option httplog:同frontend域
  • option tcplog:同frontend域
  • server [name] [ip]:[port] [params]:定义backend中的一个后端server,[params]用于指定这个server的参数,常用的包括有:
  • check:指定此参数时,HAProxy将会对此server执行健康检查,检查方法在option httpchk中配置。同时还可以在check后指定inter, rise, fall三个参数,分别代表健康检查的周期、连续几次成功认为server UP,连续几次失败认为server DOWN,默认值是inter 2000ms rise 2 fall 3
  • cookie [value]:用于配合基于cookie的会话保持,如cookie ms1.srv1代表交由此server处理的请求会在响应中写入值为srv1的cookie(具体的cookie名则在backend域中的cookie设置中指定)
  • maxconn:指HAProxy最多同时向此server发起的连接数,当连接数到达maxconn后,向此server发起的新连接会进入等待队列。默认为0,即无限
  • maxqueue:等待队列的长度,当队列已满后,后续请求将会发至此backend下的其他server,默认为0,即无限
  • weight:server的权重,0-256,权重越大,分给这个server的请求就越多。weight为0的server将不会被分配任何新的连接。所有server默认weight为1
  • timeout connect [time]:指HAProxy尝试与backend server创建连接的超时时间
  • timeout check [time]:默认情况下,健康检查的连接+响应超时时间为server命令中指定的inter值,如果配置了timeout check,HAProxy会以inter作为健康检查请求的连接超时时间,并以timeout check的值作为健康检查请求的响应超时时间
  • timeout server [time]:指backend server响应HAProxy请求的超时时间

default 域

上文所属的frontend和backend域关键配置中,除acl、bind、http-request、http-response、use_backend外,其余的均可以配置在default域中。default域中配置了的项目,如果在frontend或backend域中没有配置,将会使用default域中的配置。

listen 域

listen域是frontend域和backend域的组合,frontend域和backend域中所有的配置都可以配置在listen域下。

使用Keepalived实现HAProxy高可用

尽管 HAProxy 非常稳定,但仍然无法规避操作系统故障、主机硬件故障、网络故障甚至断电带来的风险。所以必须对 HAProxy 实施高可用方案。下文将介绍利用 Keepalived 实现的 HAProxy 热备方案。即两台主机上的两个 HAProxy实例同时在线,其中权重较高的实例为 MASTER,MASTER 出现问题时,另一台实例自动接管所有流量。

原理

在两台 HAProxy 的主机上分别运行着一个 Keepalived 实例,这两个 Keepalived 争抢同一个虚IP地址,两个 HAProxy 也尝试去绑定这同一个虚IP地址上的端口。显然,同时只能有一个 Keepalived 抢到这个虚 IP,抢到了这个虚 IP 的 Keepalived 主机上的 HAProxy 便是当前的 MASTER。Keepalived 内部维护一个权重值,权重值最高的 Keepalived 实例能够抢到虚IP。同时 Keepalived 会定期 check 本主机上的 HAProxy 状态,状态OK时权重值增加。

搭建 HAProxy 主备集群

在两台物理机上安装并配置 HAProxy,本例中,将在 192.168.8.110 和 192.168.8.111 两台主机上上安装两套完全一样的 HAProxy,具体步骤省略,请参考“使用 HAProxy 搭建L7负载均衡器”一节。

安装Keepalived

下载,解压,编译,安装:

wget http://www.keepalived.org/software/keepalived-1.2.19.tar.gz
tar -xzf keepalived-1.2.19.tar.gz
./configure --prefix=/usr/local/keepalived
make
make install

注册为系统服务:

cp /usr/local/keepalived/sbin/keepalived /usr/sbin/
cp /usr/local/keepalived/etc/sysconfig/keepalived /etc/sysconfig/
cp /usr/local/keepalived/etc/rc.d/init.d/keepalived /etc/init.d/
chmod +x /etc/init.d/keepalived

注意:Keepalived 需要使用 root 用户进行安装和配置

配置 Keepalived

创建并编辑配置文件

mkdir -p /etc/keepalived/
cp /usr/local/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/
vi /etc/keepalived/keepalived.conf

配置文件内容:

global_defs {
    router_id LVS_DEVEL  #虚拟路由名称
}

#HAProxy健康检查配置
vrrp_script chk_haproxy {
    script "killall -0 haproxy"  #使用killall -0检查haproxy实例是否存在,性能高于ps命令
    interval 2   #脚本运行周期
    weight 2   #每次检查的加权权重值
}

#虚拟路由配置
vrrp_instance VI_1 {
    state MASTER           #本机实例状态,MASTER/BACKUP,备机配置文件中请写BACKUP
    interface enp0s25      #本机网卡名称,使用ifconfig命令查看
    virtual_router_id 51   #虚拟路由编号,主备机保持一致
    priority 101           #本机初始权重,备机请填写小于主机的值(例如100)
    advert_int 1           #争抢虚地址的周期,秒
    virtual_ipaddress {
        192.168.8.201      #虚地址IP,主备机保持一致
    }
    track_script {
        chk_haproxy        #对应的健康检查配置
    }
}

如果主机没有killall命令,则需要安装psmisc包:

yum intall psmisc

分别启动两个Keepalived

service keepalived start

验证

启动后,先分别在两台主机查看虚IP 192.168.8.201由谁持有,执行命令:ip addr sh enp0s25   (将enp0s25替换成主机的网卡名)

如果你先启动备机的 Keepalived,那么很有可能虚 IP 会被备机抢到,因为备机的权重配置只比主机低1,只要执行一次健康检查就能把权重提高到 102,高于主机的 101。此时访问 http://192.168.8.201:9001/ms1/demo.html ,可以看到我们先前部署的网页。此时,检查/var/log/haproxy.log,能看到此请求落在了抢到了虚IP的主机上。接下来,我们停掉当前 MASTER 主机的 HAProxy 实例(或者Keepalive实例,效果一样)

service haproxy stop

再次访问 http://192.168.8.201:9001/ms1/demo.html ,并查看备机的 /var/log/haproxy.log,会看到此请求落在了备机上,主备自动切换成功。也可以再次执行ip addr sh enp0s25命令,会看到虚IP被备机抢去了。在/var/log/message中,也能够看到keepalived输出的切换日志。

参考链接:

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注