点赞
评论
收藏
分享
举报
避免 10 大 NGINX 配置错误(上)
发表于2022-04-22 17:14

浏览 2.2k

原文作者:Timo Stark of F5 和 Sergey Budnevich of F5
原文链接:避免 10 大 NGINX 配置错误
转载来源:NGINX 官方网站


在帮助 NGINX 用户解决问题时,我们经常会发现配置错误,这种配置错误也屡屡出现在其他用户的配置中,甚至有时还会出现在我们的 NGINX 工程师同事编写的配置中!本文介绍了 10 个最常见的错误,并解释了问题所在以及相应的解决方法。由于本文篇幅较长,我们将分上下两篇发布。

  1. 每个 worker 的文件描述符不足
  2. error_log off 指令
  3. 未启用与上游服务器的 keepalive 连接
  4. 忘记指令继承的工作机制
  5. proxy_buffering off 指令
  6. if 指令使用不当
  7. 过多的健康检查
  8. 不安全地访问指标
  9. 当所有流量都来自同一个 /24 CIDR 块时使用 ip_hash
  10. 不采用上游组


错误 1:每个 worker 没有足够的文件描述符

worker_connections 指令用于设置 NGINX worker 进程可以打开的最大并发连接数(默认为 512)。所有类型的连接(例如与代理服务器的连接)都计入最大值,而不仅仅是客户端连接。但重要的是要记住,最终每个 worker 的并发连接数还有另一个限制:操作系统对分配给每个进程的文件描述符 (file descriptor,即FD) 最大数量的限制。在现代 UNIX 发行版中,默认限制为 1024。

对于除最小的NGINX部署之外的 所有部署,将每个 worker 的连接数限制为 512 可能太少了。事实上,我们将随 NGINX 开源版二进制文件和 NGINX Plus 一起分发的默认 nginx.conf 文件将其增加到 1024。

常见的配置错误是没有将 FD 的限制增加到至少两倍的 worker_connections 的值。解决方法是在主配置上下文中使用 worker_rlimit_nofile 指令设置该值。

这就是需要更多 FD 的原因:从 NGINX worker 进程到客户端或上游服务器的每个连接都消耗一个 FD。当 NGINX 充当 Web 服务器时,每个客户端连接使用一个 FD,每个服务的文件使用一个 FD,这样每个客户端至少需两个 FD(但大多数网页是由许多文件构建的)。当充当代理服务器时,NGINX 分别使用一个 FD 连接客户端和上游服务器,并可能用到第三个 FD 给用于临时存储服务器响应的文件。作为缓存服务器时,NGINX 的行为类似于缓存响应的 Web 服务器,如果缓存为空或过期,则类似于代理服务器。

NGINX 为每个日志文件使用一个 FD,并会用几个 FD 与主进程通信,但与用于连接和文件的 FD 数量相比,这些数量通常很少。

UNIX 提供了几种方法来设置每个进程的 FD 数量:

  • 如果从 shell 启动 NGINX,则使用 ulimit 命令
  • 如果将 NGINX 作为服务启动,则使用init 脚本或 systemd 服务清单变量
  • /etc/security/limits.conf 文件

使用的方法取决于您如何启动 NGINX,而 worker_rlimit_nofile 与您启动 NGINX 的方式无关。

FD 的数量也有系统范围的限制,您可以使用操作系统的 sysctl fs.file-max 命令进行设置。它通常足够大,但有必要验证所有 NGINX worker 进程可能使用的文件描述符的最大数量 (worker_rlimit_nofile * worker_processes) 明显小于 fs.file‑max。如果 NGINX 以某种方式使用了所有可用的 FD(例如,在 DoS 攻击期间),此时甚至都无法登录机器来解决问题。


错误 2:error_log off 指令

常见的错误是认为 error_log off 指令会禁用日志记录。事实上,与 access_log指令不同,error_log 不包含 off 参数。如果在配置中添加了 error_log off 指令,则 NGINX 会在 NGINX 配置文件的默认目录(通常是/etc/nginx)中创建一个名为 off 的错误日志文件。

