避免10大Nginx配置错误(运维速查必读)

阿里云教程3个月前发布
16 0 0

补完那些配置后,线上延迟回落,内存占用稳定下来,安全告警也不再频繁触发。那天深夜把最后一条限流规则发到主机上,监控曲线终于收口,大家松了口气。

先说结尾,是由于事情是倒着发生的。先把修复、验证、回滚的流程交代清楚,再把问题一条条还原。我们是在生产环境里一步步排查、试错、回滚、再修正的,过程里每一步都有具体改动和值得记下的教训。下面按时间线倒过来,把每个坑讲清楚,顺带给出合理的配置提议和为什么这样设置。

修完之后的验证很关键。部署后我在灰度机和少量流量下跑了48小时的压测,重放了真实请求,检查了连接数、响应时间、错误率和TLS握手情况。日志分离后,静态资源的访问日志几乎不写盘,磁盘IO降低了。限流规则把异常请求挡住,登录接口的爆破尝试基本消失。证书链、强制HSTS也没有影响正常用户。确认这些后才把配置逐步推到主集群。

在把变更拉回主集群之前,我们先在测试环境做了完整验证。每一条配置都有回退脚本,方便出问题立刻回滚。推配置是分阶段的:先一台,观察一小时;没问题就推一小批;再看一天;最后全量。这个流程救了我们好几次,由于有些微妙的相互影响只有在高并发下才显现。

问题最初是怎么暴露出来的:用户抱怨图片加载慢,接口延迟高,偶发的502和超时。安全扫描还提示有老旧TLS支持和缺少安全头。翻查配置后发现好几个常见但被忽视的坑,就像多年的老毛病一次性爆发出来。

下面把这些坑一项项列出来,按我改的顺序来讲,每条都会说明当时的错误、改成什么、为什么这样以及要注意的细节。

1) worker_processes 和 worker_connections

错误表现是CPU利用不均和连接数瓶颈。有的机器把 worker_processes 写死成1,结果不能利用多核;有的把它设置得很高但没有配合ulimit,导致进程竞争和文件描述符耗尽。

改成了 worker_processes auto,worker_connections 10240,外加 worker_rlimit_nofile 设置为更高的值。auto会根据核数自动分配,结合操作系统的fd限制,才能真正提升并发。别只看一个值,核数、ulimit、每个连接的fd消耗都得算上。

2) client_header_buffer_size / large_client_header_buffers / client_max_body_size

以前有人把头部缓冲设得很小,导致大请求头频繁写磁盘;也有人把buffer设得特别大,想“保险点”,结果浪费内存。还有上传接口没设好,导致超大请求撑爆后端。

把头部缓冲设为 client_header_buffer_size 2m,
large_client_header_buffers 4 2m。请求体大小根据业务定,上传接口单独放到子域并设置 client_max_body_size 合理值,必要时给后端做分块上传。缓冲要与后端和磁盘I/O能力匹配,不能随意放大或缩小。

3) proxy_buffer_size / proxy_buffers / proxy_busy_buffers_size

代理后端时缓冲配置直接影响磁盘IO和内存使用。原先用默认值,导致大响应直接走磁盘或频繁切换。

把 proxy_buffer_size 和 proxy_buffers 配成适配常见响应大小的组合,列如 proxy_buffer_size 128k,proxy_buffers 8 256k,proxy_busy_buffers_size 256k。响应较大的API可以用更高的缓冲或直接给后端走流式传输。关键是观察响应大小分布再调整。

4) keepalive_timeout 与 keepalive_requests

有人把keepalive关掉,或者把超时时间设得过短;还有把请求数设过高没配其它限流,结果长连接占满资源。

保活能明显降低TCP握手开销,提议 keepalive_timeout 30-75s,keepalive_requests 100-1000 之间按业务调。留意连接数峰值,配合 backend 的连接池和操作系统的TIME_WAIT策略来调。

5) 日志策略

