说到web服务器、代理服务器、调度服务器,Nginx是目前用的最多的。对于运维工程师来说,每天都免不了要和Nginx打交道。为了更好的使用和管理Nginx,本文将为大家介绍虫虫日常使用??的几种秘籍。限制访问当Nginx开放到公网时,会出现大量的异常访问。这不仅消耗服务器资源,还可能是某种信息探索进而攻击的前奏。有必要有针对性地限制这些访问。.在Nginx中,可以使用一些内置变量来限制访问。限制客户端代理在nginx中可以使用$http_user_agent变量来匹配客户端类型,然后返回4.3来限制匹配到的访问。在Nginx配置的server部分,直接使用if语句:if($http_user_agent~(Go-http-client/1.1|curl)){return403;}但是如果要匹配的clientagent很多,就拼写成like这样比较繁琐,难以管理。在这种情况下,给大家一个技巧就是使用Map函数。Map功能在Nginxngx_http_map_module中实现。使用Map函数,可以创建一个变量并将其关联到其他变量(比如内置的$http_user_agent),也可以将多个值同时关联到多个不同的值,存储在一个多变的。它的基本语法是:map$var1$var2{...}它作用于http模块,这样一开始映射之后,就可以在特定的server部分进行阻塞。对应这个例子:map$http_user_agent:$arg_key$ban{~*spider*1;~Go-http-client/1.11;~curl;default0;}这样新建的$ban变量就可以在后续的ifban中使用了声明禁止。if($ban=1){return403;}IP限制有时候封禁一些恶意的IP来源更直接、简单、有效。Nginx封IP的方法也很简单。直接使用deny语句。是Nginx的内置模块ngx_http_access_module,支持allow和deny两种语句。使用:deny135.125.180.235;如果需要屏蔽的IP较多,可以直接在nginx的配置文件中包含一个block文件,对屏蔽的IP进行管理。includebanip.conf;在banip.conf文件中使用:deny135.125.180.235;deny135.125.180.1/24;...这条语句就够了,当然你也可以用allow和denyall做一个实时的白名单限制方式:allow127.0.0.1;allow192.168.0.0/18;allow110.242.68.66;...denyall;这样除了本机、18位内部网段和110.242.68.66外,其他IP都将被禁止访问。除了直接限制访问之外,很多时候,限速并不能直接限制其访问,而是需要针对特定??的请求限制访问的速率(频率)。Nginx中的速率限制由两个指令limit_req_zone和limit_req实现。limit_req_zone用于定义请求限制区域。区域包含配置请求速率限制和实际限制如何分类。limit_req将区域应用于特定的http上下文,以实现全局限制、每个虚拟服务器的服务器以及虚拟服务器中特定位置的位置。为了说明这一点,假设实施了一个速率限制配置:用户代理将全局速率限制为100RPS,以将特定源(搜索蜘蛛)请求限制为1RPM。通过API令牌将来自某些客户端的请求限制为1RPS。要对请求进行分类,需要提供limit_req_zone的索引。键通常是变量,由nginx预定义或由映射定义。要通过IP设置全局限速,需要使用IP作为key。limit_req_zone$binary_remote_addrzone=global:100mrate=100r/s;现在,通过以下方式限制搜索蜘蛛的User-Agent,这里我们使用map函数:map$http_user_agent$crawler{~*.*(Baiduspider|bot|spider|slurp).*$http_user_agent;default"";}limit_req_zone$crawlerzone=爬虫:1Mrate=1r/m;在上面的配置中,通过map设置$crawler变量作为limit_req_zone的key。limit_req_zone对于不同的客户端必须有不同的值才能正确计算请求计数。如果请求不是来自爬虫,则使用空字符串禁用速率限制。对API命令限制制请求,使用map创建一个多个键,对应用其速度限制区域:map$http_authorization$eclients{~.*6d9627000457f7f7f7f7f7f7f7f7f7c6bb76d96270004515a0486bb7f76196a72b40c55a47f;~.*956f7fd1ae68fecb2b32186415a49c316f769d75.*956f7fd1ae68fecb2b32186415a49c316f769d75;default"";}limit_req_zone$eclientszone=eclients:1Mrate=1r/s;我们来看AuthorizationAPItoken的header,比如Authorization:Bearer1234567890。如果我们匹配一些已知的标签,我们使用$eclients的值作为变量,然后作为ke??y引入到limit_req_zone中。服务器{listen80;server_nametest.show;limit_reqzone=crawlers;limit_reqzone=global;#...}服务器{listen80;server_nameapi.test.show;#...location/heavy/method{#...limit_reqzone=eclients;limit_reqzone=global;#...}#...}请注意,在配置中必须添加全局区域作为备份,以防不匹配。最后,总结一下速率限制的过程:创建保存速率限制的变量的键。不同的key值对应不同的限速区。空键禁用速率限制。使用带有限速键的变量来配置限速区配置。在需要的地方应用速率限制区limit_req。速率限制将有助于保持系统稳定。除了限速,Nginx还有一个请求频率限制方法limit_conn_zone,对应的limit_conn用于限制请求的频率。它的使用方法类似于limit_req_zone和limit_req。下面是一个例子:http{limit_conn_zone$binary_remote_addrzone=perip:10m;limit_conn_zone$server_namezone=perserver:10m;server{location/{limit_connperip10;limit_connperserver1000;}}}cacheNginx最大的用途是作为代理缓存服务器。假设请求被代理到后端应用服务器,后端服务器返回请求数据的成本非常高。您可以通过缓存它来减少后端的负载。http{#...proxy_cache_path/var/cache/nginx/testkeys_zone=test:500mmax_size=1000minactive=1d;#...server{#...location/test{proxy_passtest.show_backend;proxy_cachetest;proxy_cache_key"$scheme$proxy_host$request_uri$http_customer_token";proxy_cache_valid2003021d;proxy_cache_valid40440010m;}}}在此示例中,通过将$http_customer_token添加到Customer-TokenHTTP标头来保存该值。然后,与速率限制一样,使用proxy_cache指令定义缓存区域以应用于服务器、位置或全局。同时配置缓存失效。默认只缓存200、301、302HTTP状态码响应,缓存内容每10分钟更新一次。此外,对于后端服务器,Nginx将遵守其指示性Http标头,例如Cache-Control标头。如果标头包含诸如no-store、must-revalidate之类的内容,nginx将不会缓存响应。你可以在Nginx中配置proxy_ignore_headers"Cache-Control";覆盖此行为。因此,要配置nginx缓存失效,请执行以下操作:在proxy_cache_path上设置max_size以限制磁盘使用。如果nginx需要缓存超过max_size,它会从缓存中移除最近最少使用的值。设置inactive参数,输入proxy_cache_path配置TTL整个缓存区。可以用proxy_cache_valid指示。最后,添加proxy_cache_valid将指示TTL指令在给定位置或服务器中缓存项目,这将为缓存设置TTL条目。结构化日志Nginx的访问日志是一个巨大的宝库,我们可以通过它挖掘当前web服务的在线状态、使用状态和用户信息。不过它默认的访问日志有点过于简单,需要配置添加必要的字段,调整位置,格式化。Nginx日志的配置需要使用log_format语句。典型配置如下:log_formatmain'$remote_addr-$remote_user[$time_iso8601]"$request"''$status$body_bytes_sent"$http_referer"''"$http_user_agent-$ssl_client_s_dn$ssl_client_serial$ssl_client_verify""$http_x_forwarded_for"';上述配置中,除了常用字段外,还增加了$ssl_client_s_dn$ssl_client_serial和$ssl_client_verify,用于https双向认证时在client端用CA下发dn信息,使用用户证书序列号记录合法认证的用户信息。另外,为了与ELK或者其他日志系统集成,需要使用json格式的结构化日志。可以使用graylog转换文本日志,也可以直接在Nginx配置中生成:http{#...log_formatjsonscape=json'{''"server_name":"test.show",''"ts":"$time_iso8601",''"remote_addr":"$remote_addr","host":"$host","origin":"$http_origin","url":"$request_uri",''"request_id":"$request_id","upstream":"$upstream_addr",''"response_size":"$body_bytes_sent","upstream_response_time":"$upstream_response_time","re??quest_time":"$request_time",''"status":"$status"''"$https_info":"$ssl_client_s_dn$ssl_client_serial$ssl_client_verify"''}';#...}escape=json选项不会替换换行符等打印字符和\n等转义值。引号和反斜杠也将被转义。如果是K8S容器云节点服务,使用filter直接指定:filter{json{source=>"log"remove_field=>["log"]}}灰度发布(A/B测试)进行操作和维护部为了保证服务升级,往往采用灰度发布的方式,逐步将用户切换到新版本。在Nginx中,可以使用split_client模块来提供逐步升级的功能。它有点类似于map函数,但它不是通过某种模式设置变量,而是根据源变量的分布创建变量。这是一个示例:http{upstreamcurrent{serverbackend1;serverbackend2;}upstreamnew{serverdev.showmax_fails=0;}split_clients$arg_key$new_api{5%1;*0;}map$new_api:$cookie_app_switch$destination{~.*:1new;~0:.*current;~1:.*new;}server{#...location/api{proxy_pass$destination/;}}}本例中app_switch和split_clients的cookie值合并为生成调度密钥。如果cookie设置为设置$destination以将新的上游分派到1。否则,从split_clients分派。这会产生一个用于测试新系统的功能标志:设置了cookie的用户将始终请求新系统。密钥的分布是一致的。如果API密钥已用于split_clients,则具有相同API密钥的用户将始终被放入同一组。使用此配置,可以将流量卸载到新系统,从一小部分开始并逐渐增加。当然,修改percentage参数后,不需要reload即可生效。结语在本文中,我们介绍了Nginx在日常运维中的一些管理秘诀。当然,保密与否,也不完全是个人意见。我希望这可以作为一个起点。有什么意见和建议,可以回复说明。