我们不建议禁用错误日志,因为它是调试 NGINX 任何问题时的重要信息来源。但是,如果存储空间非常有限,记录的数据可能足以耗尽可用的磁盘空间,此时禁用错误日志记录可能有意义。在主配置上下文中包含该指令:

error_log /dev/null emerg;

请注意,在 NGINX 读取并验证配置之前,该指令不会应用。因此,每次 NGINX 启动或重新加载配置时,它可能会记录到默认的错误日志位置(通常为/var/log/nginx/error.log),直到配置验证后。更改日志目录的方法是,在 nginx 命令中添加 -e <error_log_location> 参数。  


错误 3:未启用与上游服务器的 keepalive 连接

默认情况下,NGINX 会为每个新的传入请求打开一个到上游(后端)服务器的新连接。这种操作虽然安全但是低效,因为 NGINX 和服务器必须交换三个数据包来建立连接,并交换三个或四个数据包来终止连接。

在流量高峰期,为每个请求打开一个新连接会耗尽系统资源,最终导致根本无法打开连接。原因是:对于每个连接,源地址、源端口、目标地址和目标端口的4元组必须是唯一的。对于从 NGINX 到上游服务器的连接,四元组中的三个(第一个、第三个和第四个)是固定的,只有源端口是变量。当连接关闭时,Linux 套接字会处于 TIME‑WAIT 状态两分钟,在流量高峰期时这会增加可用源端口池耗尽的可能性。如果发生这种情况,NGINX 将无法打开与上游服务器的新连接。

解决方法是在 NGINX 和上游服务器之间启用 keepalive 连接 —— 该连接不会在请求完成时关闭,而是保持打开状态以用于其他请求。这样做既降低了源端口耗尽的可能性,又提高了性能

启用 keepalive 连接的方法是:

  • 在每个 upstream{} 块中包含 keepalive 指令,以设置保存在每个 worker 进程缓存中的到上游服务器的空闲 keepalive 连接数。请注意,keepalive 指令不限制 NGINX worker 进程可以打开的上游服务器的连接总数——这一点经常被误解。所以 keepalive 的参数不需要像您想象的那么大。我们建议将该参数设置为 upstream{} 块中列出的服务器数量的两倍。这足以让 NGINX 保持与所有服务器的 keepalive 连接,同时也足够小,上游服务器还可以处理新的传入连接。另请注意,当您在 upstream{} 块中指定负载均衡算法时 —— 使用 hash、ip_hash、least_conn、least_time 或 random 指令 —— 该指令必须位于 keepalive 指令之前。通常,在 NGINX 配置中,指令顺序并不重要,而这是少数例外之一。
  • 在将请求转发到上游 group 的 location{} 块中,添加以下指令以及 proxy_pass 指令:proxy_http_version 1.1;proxy_set_header "Connection" "";NGINX 默认使用 HTTP/1.0 连接上游服务器,并相应地将 Connection: close 标头添加到它所转发到服务器的请求中。这样尽管 upstream{} 块中包含了keepalive 指令,但每个连接仍然会在请求完成时关闭。proxy_http_version 指令告知 NGINX 使用 HTTP/1.1,proxy_set_header 指令将从 Connection 标头中删除 close 值。


错误 4:忘记指令继承的工作机制

NGINX 指令是向下继承的,或者是“由外而内”继承的:一个上下文(一个嵌套在另一个上下文,即上下文中的上下文)继承父上下文包含的指令的设置。例如,http{} 上下文中的所有server{}location{} 块都继承了包含在 http 级别的指令的值,并且 server{} 块中的指令被它的所有子 location{} 块继承。但是,当父上下文及其子上下文中包含相同的指令时,这些值不会相加 —— 相反,子上下文中的值会覆盖父上下文中的值。

常见的错误是忘记了这种数组指令的“覆盖规则”,它不仅可以包含在多个上下文中,而且还可以在给定上下文中包含多次。例如 proxy_set_headeradd_header —— 第二个名称中包含“add”导致覆盖规则很容易被忘记。

我们可以通过 add_header 的例子来说明继承的工作机制:

