linuxea: HAproxy Maps简介 蓝绿部署


HAProxy映射文件存储键值对,并且是一些创造性行为的起点,包括动态速率限制和蓝绿色部署。

字典。Map。哈希值。关联数组。这些列为HAProxy负载均衡器的广泛功能集。它们被称为map。

想要设置蓝绿色部署?也许你想通过URL路径设置速率限制?如何动态切换哪些后端服务器用于域?这一切都是用map完成的!

在本文中,你将学习如何创建map文件,将其存储在系统中,在HAProxy配置中引用它,并实时更新。你还将看到一些有用的场景,可以充分利用你的新知识。

I. 入门

在考虑使用Map文件可以做的有趣事情之前,让我们围绕Map文件进行思考。

map file

一切都始于创建一个map文件。创建名为hosts.map的文件。然后添加以下行:

[root@www.linuxea.com ~]# mkdir /etc/haproxy/maps/
[root@www.linuxea.com ~]# cat /etc/haproxy/maps/hosts.map 
static.linuxea.com  be_static
www.linuxea.com     be_static

linuxea.com         be_static
api.linuxea.com     be_api

有关此文件结构的一些注意事项:

  • 这是纯文本
  • 每行一个Key
  • 一个value出现在一个键之后,由至少一个空格隔开(例如be_static)
  • 单词之间的空行和额外空格将被忽略
  • 注释必须以#号开头,并且必须在各自的行开头

映射文件存储键值对。 HAProxy将它们用作查找表,例如根据Host标头的值找出将客户端路由到哪个后端。将此关联存储在文件而不是HAProxy配置本身的好处是能够动态更改这些值。

接下来,将此文件传输到服务器测试,并将其放入你选择的目录中。 HAProxy启动时会加载映射文件,但正如你将看到的那样,它们可以在运行时修改而无需重新加载

Map Converters

为了更好的了解可以对映射文件执行的操作,让我们看一下使用一个来查找应该发送用户的正确后端服务器池。你将使用先前创建的hosts.map文件来查找应根据给定域名使用哪个后端。

首先编辑haproxy.cfg文件。正如你将看到的,你将添加一个读取map文件并返回后端名称的Map Converters。

 use_backend %[str(linuxea.com),map(/etc/haproxy/maps/hosts.map)]

将linuxea.com作为键的hosts.map中的第一行将返回其值。注意输入str(linuxea.com)如何从表达式开始并用逗号分隔转换器。

在运行时计算此表达式时,它将转换为use_backend be_static行,该行将请求定向到be_static服务器池。当然,你可以发送HTTP标头或URL参数的值,而不是传递像linuxea.com这样的硬编码字符串。下一个示例使用Host标头的值作为输入。

use_backend %[req.hdr(host),lower,map(/etc/haproxy/maps/hosts.map,be_static)]

map converters最多需要两个参数。第一个是map文件的路径。第二个可选参数声明了一个默认值,如果找不到匹配的键,将使用该值。所以,在这种情况下,如果没有匹配,be_static将被使用。如果输入匹配Map文件中的多个项目,HAProxy将返回第一个项目。

map converters在文件中查找完全匹配,但有一些变体为部分匹配提供了机会。这里总结了最常用的:

|--------------------------------------------------------------------------------------------------------------------------------------
|map_beg    :在Map文件中查找与输入开头匹配的条目(例如,“abcd”的输入将匹配文件中的“a”)。
|map_end    :在映射文件中查找与输入结尾匹配的条目(例如,“abcd”的输入将匹配文件中的“d”)。
|             与其他匹配模式不同,这不执行ebtree查找,而是检查每一行。
|--------------------------------------------------------------------------------------------------------------------------------------
|map_sub    :在映射文件中查找构成样本子字符串的条目(例如,“abcd”的输入将与文件中的“ab”或“c”匹配)。
|             与其他匹配模式不同,这不执行ebtree查找,而是检查每一行。
|--------------------------------------------------------------------------------------------------------------------------------------     
|map_ip     :这将输入作为IP地址并在Map中查找。如果Map中有掩码(例如192.168.0.0/16),则该范围内的任何IP都将匹配它。
|             如果输入类型是IP地址,则这是默认值。
|--------------------------------------------------------------------------------------------------------------------------------------     
|map_reg    :这会将Map中的样本作为正则表达式读取,如果正则表达式匹配则匹配。与其他匹配模式不同,这不会执行ebtree查找,而是检查每一行。
|map_str    :别名map。这将一个字符串作为输入,匹配整个键,并返回一个值作为字符串
|--------------------------------------------------------------------------------------------------------------------------------------