运行时日志写满磁盘是常见问题。我们之前把所有静态资源都打到同一个access_log里,磁盘IO被日志写满。

改成把静态资源单独一条日志规则,甚至直接 access_log off 对图片、字体等静态资源关闭记录。动态请求单独记录,错误日志分级。配合日志收集系统(列如文件滚动、异步推送)才能既保留排查能力又不被日志拖垮。

6) 缓存策略(Cache-Control)

没给静态资源合适的缓存期限,或者都给了长缓存,发布更新后又没人注意版本控制,导致客户端拿到过期资源。

我把图片类资源设置为一年(带ETag/版本管理的除外),CSS/JS 默认1个月,带hash的资源可以长期缓存并永久缓存。并且在部署流程里强制带hash的文件名。这样既减少了带宽又避免旧资源影响新版页面。

7) 安全头(X-Frame-Options / X-Content-Type-Options / CSP 等)

安全扫描提示缺少防护头。原先假设后端会加这些头,结果各种微服务不同步,安全策略不一致。

统一在Nginx层加上:X-Frame-Options SAMEORIGIN、X-Content-Type-Options nosniff、Referrer-Policy、Content-Security-Policy 根据页面需求定。CSP必定要循序渐进上线,先报告模式观察,再逐步强化。别一上来就把CSP写死,容易把自己锁死。

8) TLS 配置

站点还支持 TLS1.0/1.1,弱密码套件也在允许列表里。安全扫描每次都会打高危。

把 TLS 最低版本限制到 TLS1.2,优先使用 TLS1.3,剔除 RC4、DES 等老套件,启用 HSTS。证书链要完整,OCSP Stapling 打开。上线后在多个客户端上做兼容性测试,别由于太严格影响老客户访问。

9) 头部传递与超时(proxy_set_header / proxy_read_timeout / send_timeout)

原配置没有正确传递真实IP,后端日志里面看到都是Nginx的IP,导致审计和限流失效。还有超时时间设置太大,导致请求堆积。

统一写明 proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 并合理设置 proxy_read_timeout、proxy_send_timeout、send_timeout 等,防止长连接把工作进程占满。超时时间设短了会中断长操作,设长了会耗尽资源,得看接口特性设定。

10) 限流和内部访问控制(limit_req_zone / internal / 禁止危险文件访问)

没有限流,导致某些API被恶意刷;有些内部接口没有做 internal 限制,外网能直接访问,还有人把敏感文件放在可执行路径下。

用 limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s,并对登录这类敏感接口单独设低速率(列如2r/s)。把只有内部调用的 location 设置 internal,外部请求会被拒绝。禁止访问 .git、备份、敏感脚本,location ~* .(sql|bak|zip|swp)$ { deny all; }。若用PHP等解释器,确保 try_files 和 fastcgi_pass 的顺序正确,避免路径遍历或直接执行上传目录里的文件。

补丁上了之后,有几次是小细节救了我们。列如把静态资源日志直接关掉那天,磁盘利用率从90%降到30%;把限流规则上线后,登录接口的异常请求数骤降。还有一次是TLS配置看起来没问题,但在某些老手机上失败,是由于我们忘了留回退的cipher,最后把某些兼容选项单独列入白名单,避免影响这部分用户。

说实话,许多坑都是长期积累的。有人为了“先跑起来”临时改了配置,没人后续审计;有的以为默认值够用。作为运维或者架构,得常常复查这些基础配置。做版本化、审计、分阶段部署,这三点在运维里太实用了。

改配置的脚本、回滚脚本和灰度流程我们都留了记录。每改一次,都在变更单里写清楚缘由、验证步骤和回滚点。那晚最后一条限流规则通过自动化推到主集群的时候,我站在监控面板前,看到错误率曲线往下掉,心里有种“终于把这些老毛病堵完了”的轻松感。

© 版权声明

相关文章

暂无评论

none
暂无评论...