返回介绍

最佳实践 27:安装与优化

发布于 2025-04-20 17:44:42 字数 9756 浏览 0 评论 0 收藏

HAProxy 的安装过程比较简单,参照如下命令:

wget http://www.haproxy.org/download/1.5/src/haproxy-1.5.15.tar.gz
tar zxvf haproxy-1.5.15.tar.gz 
cd haproxy-1.5.15
make TARGET=linux26
make install

前一小节讲到 HAProxy 同时支持对 TCP 和 HTTP 的负载均衡,那就先从对 TCP 的负载均衡开始实践。

HAProxy TCP 负载均衡

HAProxy 的 TCP 负载均衡方式,与第 4 章中提到 LVS 的 3 种模式均不同。

- HAProxy 不要求后端服务器的网关指向负载均衡器的内网地址,在 LVS-NAT 模式下,该配置为必须。

- HAProxy 不要求后端服务器和负载均衡处于同一个网段,在 LVS-DR 模式下,这个是前提条件。

- HAProxy 不要求后端服务器配置 IPIP 隧道,这个与 LVS-Tun 模式不同。

- HAProxy 仅仅要求后端服务器能够在网络上连通,可以跨网段。

本次配置实现的架构图如图 5-1 所示。

图 5-1 HAProxy TCP 负载均衡架构图

在 HAProxy 安装完成后,启用 haproxy.cfg 进行配置,如下所示:

# cat /etc/haproxy/haproxy.cfg 
global
    daemon
    maxconn 50000
defaults
    mode http
listen www
    bind 0.0.0.0:80
    mode tcp #注意,此处是 tcp
    server s1 10.1.6.21:80 #后端服务器 1 的 IP 和端口
    server s2 10.1.6.44:80 #后端服务器 2 的 IP 和端口

在测试机器上使用 curl 命令测试,2 台后端服务器都会被访问到。使用命令如下:

# curl http://10.1.6.28/index.html
web1
# curl http://10.1.6.28/index.html
web2

HAProxy HTTP 负载均衡

HTTP 负载均衡的架构图和图 5-1 相同,基于 7 层负载均衡,要求实现以下功能。

- 请求域名 www1.example.com 的访问调度到 web1。

- 请求域名 www2.example.com 的访问调度到 web2。

本案例中使用到的配置文件内容如下:

global
        daemon
        maxconn 50000
        log 127.0.0.1 local0
        uid 99
        gid 99
        pidfile /var/run/haproxy-private.pid
        chroot  /var/empty #chroot 增加系统安全性
        
frontend public
        bind    0.0.0.0:80
        mode    http #注意,此处是 http
        log     global
        timeout http-request 3s #客户端必须在 3s 内传输完成请求
        timeout http-keep-alive 3s #和客户端的 keep-alive 时间
        timeout client 10s #和客户端的连接保活超时

        option  httplog
        option  forwardfor #使用 X-Forwarded-For 向后端服务器传递客户端来源 IP
        option  dontlognull
        option http-keep-alive #使用 keep-alive,减少对后端服务器的连接数

        capture request header Host len 30 #日志中记录 Host 头部
        capture request header Referer len 60 #日志中记录 Referer
#以下 5 行配置,使 HAProxy 根据请求里面的 Host 字段进行负载均衡
        acl www1_example_com hdr_beg(host) -i    www1.example.com
        use_backend backend_www1_example_com    if www1_example_com
        acl www2_example_com hdr_beg(host) -i    www2.example.com
        use_backend backend_www2_example_com    if www2_example_com
        default_backend  backend_www1_example_com

backend backend_www1_example_com
        mode            http #模式为 http
        balance         source #使用基于源地址的分配方法
        cookie          appsession insert indirect preserve 
        timeout http-request 3s
        timeout http-keep-alive 3s
        timeout connect 1s
        timeout server 10s
        timeout check  500ms

        option redispatch #后端服务器故障时,重新分发请求
        option http-keep-alive
        option httpchk GET /test.html HTTP/1.1\r\nHost:\ www1.example.com #基于 http 的监控检查方法
        http-check expect string ok
        retries        2

        server          www1_example_com_srv1 10.1.6.21:80 cookie b1 weight 8 check inter 2s rise 3 fall 5
        stats enable #HAProxy 状态监控使用
        stats scope   .
        stats uri     /admin?stats
        stats realm   Haproxy\ Statistics
        stats auth    admin:6w5_xbkRGU

backend backend_www2_example_com
        mode            http
        balance         source
        cookie          appsession insert indirect preserve
        timeout http-request 3s
        timeout http-keep-alive 3s
        timeout connect 1s
        timeout server 10s
        timeout check  500ms

        option redispatch
        option http-keep-alive
        option httpchk GET /test.html HTTP/1.1\r\nHost:\ www2.example.com
        http-check expect string ok
        retries         2

        server          www2_example_com_srv1 10.1.6.44:80 cookie b2 weight 8 check inter 2s rise 3 fall 5
        stats enable
        stats scope   .
        stats uri     /admin?stats
        stats realm   Haproxy\ Statistics
        stats auth    admin:6w5_xbkRGU