修改值

map file的好处在于更新他们,比如从一个后端更改到另外一个后端来进行维护。

其中有四种方法修改。

  • 1 ,直接编辑文件。但是需要重新加载haproxy(1.8已经引入了无中断重载),假如你使用的是puppet或ansible,那么这将会是一个不错的选择
  • 2,企业版haproxy支持lb-update
  • 3,Runtime API修改,无需重新加载haproxy
  • 4,使用http-request set-map配置文件的指令。这使你有机会根据请求中的URL参数更新映射条目。很容易将其转换为方便的基于HTTP的界面,以便从远程客户端进行Map文件更改。

在这里重点介绍Runtime API编辑

使用Runtime API进行编辑

回顾我们之前的博客文章,使用HAProxy Runtime API动态配置,你将看到有几种API方法可用于更新现有的映射文件。

API方法         描述
show map    :列出可用的Map文件或显示Map文件的内容。
get map     :报告与给定输入匹配的键和值。
set map     :修改Map条目。
add map     :添加Map条目。
del map     :删除Map条目。
clear map   :删除映射文件中的所有条目。

没有任何参数,show map列出加载到内存中的映射文件。如果给它指向特定文件的路径,它将显示其内容。在以下示例中,我们使用它来显示hosts.map中的键值对。

[root@www.linuxea.com ~]# echo "@1; show map /etc/haproxy/maps/hosts.map"| socat stdio /var/run/haproxy.sock
0x14381b0 static.linuxea.com be_static
0x1438c20 www.linuxea.com be_static
0x1438ca0 linuxea.com be_static
0x1438d20 api.linuxea.com be_api

第一列是条目的位置,通常被忽略。第二列是要匹配的键,第三列是值。我们可以通过Runtime API轻松添加和删除条目。要从Map文件中删除条目,请使用del map。请注意,这只会将其从内存中删除,而不是从实际文件中删除。

[root@www.linuxea.com ~]# echo "@1; del map /etc/haproxy/maps/hosts.map linuxea.com"| socat stdio /var/run/haproxy.sock

[root@www.linuxea.com ~]# echo "@1; show map /etc/haproxy/maps/hosts.map"| socat stdio /var/run/haproxy.sock
0x14381b0 static.linuxea.com be_static
0x1438c20 www.linuxea.com be_static
0x1438d20 api.linuxea.com be_api

你还可以删除所有条目clear map

[root@www.linuxea.com ~]# echo "@1; clear map /etc/haproxy/maps/hosts.map "| socat stdio /var/run/haproxy.sock

[root@www.linuxea.com ~]# echo "@1; show map /etc/haproxy/maps/hosts.map"| socat stdio /var/run/haproxy.sock

[root@www.linuxea.com ~]# 

添加新密钥和值add map

[root@www.linuxea.com ~]# echo "@1; add map /etc/haproxy/maps/hosts.map linuxea.com be_stats "| socat stdio /var/run/haproxy.sock

[root@www.linuxea.com ~]# echo "@1; show map /etc/haproxy/maps/hosts.map"| socat stdio /var/run/haproxy.sock
0x1438d20 linuxea.com be_stats

更改现有条目set map

[root@www.linuxea.com ~]# echo "@1; set map /etc/haproxy/maps/hosts.map  linuxea.com dev "| socat stdio /var/run/haproxy.sock

[root@www.linuxea.com ~]# echo "@1; show map /etc/haproxy/maps/hosts.map"| socat stdio /var/run/haproxy.sock
0x1438d20 linuxea.com dev

[root@www.linuxea.com ~]# 

使用show map,我们可以获取文件的内容,使用awk将其过滤到第二和第三列,然后将内存中的表示保存回磁盘:

