NGINX大全 第三章 流量管理

NGINX大全 第三章 流量管理

第三章 流量管理

3.0 介绍

NGINX和NGINX Plus也被作为网络流量控制器。您可以使用NGINX基于许多属性智能地路由流量和控制流量。本章介绍NGINX的基于百分比分割客户端请求,利用客户端的地理位置还有以速率,连接和带宽限制的形式控制流量的能力。在阅读本章时,请记住,您可以混合地使用这些功能以实现无数可能性。

3.1 A/B测试

问题

您需要在两个或多个版本的文件或应用程序之间拆分客户以测试接受度。

解决方案

使用split_clients模块将一定比例的客户端定向到不同的upstream池:

split_clients "${remote_addr}AAA" $variant {
    20.0% "backendv2";
    * "backendv1";
}

`split_clients指令将您提供的作为第一个参数的字符串进行哈希,并将该哈希值以提供的百分比分割,映射到第二个参数提供的变量的值。第三个参数是包含键值对的对象,其中键是权重百分比,值是要分配的值。键可以是百分比或星号。在所有百分比被计算后,星号表示整体的剩余部分。对于20%的客户端IP地址,$variant变量的值是backendv2,对于剩余的80%,变量的值是backendv1

在这个例子中,backendv1backendv2代表upstream服务器池,可以与proxy_pass指令一起使用:

location / {
    proxy_pass http://$variant
}

使用变量$variant,我们的流量将在两个不同的应用服务器池之间分配。

讨论

当为了电子商务网站上的转换率测试不同类型的营销和前端功能时,此类A/B测试非常有用。应用程序通常使用称为canary release的部署类型。在此类部署中,流量会缓慢切换到新版本。推出新版本的代码时,在不同版本的应用程序之间分流客户端是有用的,因为要在发生错误时限制影响范围。无论在两个不同的应用程序集之间拆分客户端的原因是什么,NGINX通过使用这个split_clients模块简化了这一过程。

3.2 使用GeoIP模块和数据库

问题

您需要安装GeoIP数据库并在NGINX中启用其内置变量来记录日志并向您的应用程序指定客户端的位置。

解决方案

官方NGINX开源软件包存储库(在第一章中安装NGINX时配置过)提供了一个名为nginx-module-geoip的软件包。使用NGINX Plus软件包存储库时,此软件包被称为nginx-plus-module-geoip。这些软件包安装了GeoIP模块的动态版本。

RHEL/CentOS NGINX Open Source:
# yum install nginx-module-geoip
Debian/Ubuntu NGINX Open Source:
# apt-get install nginx-module-geoip
RHEL/CentOS NGINX Plus:
# yum install nginx-plus-module-geoip
Debian/Ubuntu NGINX Plus:
# apt-get install nginx-plus-module-geoip

下载GeoIP国家和城市数据库并解压缩:

# mkdir /etc/nginx/geoip
# cd /etc/nginx/geoip
# wget "http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz"
# gunzip GeoIP.dat.gz
# wget "http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz"
# gunzip GeoLiteCity.dat.gz

这组命令在/etc/nginx目录中创建一个geoip目录,并进入这个新目录,然后在此下载并解压缩包。

通过本地磁盘上的国家和城市的GeoIP数据库,您现在可以指示NGINX GeoIP模块使用它们揭露那些内置变量基于客户端IP地址:

load_module "/usr/lib64/nginx/modules/ngx_http_geoip_module.so";
http {
    geoip_country /etc/nginx/geoip/GeoIP.dat;
    geoip_city /etc/nginx/geoip/GeoLiteCity.dat;
    ...
}

load_module指令从文件系统上的路径动态加载模块。load_module指令仅在主上下文中有效。geoip_country指令获取GeoIP.dat文件的路径,该文件包含将IP地址映射到国家/地区代码的数据库,并且仅在HTTP上下文中有效。

讨论

geoip_countrygeoip_city指令公开了许多该模块中可用的内置变量。geoip_country指令启用变量,以便您区分客户端的原来的国家/地区。这些变量包括$geoip_country_code$geoip_country_code3$geoip_country_name。country_code变量返回两个字母的国家/地区代码,country_code3变量返回三个字母的国家/地区代码。country_name变量返回国家/地区的全名。

geoip_city指令启用了很多变量。geoip_city指令启用所有与geoip_country指令相同的变量,只是使用不同的名称,例如$geoip_city_country_code$geoip_city_country_code3``$geoip_city_country_name。其他变量包括$geoip_city$geoip_city_continent_code$geoip_latitude$geoip_longitude$geoip_postal_code,所有这些都描述了它们返回的值。$geoip_region$geoip_region_name描述了区域,领土,州,省,联邦土地等。Region是双字母代码,其中region_name是全名。$geoip_area_code,仅在美国有效,返回三位数的电话区号。

使用这些变量,您可以记录有关客户端的信息。您可以选择将此信息作为header或变量传递给您的应用程序,或者用NGINX以特定方式路由您的流量。

3.3 基于国家限制访问

问题

由于合同或应用程序要求,您需要限制特定国家/地区的访问权限。

解决方案

把要阻止或允许的国家/地区代码映射到变量:

load_module "/usr/lib64/nginx/modules/ngx_http_geoip_module.so";
http {
    map $geoip_country_code $country_access {
        "US" 0;
        "RU" 0;
        default 1;
    }
    ...
}

此映射将新变量$country_access设置为1或0。如果客户端IP地址来自美国或俄罗斯,则变量将设置为0。对于任何其他的国家/地区,变量将设置为1。

现在,在我们的server块中,我们将使用if语句拒绝来自美国或俄罗斯以外的任何访问:

server {
    if ($country_access = '1') {
        return 403;
    }
    ...
}

如果$country_access变量设置为1,则此if语句将评定为True。如果为True,服务器将返回403未授权。否则服务器正常运行。因此,这个if块只会拒绝那些不是来自美国或俄罗斯的人。

讨论

这是一个简短但简单的示例,说明如何仅允许来自几个国家/地区的访问。这里阐述此示例以满足您的需求。您可以根据GeoIP模块提供的任何内置变量,使用相同的实践来允许或阻止访问。

3.4 找到最初的客户端

问题

您需要找到最初的客户端IP地址,因为NGINX服务器前面有代理。

解决方案

使用geoip_proxy指令定义代理IP地址范围,使用geoip_proxy_recursive指令查找最初的IP:

load_module "/usr/lib64/nginx/modules/ngx_http_geoip_module.so";
http {
    geoip_country /etc/nginx/geoip/GeoIP.dat;
    geoip_city /etc/nginx/geoip/GeoLiteCity.dat;
    geoip_proxy 10.0.16.0/26;
    geoip_proxy_recursive on;
    ...
}

geoip_proxy指令定义了我们的代理服务器所在的CIDR范围,并指示NGINX利用X-Forwarded-For header来查找客户端IP地址。geoip_proxy_recursive指令指示NGINX以递归方式查看已知的最后一个客户端IP的X-Forwarded-For header。

讨论

您可能会发现,如果您在NGINX前使用代理,NGINX将获取代理的IP地址而不是客户端的。为此,您可以使用geoip_proxy指令指示NGINX在从给定范围打开连接时使用X-Forwarded-For标头。geoip_proxy指令采用地址或CIDR范围。当有多个代理在NGINX前面传递流量时,您可以使用geoip_proxy_recursive指令递归搜索X-Forwarded-For地址以查找最初的客户端。当你在NGINX的前面使用诸如AWS ELB,Google负载均衡器或Azure负载均衡器等负载均衡器时,您会希望使用这样的功能。

3.5 限制连接数

问题

您需要根据预定义的键限制连接数,例如客户端的IP地址。

解决方案

构造共享内存区域以保存连接指标,并使用limit_conn指令限制打开的连接数:

http {
    limit_conn_zone $binary_remote_addr zone=limitbyaddr:10m;
    limit_conn_status 429;
    ...
    server {
        ...
        limit_conn limitbyaddr 40;
        ...
    }
}

此配置创建名为limitbyaddr的共享内存区域。用到的预定义的键是二进制形式的客户端IP地址。共享内存区域的大小设置为10兆字节。limit_conn指令有两个参数:limit_conn_zone名称和允许的连接数。当连接数被限制时,limit_conn_status设置响应状态为429,表示请求太多。limit_connlimit_conn_status指令在HTTP,server和location上下文中有效。

讨论

基于键限制连接数可被用于防止滥用并在所有客户端之间公平共享资源。谨慎使用预定义键非常重要。如前所述,如果许多用户来自源于同一IP的同一网络上,例如在网络地址转换(NAT)后面,则使用IP地址可能会很危险。整个客户群将受到限制。limit_conn_zone指令仅在HTTP上下文中有效。您可以在HTTP上下文中使用NGINX允许的任意数量的变量,以构建要限制的字符串。根据使用情况,利用可以在应用级别识别用户的变量(例如会话cookie)可能是更清晰的解决方案。limit_conn_status默认为503,服务不可用。您可能会觉得使用429更好,即该服务可用,500级响应表示服务器错误,而400级响应表示客户端错误。

3.6 限制频率

问题

您需要通过预定义键来限制请求频率,例如客户端的IP地址。

解决方案

利用频率限制模块来限制请求频率:

http {
    limit_req_zone $binary_remote_addr
    zone=limitbyaddr:10m rate=1r/s;
    limit_req_status 429;
    ...
    server {
        ...
        limit_req zone=limitbyaddr burst=10 nodelay;
        ...
    }
}

此示例配置创建名为limitbyaddr的共享内存区域。使用的预定义键是二进制形式的客户端IP地址。共享内存区域的大小设置为10兆字节。区域使用关键字参数设置频率。limit_req指令有两个可选的关键字参数:zoneburstzone被用来指示使用哪个共享内存请求限制区域的指令。当给定区域的请求频率超出时,请求被延迟,直到其降到最大突发值,该值由burst关键字参数表示。burst关键字参数默认为零。limit_req还接受第三个可选参数nodelay。此参数使客户端能够在受限之前无延迟地使用burst参数。limit_req_status将返回给客户端的状态设置为特定的HTTP状态代码;默认值为503。limit_req_statuslimit_req在HTTP,server和location的上下文中有效。limit_req_zone仅在HTTP上下文中有效。在NGINX Plus中,频率限制是群集感知的,R16版本新加的功能。

讨论

频率限制模块非常强大,可以防止滥用快速请求,同时仍然为每个人提供优质服务。限制请求率的原因有很多,其中一个是安全性。您可以通过对登录页面施加非常严格的限制来拒绝暴力攻击。您可以对所有请求设置合理的限制,从而禁止恶意用户的尝试使应用程序无法提供服务或浪费资源的的计划。频率限制模块的配置很像之前的章节3.5中描述的连接数限制模块,并且许多相同的问题都适用。您可以指定每秒请求数或每分钟请求数限制请求的频率。达到频率限制时,事件将记录日志。还有一个不在示例中的指令,limit_req_log_level,它默认为error,但可以设置为info,notice或warn。NGINX Plus中的新功能,版本R16频率限制现在可识别群集(有关区域同步示例,请参阅章节12.5)。

3.7 限制带宽

问题

您需要限制每个客户端的资源下载带宽。

解决方案

利用NGINX的limit_ratelimit_rate_after指令来限制对客户端的响应速率:

location /download/ {
    limit_rate_after 10m;
    limit_rate 1m;
}

这个location块的配置指定了对于具有前缀download的URI,响应给客户端的速率将在达到10兆字节之后被限制为每秒1兆字节。bandwidth(带宽)限制是每个连接的,因此您可能需要在适合的地方建立连接限制和带宽限制。

讨论

限制特定连接的带宽使NGINX能够让您以指定的方式在所有客户端之间共享其上载带宽。这两个指令可以完成所有操作:limit_rate_afterlimit_rate。几乎任何上下文中都可以设置limit_rate_after指令:HTTP,server,location,以及位于location中的iflimit_rate指令适用于与limit_rate_after相同的上下文;但是,也可以通过设置名为$limit_rate的变量来设置它。limit_rate_after指令指定在传输指定数量的数据之前,不应对连接进行速率限制。limit_rate指令默认指定给定上下文的速率限制,以每秒的字节数为单位。但是,您可以指定m表示兆字节或g表示千兆字节。两个指令默认值为0。值0表示根本不限制下载速率。此模块允许您以编程方式更改客户端的速率限制。

猜你喜欢
NGINX大全 第四章 可大规模扩展的内容缓存
阅读 2748

使用NGINX,您可以在任何可以放置NGINX服务器的地方缓存您的内容,从而有效地创建您自己的CDN。

NGINX大全 第十一章 容器/微服务
阅读 1250

本章重点介绍如何构建NGINX和NGINX Plus容器镜像,使容器化环境更容易工作的特性,以及在Kubernetes和OpenShift上部署镜像。

NGINX大全 第六章 验证
阅读 1510

NGINX能够验证客户端。通过NGINX验证客户端请求降低了工作量,并可以阻止未经身份验证的请求到达应用程序服务器。

NGINX大全 第八章 HTTP/2
阅读 1874

本章详细介绍了在NGINX中启用HTTP/2以及配置gRPC和HTTP/2服务器推送支持的基本配置。

NGINX大全 第十三章 高级活动监控
阅读 1496

本章详细介绍了NGINX Plus仪表板,NGINX Plus API和开源存根状态模块的功能。

NGINX大全 第七章 安全控制
阅读 1183

在本章中,我们将通过许多不同的方式使用NGINX和NGINX Plus来保护您的Web应用程序。您可以将这些安全方法中的许多方法相互结合使用,以帮助加强安全性。

NGINX大全 第十五章 性能调优
阅读 1942

本章还介绍了连接调优,以保持连接对客户端和上游服务器的开放性,并通过调整操作系统来提供更多连接。

NGINX大全 第十六章 实用操作提示和结论
阅读 2165

在本章中,我将介绍如何确保配置文件简洁明了以及调试配置文件。