注意

默认 HAProxy 会把日志记录到/var/log/messages 文件,本例配置中它单独记录到/app/logs/haproxy.log 中。

配置 HAProxy 把访问日志记录到/app/logs/haproxy.log 需要执行以下操作:

# /etc/sysconfig/syslog ==> SYSLOGD_OPTIONS="-r -m 0"
# /etc/syslog.conf ==> local0.*    
# chown nobody.nobody /app/logs/haproxy.log 
# /etc/init.d/syslog restart

HAProxy 的核心配置参数

HAProxy 的核心配置参数如下:

1)mode{tcp|http|health}:设置该实例运行的协议。可配置的值有如下几个。

- tcp:该实例运行在纯 TCP 模式,在客户端和服务器之间建立一个全双工的通道,不会进行任何 7 层的内容检查,为默认值。在负载均衡 SMTP 和 SSH 等时,必须使用该模式。

- http:该实例运行在 HTTP 模式,在被调度到后端服务器之前,客户端的请求会被深度分析,任何不符合 RFC 兼容的请求都被拒绝掉。可以实现 7 层过滤、处理、内容交换。在这种模式下,HAProxy 可以发挥最大的价值。

2)balance<algorithm>[<arguments>]:负载均衡算法。

- <algorithm>:是在选择一台后端服务器时使用到的算法。在没有其他可以作为持久连接的信息时,使用该算法进行调度。一般用到以下有的几个。

- roundrobin:轮询。

- static-rr:轮询,在线修改权重时不生效。

- leastconn:最小连接数。

- first:有可用连接的服务器组里面选择一台后端服务器(没有达到 maxconn 的)。先让一台达到 maxconn,然后再使用另外一台没达到的。

- source:基于客户端来源的哈希。

- uri:根据 uri 的部分或者完整 uri 进行哈希。

3)acl:HAProxy 的配置里面,最能体现灵活性的地方是 ACL(Access Control List,访问控制链),配置指令是 acl。ACL 的使用,提供了一个灵活的方法,根据从请求、响应或者任何环境状态里面解析出来的内容来实现内容交换和做决策(比如丢弃、变换或转发等)。

在该指令(acl)中,我们可以指定使用如下规则予以匹配需要特殊处理的请求:

- 3 层网络层的匹配:dst,src 目的 IP 和源 IP。

- 4 层 TCP 的匹配:dst_port,src_port 目的端口和源端口。

- 7 层应用层信息匹配:req.hdr([<name>[,<occ>]])匹配 HTTP 请求里面的 Header 字段。例如 req.hdr(Host)则匹配 HTTP 请求 Header 里面的 Host 字段。

HAProxy 的会话保持机制

会话保持,是指在应用中,保证同一个客户端的连续的请求被持续地调度到一台后端服务器的过程。在以下业务场景中需要用到此种技术。

- 购物车。里面存储了客户端已确认购买的商品列表,需要由同一台后端服务器进行处理。

- 访问登录后的内容。接受客户端登录的服务器上存储了其临时信息作为有效性的判断,后续的请求必须调度到同一台后端服务器,否则可能被其他后端服务器检测为未登录状态。

HAProxy 有 2 种基本的方法。

- 使用基于源地址的负载调度算法。配置指令是:

balance source

- 使用基于 cookie 的技术。配置指令是:

cookie          appsession insert indirect preserve

这个是什么意思呢?举个例子。

假设李三(客户端)第一次到派出所办事(需要访问业务),到了办事大厅之后,引导员(HAProxy)根据李三要办理的业务内容,将李三的业务提交给了一个民警 X(后端服务器),民警完成后把处理结果给了引导员(处理结果),引导员看到李三第一次来,就额外给了李三一个令牌,是一个字符串(cookie);下次李三再来办理业务的时候,出具了该令牌,则引导员可以迅速判断是由原来的民警 X 来处理他的业务。

这样就保持了会话的连续性。下面来看具体指令。

appsession 是指 HAProxy 在第一次响应中插入到后端服务器给客户端的响应里面的 cookie 名字。

insert 是指如果 HAProxy 没有在用户的请求中发现该 cookie,那么在后端服务器给客户端的响应中,它会加入该 cookie。如果没有和 preserve 联合使用,那么后端服务器发送的响应中若有相同名字(appsession)的 cookie,则同名 cookie 会被删除,这就可能导致问题。

indirect 是指如果 HAProxy 发现客户端已经有该名字(appsession)的 cookie,则不操作;如果和 insert 合用,在向后端服务器转发请求的时候,会把该名字(appsession)的 cookie 删除,此时对后端服务器端完全透明。