http {    add_header X-HTTP-LEVEL-HEADER 1;    add_header X-ANOTHER-HTTP-LEVEL-HEADER 1;    server {        listen 8080;        location / {            return 200 "OK";        }     }    server {        listen 8081;        add_header X-SERVER-LEVEL-HEADER 1;        location / {            return 200 "OK";        }        location /test {            add_header X-LOCATION-LEVEL-HEADER 1;            return 200 "OK";        }        location /correct {            add_header X-HTTP-LEVEL-HEADER 1;            add_header X-ANOTHER-HTTP-LEVEL-HEADER 1;            add_header X-SERVER-LEVEL-HEADER 1;            add_header X-LOCATION-LEVEL-HEADER 1;            return 200 "OK";        }     }}

对于监听端口 8080 的服务器,server{}location{} 块中都没有 add_header 指令。所以继承很简单,我们看到 http{} 上下文中定义了下面两个http消息头:

% curl -is localhost:8080HTTP/1.1 200 OKServer: nginx/1.21.5Date: Mon, 21 Feb 2022 10:12:15 GMTContent-Type: text/plainContent-Length: 2Connection: keep-aliveX-HTTP-LEVEL-HEADER: 1X-ANOTHER-HTTP-LEVEL-HEADER: 1OK

对于监听端口 8081 的服务器,server{} 块中有一个 add_header 指令,但其子 location / 块中没有该指令。server{} 块中定义的消息头覆盖了 http{} 上下文中定义的两个http消息头:

% curl -is localhost:8081HTTP/1.1 200 OKServer: nginx/1.21.5Date: Mon, 21 Feb 2022 10:12:20 GMTContent-Type: text/plainContent-Length: 2Connection: keep-aliveX-SERVER-LEVEL-HEADER: 1OK

在子 location /test 块中,有一个 add_header 指令,该指令覆盖了其父 server{} 块中的http消息头和 http{} 上下文中的两个http消息头:

% curl -is localhost:8081/testHTTP/1.1 200 OKServer: nginx/1.21.5Date: Mon, 21 Feb 2022 10:12:25 GMTContent-Type: text/plainContent-Length: 2Connection: keep-aliveX-LOCATION-LEVEL-HEADER: 1OK

如果我们希望 location{} 块保留其父上下文中定义的http消息头以及本地定义的任何http消息头,我们必须在 location{} 块中重新定义父http消息头。这就是我们在 location /correct 块中所做的:

% curl -is localhost:8081/correctHTTP/1.1 200 OKServer: nginx/1.21.5Date: Mon, 21 Feb 2022 10:12:30 GMTContent-Type: text/plainContent-Length: 2Connection: keep-aliveX-HTTP-LEVEL-HEADER: 1X-ANOTHER-HTTP-LEVEL-HEADER: 1X-SERVER-LEVEL-HEADER: 1X-LOCATION-LEVEL-HEADER: 1OK


错误 5: proxy_buffering off 指令

NGINX 默认启用代理缓冲(proxy_buffering 指令设置为 on)。代理缓冲意味着 NGINX 将来自服务器的响应存储在内部缓冲区中,并且在整个响应被缓冲之后才开始向客户端发送数据。缓冲有助于优化慢速客户端的性能 —— 因为 NGINX 缓冲响应的时间与客户端检索所有响应的时间一样长,代理服务器可以尽可能快地返回响应,然后返回到可用的状态以响应其他请求。

如果代理缓冲被禁用,则 NGINX 只会在默认为一个内存页大小(4 KB 或 8 KB,具体取决于操作系统)的缓冲区内缓存服务器响应的开头部分后就开始向客户端传输。通常,这个缓存空间只够缓存响应http消息头。NGINX 收到响应后会同步发送给客户端,迫使服务器处于空闲状态,直到 NGINX 可以接受下一个响应段为止。

因此,对于经常在 NGINX 配置中看到 proxy_buffering off 指令的情况,我们感到非常惊讶。也许这样做是为了减少客户端延迟,但其影响可以忽略不计,而关闭后的副作用却很多:代理缓冲被禁用后,速率限制和缓存即便配置了也不起作用,性能也会受影响等等。

