【转载】为什么 nginx 主机的 io 使用率会 100%?
73 次浏览
发表于 2021-09-29 17:22
转载链接:

作者: BUG侦探

原文链接:https://xie.infoq.cn/article/775d768a25697ef02bef4aa8c

起因

有业务反馈构建容器镜像失败,查看代理日志和监控发现 4xx、5xx 响应码有增长,起初怀疑是后端服务响应慢,排查后没有发现异常,然后发现了 nginx 主机的 io 很高,iostat 看 utilization 到 100%,iotop 查看只有 nginx 在大量写磁盘,第一反应是 proxy buffer 落盘了

基本信息


我们的镜像仓库共有新旧两套,通过 nginx 上配置的规则做转发,主 nginx001/002 是万兆网卡,此时两台主机都出现了 io 爆满的情况,备用 nginx 也承载其他环境的请求

应急处理

影响范围:影响业务构建和发布

构建上针对有反馈的业务紧急处理:

  1. 其中一台构建机修改 host,绕过主 nginx 通过备用 nginx 代理至镜像仓库

先后的处理方式:

  1. 切回老的 registry 仓库,评估需要同步数据+关闭构建,放弃 【确定问题原因之前】

    关闭 proxy_buffer,不确定是否会导致问题更严重,放弃

  2. 调大 proxy_buffers size 和数量,评估可控,分别调整数量到 64、128、512,对应带宽会上涨,有效果能缓解但是作用不大

  3. 扩容其他 nginx 代理进来,需要单独主机,否则可能会影响到其他域名

  4. 扩容到备用 nginx,评估可能造成备用也不可用,影响其他环境访问,逐步依次扩容 1、2 台,期间备用也概率出现 io 爆满的问题

  5. nginx 使用 proxy_limit_rate 进行限速,还没评估好值+问题恢复

  6. 关闭其中一台 proxy_buffer 观察,此时问题已在逐步恢复

最终如何恢复的?

应该主要是在离线集群集中拉取完镜像后自动恢复的

调大 proxy_buffers size 和数量能解决什么问题?

尽量多的使用内存存放后端响应,降低写磁盘的数量,针对响应体很大的情况作用不大,比如 6GB+ 的镜像

排查过程

结论:镜像拉取量太大导致 nignx buffer 落磁盘导致的 io 爆满,这个问题一直都存在,当天被触发并且持续时间长,触发的直接原因不确定

之前是否出现过?

5-12 21:42 左右 io 也爆满过,持续时间短很快恢复,也对应带宽到 5G+



当天是否有变动?

主要两个变动:

  1. 镜像仓库 harbor 升级网卡 10Gb → 25Gb

    在离线集群集中进场新增放量 300+ 实例,其中有大镜像,跟 io 爆满时间点吻合

是否一直存在带宽瓶颈导致的拉镜像慢问题?

存在,不过只影响在离线集群和镜像较大的项目;瓶颈主要在后端主机上,升级 25G 网卡后吞吐能提升

原理分析

结论:问题直接原因就是客户端接收数据慢导致的,最快的恢复办法是关闭 proxy_buffering

nginx 做反向代理时默认情况下是开启 proxy_buffering 配置的,会把后端的响应缓冲到内存中,这样可以最快的速度让后端完成本次请求以释放资源。可是当响应体大于缓冲区(默认 8 * 4k)时,会临时写到磁盘上,默认配置最多可写 1GB,同时写入速度是  2 * buffer size = 8KB,由于请求的镜像大至 GB+ && 同时拉取量大 && 大于客户端接收速度,导致写盘频繁最终 io 爆


proxy_buffering 文档说明


关闭 proxy buffer 会有什么问题?从文档里看会同步的把后端响应发给客户端,信息量比较少,需要从整个请求处理流程来看,以下是 nginx 请求处理时序图简化版


默认 proxy_buffer 开启时


proxy_buffer 关闭时


可以看出来是否启用 proxy_buffering 在处理逻辑上差别很大。

upstream 模块在调用 ngx_http_upstream_send_response 的时候开始出现差异,关闭的时会注册 r/w 的 handler 为 

ngx_http_upstream_process_non_buffered_downstream、

ngx_http_upstream_process_non_buffered_upstream,

否则注册为

ngx_http_upstream_process_upstream、

ngx_http_upstream_process_downstream


开启时会使用 pipe 适配上游速度做转发,依次尝试 free_buffer -> 分配 buffer -> 转发下游 -> 写临时文件 ->  停止读上游 


关闭时是适配下游速度转发,无论处理上游还是下游,都会最终调用 ngx_http_upstream_process_non_buffered_request 同步读取 &发送数据,会导致 nginx、 后端建立更多的连接直至请求处理完成才能释放或者复用

所以关闭 proxy buffer 的影响是会导致连接数增长,nginx 主循环中注册的事件变多,在我们这个场景中因为客户端数量有限,所以副作用基本可忽略


nginx 针对这个参数是默认打开的,这是作为反向代理的基本功能,可以帮后端保持住大量的网络连接,除了以下情况都不建议关闭:

  1. 客户端数量少,nginx 主要以转发大文件为主

  2. 客户端接收速度远小于后端响应速度且同时响应体大于 nginx 主机可用内存一定比例,比如 50%

这些情况都可能会导致 nginx 出现数据堆积从而写磁盘

最佳实践

在这个场景中做了以下完善:

  1. 关闭 proxy_buffering,彻底解决这个隐患

    构建请求拆分到独立的 nginx,避免镜像拉取影响到发布系统的构建功能

如果不关闭可以做的优化:

  1. 开启线程池可以避免 worker 进程被阻塞,但是无法避免导致 io 高

    通过调整写盘参数可以避免 io 高但是无法解决 woker 进程被瞬时阻塞

  2. proxy_limit_rate 可以控制接收后端响应体的速度

  3. 线程池+优化写盘参数可能是最佳组合

总结

proxy buffer 是反向代理帮后端抗压很重要的点,也需要根据使用场景来配置


发表评论
发表者

NGINX社区小N助手

暂无个人介绍

  • 8

    文章

  • 0

    关注

  • 1

    粉丝

活动推荐
版权所有©F5 Networks,Inc.保留所有权利。京ICP备16013763号-5