preserve 与 insert 和/或 indirect 合用,使得后端服务器发送该名字(appsession)的 cookie 时不被 HAProxy 删除或者替换掉。

理解 HAProxy 的会话保持机制,在进行排查问题时具有很重要的作用。

下面看一下效果:

# wget -S --header="Host: www1.example.com" http://10.1.6.18/index.html
--2015-12-07 15:32:58--  http://10.1.6.18/index.html
Connecting to 10.1.6.18:80... connected.
HTTP request sent, awaiting response... 
  HTTP/1.1 200 OK
  Server: nginx/0.8.55
  Date: Mon, 07 Dec 2015 07:32:58 GMT
  Content-Type: text/html
  Content-Length: 5
  Last-Modified: Mon, 30 Nov 2015 09:53:22 GMT
  Accept-Ranges: bytes
  Set-Cookie: appsession=b1; path=/ #注意该行,即是 HAProxy 插入的 cookie。作为客户端后续请求选择后端服务器的依据
  Connection: close
Length: 5 [text/html]
Saving to: `index.html'

100%[================================================================================================================================================================>] 5           --.-K/s   in 0s      

2015-12-07 15:32:58 (610 KB/s) - `index.html' saved [5/5]

HAProxy 中 ip_local_port_range 问题

前面的章节使用 HAProxy 配置了两种模式的负载均衡:TCP 和 HTTP。在这两种模式下,都需要注意负载均衡器上的 ip_local_port_range 问题。

HAProxy 在 TCP 和 HTTP 负载均衡时,都是使用本机的 TCP 端口作为源端口、本机的 IP 地址作为源地址向后端服务器进行转发。此时,请注意单个 IP 可以使用到的 TCP 源端口号是有限制的,使用如下命令可以看到当前服务器配置的范围:

# sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 32768    61000 #单个 IP 可以使用的 TCP 端口范围是 32768 到 61000

在代理了多台(如 20 台以上)后端服务器并且并发访问量比较大时,需要注意该参数。如果负载均衡器对后端服务器发起的 TCP 连接数过多,则可能导致负载均衡器本地端口用光(使用 netstat 统计),无法向后端服务器建立新的 TCP 连接导致负载均衡失败。

使用如下命令修改该参数的值,增加可用端口号:

echo 1024 61000 > /proc/sys/net/ipv4/ip_local_port_range

在 sysctl.conf 中进行修改,以便在服务器重启后依然有效:

# echo "net.ipv4.ip_local_port_range=1024 61000" >> /etc/sysctl.conf

HAProxy 后端服务器获取客户端 IP

虽然 HAProxy 支持 TCP 和 HTTP 的负载均衡,但这 2 种方式在网络层面,无法使得后端服务器直接获取到客户端的 IP,后端服务器上看到的来源 IP 是 HAProxy 的内网 IP。

后端服务器的程序在审计需要或者需要对来源 IP 做某些限制时,怎么才能获取到客户端的 IP 呢?

通常的做法是配置如下命令:

option  forwardfor

此时,后端服务器可以使用分析请求中的 X-Forwarded-For 字段来解析客户端来源 IP。

在 PHP 中的代码如下:

$_SERVER["HTTP_X_FORWARDED_FOR"]

TCP 负载均衡和 HTTP 负载均衡的对比

对于同一个负载均衡的需求,配置 HAProxy 分别使用 TCP 和 HTTP 的方式进行了负载均衡转发,从结果上看是相同的,但在 HAProxy 内部的实现上,是否完全相同呢?

先看看 TCP 方式下,HAProxy 上的网络行为(文件:HAProxy_TCP.pcap,Frame 23、24、25)如图 5-2 所示。

图 5-2 HAProxy TCP 负载均衡网络行为

客户端在和 HAProxy 完成 3 次握手(Frame 23、24、25)建立了 TCP 连接后,HAProxy 立即向后端发送了 SYN 包(Frame 26),要求和其选择的后端服务器建立 TCP 连接。

在 HTTP 模式下,HAProxy 上的网络行为(文件:HAProxy_HTTP.pcap,Frame 58、59、60)如图 5-3 所示。

图 5-3 HAProxy HTTP 负载均衡网络行为

客户端在和 HAProxy 完成 3 次握手(Frame 58、59、60)建立了 TCP 连接,客户端发送了 TCP Data(Frame 61),也就是 HTTP 请求后,HAProxy 才开始向后端服务器发送 SYN 包(Frame 62),要求建立 TCP 连接。

在应用实践中,如基于 HTTP 的应用,一般建议使用 HTTP 模式进行调度,这样,可以对后端服务器进行更精确的调度,比如使用 Host 头部、URI 规则等,都可以作为调度的匹配依据。

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。