[root@www.linuxea.com ~]# echo "@1; show map /etc/haproxy/maps/hosts.map"| socat stdio /var/run/haproxy.sock | awk '{print $2" "$3}' > /etc/haproxy/maps/hosts.map 
[root@www.linuxea.com ~]# cat /etc/haproxy/maps/hosts.map
linuxea.com dev

操作也可以用分号链接在一起,这样可以轻松编写更改脚本并保存结果:

[root@www.linuxea.com ~]# echo "@1; clear map /etc/haproxy/maps/hosts.map; add map /etc/haproxy/maps/hosts.map bar.linuxea.com be_foo; add map /etc/haproxy/maps/hosts.map foo.linuxea.com be_baz" | socat stdio /var/run/haproxy.sock
[root@www.linuxea.com ~]#  echo "@1; show map /etc/haproxy/maps/hosts.map"| socat stdio /var/run/haproxy.sock
0x1438d20 bar.linuxea.com be_foo
0x14381b0 foo.linuxea.com be_baz

如果你通过多个进程分配HAProxy,则nbproc需要为每个进程配置一个套接字,然后运行循环以单独更新每个进程。使用多线程时这不是问题

使用http-request set-map

假设你不想手动编辑文件或使用Runtime API。相反,你希望能够使用特定的URL参数发出HTTP请求,并更新你的Map文件。在那种情况下,http-request set-map是你的首选。

这允许使用fetches,converters和ACL来决定何时以及如何在运行时更改映射条件。除了set-map,还有del-map,它允许你以相同的方式删除map条目。与Runtime API一样,这些更改也仅适用于请求最终执行的进程。

将map文件的路径传递给set-map,并使用要添加或更新的键和值(用空格分隔)。 key和value都支持日志格式表示法,所以你可以将它们指定为普通字符串或使用提取和转换器。例如,要向hosts.map文件添加新条目,但前提是源地址属于在192.168.122.0/24范围内,你可以使用如下配置:

frontend linuxea.com
    bind :80
    acl in_network src 192.168.122.0/24
    acl is_map_add path_beg /map/add
    http-request set-map(/etc/haproxy/maps/hosts.map) %[url_param(domain)] %[url_param(backend)] if is_map_add in_network
    http-request deny deny_status 200 if { path_beg /map/ }
    use_backend %[req.hdr(host),lower,map(/etc/haproxy/maps/hosts.map)]

这将允许你发出Web请求,例如http://192.168.122.64/map/add?domain=example.com&backend=be_static,以便快速轻松地更新maps。如果该条目已存在,则将更新。请注意,你可以使用http-request deny deny_status 200来阻止请求转到后端服务器。

http-request del-map命令后跟要从映射文件中删除的密钥

acl is_map_del path_beg /map/delete
http-request del-map(/etc/haproxy/maps/hosts.map) %[url_param(domain)] if is_map_del in_network

使用你之前看到的show map技术,你可以安排一个cron作业来每隔几分钟保存一次Map文件。但是,如果你需要跨HAProxy的多个实例复制这些更改,使用其他方法之一将是一个更好的选择。

控制何时设置或删除条目的另一种方法是检查请求的方法,然后设置条目(如果它是POST或PUT)。如果是DELETE,则删除条目。

测试下:

[root@www.linuxea.com ~]# echo "@1; show map /etc/haproxy/maps/hosts.map"| socat stdio /var/run/haproxy.sock
0x9d74a0 linuxea.com dev

我们更新一个
http://172.25.10.245:2379/map/add?domain=linuxea.com&backend=B-backend-linuxea.com

[root@www.linuxea.com ~]# echo "@1; show map /etc/haproxy/maps/hosts.map"| socat stdio /var/run/haproxy.sock
0x9d74a0 linuxea.com B-backend-linuxea.com
  • 在做一次测试

配置修改如下:

    acl in_network src 10.0.0.0/8
    acl is_map_add path_beg /map/add
    http-request set-map(/etc/haproxy/maps/hosts.map) %[url_param(domain)] %[url_param(backend)] if is_map_add in_network
    http-request deny deny_status 200 if { path_beg /map/ }
    use_backend %[str(active),map(/etc/haproxy/maps/hosts.map)]