只有在少数情况下,禁用代理缓冲可能有意义(例如长轮询),因此我们强烈建议不要更改默认设置。有关更多信息,请参阅《NGINX Plus 管理指南》


更多资源

想要更及时全面地获取 NGINX 相关的技术干货、互动问答、系列课程、活动资源?

请前往 NGINX 开源社区:

  • 官网:nginx.org.cn
  • 微信公众号:https://mp.weixin.qq.com/s/XVE5yvDbmJtpV2alsIFwJg
  • 微信群:https://www.nginx.org.cn/static/pc/images/homePage/QR-code.png?v=1621313354
  • B 站:https://space.bilibili.com/628384319
已修改于2023-10-26 16:14
本作品系原创
创作不易,留下一份鼓励
NGINX官方账号

暂无个人介绍

关注



写下您的评论
发表评论
全部评论(1)

按点赞数排序

按时间排序

good good

赞同

0

回复举报

发表于2023-01-16 10:15



回复风声
回复
关于作者
NGINX官方账号
这家伙很懒还未留下介绍~
228
文章
21
问答
198
粉丝
相关文章
介绍nginx网页配置工具QQ技术交流群1:1106758598QQ技术交流群2:560797506邮箱: cym1102@qq.com官网地址: http://www.nginxwebui.cn码云: https://gitee.com/cym1102/nginxWebUIgithub: https://github.com/cym1102/nginxWebUI功能特点nginxWebUI也可管理多个nginx服务器集群,随时一键切换到对应服务器上进行nginx配置,也可以一键将某台服务器配置同步到其他服务器,方便集群管理.部署此项目后,配置nginx再也不用上网各种搜索配置代码,再也不用手动申请和配置ssl证书,只需要在本项目中进行增删改查就可方便的配置和启动nginx。技术说明本项目是基于springBoot的web系统,数据库使用sqlite,因此服务器上不需要安装任何数据库项目启动时会释放一个.sqlite.db到系统用户文件夹中,注意进行备份本系统通过Let'sencrypt申请证书,使用acme.sh脚本
点赞 6
浏览 6.1k
  前三周学习了陶辉老师的“NGINX基础培训系列课程”,感觉受益良多,在这里想把一些知识点记录一下,和大家分享一下知识点,也方便日后的随手查看,温故知新。  首先,我们了解到了Nginx的版本,Nginx发布版本分为主线版本和稳定版本,区分两个版本也非常简单,主线版本版本号为单数,比如1.19,稳定版本为双数,比如1.18,今天我要说的是稳定版本,这个版本会尽量少的减少Nginx的bug问题,适用于生产环境,这里我不建议使用Nginx和其他软件一样在生产环境中落后一个或多个大版本使用,之前生产环境做过漏扫,发现我们编译自带的Nginx版本为:nginx/1.13.3(查询命令为nginx-V),结果出现了多个漏洞,四个高危和一个中危漏洞:        通过升级Nginx到稳定版最新版本后修复!  其次,是Nginx发行版本的选择,目前比较流行的有:nginx、nginxplus、Tengine、openresty、ope
点赞 1
浏览 3.2k
感谢您参加“NGINX从入门到精通进阶系列培训”!以下为培训的问答、课件和录像,希望您能通过此培训学有所得,祝学习进步!>问与答:- 基础篇+高级篇 - 应用篇+实战篇(New)>课件(PPT):基础篇:-NGINX概要、安装、配置:https://interact.f5.com/rs/653-SMC-783/images/CNFEB22-NginxCoreCourse-Setup.pdf-NGINX日志、运维:https://interact.f5.com/rs/653-SMC-783/images/cnfeb22-nginxcorecourse-maintenance.pdf高级篇:-NGINX变量、API:https://interact.f5.com/rs/653-SMC-783/images/CNFEB22-NginxCoreCourse-API.pdf-NGINXSSL、NJS:https://interact.f5.com/rs/653-SMC-783/images/CNFEB22-NginxCoreCourse-SSL.pdf
点赞 10
浏览 4.8k