haproxy 限制流量 限制连接数
场景
我们的 haproxy 作为负载均衡,当前端大量的连接过来,后端处理不过来,会把后面服务器搞死 504。以至于不能正常服务。所以我们可以在 haproxy 拦截一部分流量,使后端不至于崩掉。
您可以设置速率限制以防范应用程序层DDoS攻击。
1. 限制速率的几种方法
每种方法配合使用 HAProxy 灵活的配置语言,结合: 访问控制列表(ACL), stick tables 和 maps,以构成不同的解决方案。
例如如下方法:
限制连接数
流入的字节数
流出的字节数
最大错误数
当看到客户端超过速率限制时,HAProxy 可以采取许多措施
暂存它们
将它们发送到其他服务器池
禁止它们较长时间
2. 设置最大连接数 Setting the Maximum Connections
在讲速率限制之前,我们可以启用 queuing ,queuing 可以将多余的连接存储在 HAProxy 中,直到腾出服务器来处理它们为止。打开 queuing 还可以在保持大量连接时,而不会显着增加内存或CPU使用率。
maxconn 参数来限制并发连接数的数量,
2.1 设置最多给每个服务器发送 30 个连接
当所有服务器达到最大连接数后,其余的连接将在 Haproxy 内排队
backend servers server s1 192.168.30.10:80 check maxconn 30 server s2 192.168.31.10:80 check maxconn 30 server s3 192.168.31.10:80 check maxconn 30
如上面配置,三台服务器每个上面上都有30个连接,则新连接将必须排队等待。 这意味着服务器本身不会过载!!
2.2 给在队列中的连接指定超时时间
设置超时的目的是:
让客户端收到 503 服务不可用,总比把你的服务器压死的好吧。当然,也可以更早的让客户收到错误信息,而不是等待更长的时间并可能导致更难解决的错误。
backend servers timeout queue 10s server s1 192.168.30.10:80 check maxconn 30 server s2 192.168.31.10:80 check maxconn 30 server s3 192.168.31.10:80 check maxconn 30
3. 滑动窗口限制速率 Sliding Window Rate Limiting
您想限制用户在一定时间内可以发出的请求数。
3.1 限制每个客户端在最近10秒钟内最多允许20个请求。【更多示例点击看查】
frontend website bind :80 stick-table type ip size 100k expire 30s store http_req_rate(10s) http-request track-sc0 src http-request deny deny_status 429 if { sc_http_req_rate(0) gt 20 } default_backend servers
这里我们允许每个客户端最多有 20 个连接数,拒绝额外的请求,并返回 429 状态码。sc_http_req_rate 方法返回客户端的当前请求速率。
其他的处理方法还有:
把客户端转发到其他的后端
丢弃连接
配置解释
stick-table 指令创建一个键/值存储,用于存储计数器(如: 每个客户端的 HTTP 请求速率)。
键是由 type 参数指定的,这里是客户端 IP 地址,用于存储和汇总该客户端的请求数量。
expire 参数设置,设置客户端经过一段不活动的时间后将其删除,如果没有设置,那么在存储空间已满时将清除最旧的记录。
size 保存条目数量,这里我们允许 100,000 条记录。
http-request track-sc0 src 行将客户机添加为 stick table 中的记录。当有 ip 加入,计数就开始。
http-request deny 行设置 速率限制阈值 以及 超过该阈值时应采取的措施。
4. 固定窗口限制速率 Rate Limit by Fixed Time Window
假设您希望每天最多允许1000个请求。在零点重置。
在这里你需要使用 http_req_cnt 而不是上面的 http_req_rate,它会一直递增直到重置或达到到期为止, 然后,您将使用 HAProxy Runtime API 在恰好午夜清除所有记录。
frontend website bind :80 stick-table type ip size 100k expire 24h store http_req_cnt http-request track-sc0 src http-request deny deny_status 429 if { sc_http_req_cnt(0) gt 1000 } default_backend servers
当一个客户端发送超过 1000 个请求,他们将被拒绝 。
你需要通过向您的 HAProxy 配置的 global 部分添加 stats 套接字指令来启用 Runtime API 以在每天结束时重置此状态的方法。
global stats socket /run/haproxy.sock mode 660 level admin
接下来,安装 socat 实用程序,并使用它调用 clear table Runtime API 命令来清除 stick 表中的所有记录:
$ echo "clear table website" | sudo socat stdio /run/haproxy.sock
您可以设置一个 cron 作业以每天自动执行此操作。 如果您需要一次性清除一条记录,则可以包含客户的 IP 地址,如下所示
$ echo "clear table website key 192.168.50.10" | sudo socat stdio /run/haproxy.sock
5. 通过 URL 限制速率 Rate Limit by URL
有些页面需要比其他页面更多的处理时间,这时候你可能需要通过以 URL 维度设置阈值。
1. 首先需要在 /etc/haproxy 下面新建一个 rates.map 文件(格式如下),这个文件关联 URL 路径和 他们的速率限制。
/urla 10 /urlb 20 /urlc 30
2. 然后在 haproxy 配置文件中配置
frontend website bind :80 stick-table type binary len 8 size 100k expire 10s store http_req_rate(10s) # Track client by base32+src (Host header + URL path + 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/rates.map,20) # Client's request rate is tracked http-request set-var(req.request_rate) base32+src,table_http_req_rate() # Subtract the current request rate from the limit # If less than zero, set rate_abuse to 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 default_backend servers
我们没有指定 stick 表中的IP地址,而是指定了一种二进制类型。其中填充了 HTTP Host header,URL 路径 和客户端的源IP地址的哈希。当调用 http-request track-sc0 base32+src 行时,您将获得所有这些。这样,您可以在许多不同的网页上区分客户的请求率。
第一个 http-request set-var 行在 rate.map 文件中找到当前请求的URL路径的请求速率阈值。如果找不到请求的 URL,则使用默认值 20。它将结果存储在名为 req.rate_limit 的变量中。
第二个 http-request set-var 行将客户对该页面的当前请求率存在 req.request_rate 的变量
我们 req.rate_limit 减(subtract) 去 req.rate_limit ,并确保差值大于零。 如果不是,我们将拒绝该请求,因为它们已超过该页面的阈值。
5. 通过 URL 参数 限制速率 Rate Limit by URL Parameter
这个和通过 URL路径 进行速率限制相比有些许变化。 如果您的客户端在URL中包含一个API令牌以标识自己,通过URL参数进行速率限制
frontend website bind :80 stick-table type string size 100k expire 24h store http_req_rate(24h) # check for token parameter acl has_token url_param(token) -m found # check if exceeds limit acl exceeds_limit url_param(token),table_http_req_rate() gt 1000 # start tracking based on token parameter http-request track-sc0 url_param(token) unless exceeds_limit # Deny if missing token or exceeds limit http-request deny deny_status 429 if !has_token or exceeds_limit default_backend servers
在这里,我们使用 24小时的滑动窗口,在此期间,客户最多可以提出1000 个请求。
stick table 的类型是 string
http-request track-sc0 行将名为 token 的 URL参数 存储为表中的键。url 形如:http://yourwebsite.com/api/v1/does_a_thing?token=abcd1234
请注意:我们已在 http-request track-sc0 行的末尾添加了unless exceeds_limit 子句,因为在超出限制后没有必要继续增加计数器。 它还可以防止永久阻止客户端。
has_token ACL 确保URL中包含令牌
exceeds_limit ACL 查找最近 24 小时内的当前请求计数。
http-request deny 行拒绝 哪些 没有 token 或者 超过 limit 限制的请求。
6. http_req_rate 和 http_req_cnt 区别
http_req_rate
是过去最近24小时内的滑动窗口。
http_req_cnt
从用户发送第一个请求时开始,并从那时起递增直到到期;
但是,除非您每隔 24 小时通过 Runtime API 手动清除该表,当客户端保持活动状态时,http_req_cnt 可能会长时间保留,因为每当有记录更新时,过期时间就会重置。
摘自:
https://www.haproxy.com/blog/four-examples-of-haproxy-rate-limiting/
共 0 条评论