reload

[root@www.linuxea.com ~]# echo "reload" | socat stdio /var/run/haproxy.sock

haproxy的配置如下:

backend A-backend-linuxea.com
        option forwardfor header X-REALL-IP #获取后端ip
        option httpchk HEAD / HTTP/1.0
        balance roundrobin #负载均衡算法:roundrobin:轮询 source:源ip hash    leastconn:最小连接数

        server etcd1 172.25.6.37:81 check inter 2000 rise 30 fall 15
        server etcd2 172.25.10.245:81 check inter 2000 rise 30 fall 15
        server etcd3 172.25.50.250:81 check inter 2000 rise 30 fall 15

backend B-backend-linuxea.com
        option forwardfor header X-REALL-IP
        option httpchk HEAD / HTTP/1.0
        balance roundrobin
        server etcd2 172.25.10.35:83 check inter 2000 rise 30 fall 15

修改下:http://172.25.10.245:2379/map/add?domain=active&backend=B-backend-linuxea.com

现在active的是 B-backend-linuxea.com

[root@www.linuxea.com ~]#  echo "@1; show map /etc/haproxy/maps/hosts.map"| socat stdio /var/run/haproxy.sock
0x1e6b450 linuxea.com dev
0x7f748c027820 active B-backend-linuxea.com

II. 蓝绿部署

假设你希望实施蓝绿部署,其中你可以将Web应用程序的新版本部署到一组临时服务器上,然后将它们与一组生产服务器交换。你可以创建一个名为bluegreen.map的文件并添加一个条目

[root@www.linuxea.com ~]# cat /etc/haproxy/maps/bluegreen.map
active A-backend-linuxea.com

在这种情况下,A-backend-linuxea.com backend包含你当前活动的生产服务器集。这是HAProxy配置文件:

backend A-backend-linuxea.com
        option forwardfor header X-REALL-IP #获取后端ip
        option httpchk HEAD / HTTP/1.0
        balance roundrobin 
        server etcd1 172.25.6.37:81 check inter 2000 rise 30 fall 15
        server etcd2 172.25.10.245:81 check inter 2000 rise 30 fall 15
        server etcd3 172.25.50.250:81 check inter 2000 rise 30 fall 15

backend B-backend-linuxea.com
        option forwardfor header X-REALL-IP
        option httpchk HEAD / HTTP/1.0
        balance roundrobin
        server etcd2 172.25.10.35:83 check inter 2000 rise 30 fall 15

将新版本的应用程序部署到A-backend-linuxea.com服务器并对其进行测试后,可以使用Runtime API将活动A-backend-linuxea.com服务器与B-backend-linuxea.com服务器交换,从而使B-backend-linuxea.com服务器在生产中变为活动状态。

将业务修改到B-backend-linuxea.com

19332> set map /etc/haproxy/maps/bluegreen.map active B-backend-linuxea.com

观察。现在,你的流量将从A-backend-linuxea.com服务器转移到B-backend-linuxea.com服务器。与滚动部署不同,这可确保你的所有用户同时迁移到新版本的应用程序。

[root@www.linuxea.com ~]# while true;do curl 172.25.10.245:2379 && sleep 1;done
172.25.50.250
172.25.6.37
172.25.10.245
172.25.50.250
172.25.6.37
172.25.10.245
172.25.50.250
172.25.10.35 is ok
172.25.10.35 is ok
172.25.10.35 is ok
172.25.10.35 is ok
172.25.10.35 is ok
172.25.10.35 is ok
172.25.10.35 is ok

III. 按URL路径限制速率

对于此示例,你将为你的网站设置速率限制。使用映射文件可以为不同的URL设置不同的限制。例如,与开始的URL /api/old可以允许比那些与开始了更高的要求速度/api/new

添加名为rates.map的Map文件并添加以下条目:

/api/old  40
/api/new  20
[root@www.linuxea.com ~]# mkdir /etc/haproxy/maps/ -p
[root@www.linuxea.com ~]# cat > /etc/haproxy/maps/rates.map << EOF
/api/old 40
/api/new 20
EOF

正常情况下你将分别看到

172.25.10.35 old
172.25.10.35 new

考虑以下内容frontend,其中每个客户端的当前请求率在10秒内测量。像/api/old这样的URL路径每秒最多允许四个请求(40个请求/ 10秒= 4个rps)。

frontend frontend-web.com
    bind :80
    default_backend A-backend-linuxea.com

    # Set up stick table to track request rates
    # 设置跟踪请求率
    stick-table type binary len 8 size 1m expire 10s store http_req_rate(10s)

    # Track client by base32+src (Host header + URL path + src IP)
    # 通过base32 + src跟踪客户端(主机头+ URL路径+ src IP)
    http-request track-sc0 base32+src

    # Check map file to get rate limit for path
    # 检查映射文件以获取路径的速率限制
    http-request set-var(req.rate_limit)  path,map_beg(/etc/haproxy/maps/rates.map)

    # Client's request rate is tracked
    # 跟踪客户的请求率
   http-request set-var(req.request_rate)  base32+src,table_http_req_rate(frontend-web.com)

    # Subtract the current request rate from the limit
    # If less than zero, set rate_abuse to true

    #从限制中减去当前请求率
    #如果小于零,则将rate_abuse设置为true    
    acl rate_abuse var(req.rate_limit),sub(req.request_rate) lt 0    

    # Deny if rate abuse
    # 拒绝率滥用
    http-request deny deny_status 429 if rate_abuse

这里,stick-table定义记录十秒钟内的客户请求率。请注意,我们使用base32 + src fetch方法跟踪客户端,该方法是Host头,URL路径的组合,URL路径和源IP地址。这允许我们基于每个路径跟踪每个客户端的请求率。 base32 + src值作为二进制数据存储在base32+src中。

然后,使用http-request set-var设置两个变量。第一个req.rate_limit设置为rates.map文件中当前路径的预定义速率限制。第二个req.request_rate设置为客户端的当前请求率

ACL rate_abuse进行计算以查看客户端的请求率是否高于此路径的限制。它通过从请求限制中减去请求率并检查差异是否小于零来完成此操作。如果是,则http-request deny指令以429 Too Many Requests响应

  • /api/old/
[root@www.linuxea.com ~]# while true;do  curl 172.25.10.245:2379/api/old/ ; done
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
172.25.10.35 old
<html><body><h1>429 Too Many Requests</h1>
You have sent too many requests in a given amount of time.
</body></html>
<html><body><h1>429 Too Many Requests</h1>
You have sent too many requests in a given amount of time.
</body></html>
<html><body><h1>429 Too Many Requests</h1>
You have sent too many requests in a given amount of time.
</body></html>
<html><body><h1>429 Too Many Requests</h1>

  • /api/new
[root@www.linuxea.com ~]# while true;do  curl 172.25.10.245:2379/api/new/ ; done
172.25.10.35 new
172.25.10.35 new
172.25.10.35 new
172.25.10.35 new
172.25.10.35 new
172.25.10.35 new
172.25.10.35 new
172.25.10.35 new
172.25.10.35 new
172.25.10.35 new
172.25.10.35 new
172.25.10.35 new
172.25.10.35 new
172.25.10.35 new
172.25.10.35 new
172.25.10.35 new
172.25.10.35 new
172.25.10.35 new
172.25.10.35 new
172.25.10.35 new
<html><body><h1>429 Too Many Requests</h1>
You have sent too many requests in a given amount of time.
</body></html>
<html><body><h1>429 Too Many Requests</h1>
You have sent too many requests in a given amount of time.
</body></html>
<html><body><h1>429 Too Many Requests</h1>
You have sent too many requests in a given amount of time.

IV. 延伸阅读

linuxea:haproxy 1.9中的多线程
linuxea:haproxy1.9 了解四个基础部分
linuxea:haproxy1.9日志简介
linuxea: 使用HAproxy 1.9 Runtime API进行动态配置
linuxea: 探索 HAproxy 1.9 统计页面
linuxea: HAproxy 1.9 ACL简介

0 分享

您可以选择一种方式赞助本站

支付宝扫码赞助

支付宝扫码赞助

日期: 2019-06-12分类: HAproxy

标签: haproxy

发表评论