为什么 nginx 主机的 io 使用率会 100%?
起因
有业务反馈构建容器镜像失败,查看代理日志和监控发现 4xx、5xx 响应码有增长,起初怀疑是后端服务响应慢,排查后没有发现异常,然后发现了 nginx 主机的 io 很高,iostat 看 utilization 到 100%,iotop 查看只有 nginx 在大量写磁盘,第一反应是 proxy buffer 落盘了
基本信息
我们的镜像仓库共有新旧两套,通过 nginx 上配置的规则做转发,主 nginx001/002 是万兆网卡,此时两台主机都出现了 io 爆满的情况,备用 nginx 也承载其他环境的请求
应急处理
影响范围:影响业务构建和发布
构建上针对有反馈的业务紧急处理:
其中一台构建机修改 host,绕过主 nginx 通过备用 nginx 代理至镜像仓库
先后的处理方式:
切回老的 registry 仓库,评估需要同步数据+关闭构建,放弃 【确定问题原因之前】
关闭 proxy_buffer,不确定是否会导致问题更严重,放弃
调大 proxy_buffers size 和数量,评估可控,分别调整数量到 64、128、512,对应带宽会上涨,有效果能缓解但是作用不大
扩容其他 nginx 代理进来,需要单独主机,否则可能会影响到其他域名
扩容到备用 nginx,评估可能造成备用也不可用,影响其他环境访问,逐步依次扩容 1、2 台,期间备用也概率出现 io 爆满的问题
nginx 使用 proxy_limit_rate 进行限速,还没评估好值+问题恢复
关闭其中一台 proxy_buffer 观察,此时问题已在逐步恢复
最终如何恢复的?
应该主要是在离线集群集中拉取完镜像后自动恢复的
调大 proxy_buffers size 和数量能解决什么问题?
尽量多的使用内存存放后端响应,降低写磁盘的数量,针对响应体很大的情况作用不大,比如 6GB+ 的镜像
排查过程
结论:镜像拉取量太大导致 nignx buffer 落磁盘导致的 io 爆满,这个问题一直都存在,当天被触发并且持续时间长,触发的直接原因不确定
之前是否出现过?
5-12 21:42 左右 io 也爆满过,持续时间短很快恢复,也对应带宽到 5G+
当天是否有变动?
主要两个变动:
镜像仓库 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 针对这个参数是默认打开的,这是作为反向代理的基本功能,可以帮后端保持住大量的网络连接,除了以下情况都不建议关闭:
客户端数量少,nginx 主要以转发大文件为主
客户端接收速度远小于后端响应速度且同时响应体大于 nginx 主机可用内存一定比例,比如 50%
这些情况都可能会导致 nginx 出现数据堆积从而写磁盘
最佳实践
在这个场景中做了以下完善:
关闭 proxy_buffering,彻底解决这个隐患
构建请求拆分到独立的 nginx,避免镜像拉取影响到发布系统的构建功能
如果不关闭可以做的优化:
开启线程池可以避免 worker 进程被阻塞,但是无法避免导致 io 高
通过调整写盘参数可以避免 io 高但是无法解决 woker 进程被瞬时阻塞
proxy_limit_rate 可以控制接收后端响应体的速度
线程池+优化写盘参数可能是最佳组合
总结
proxy buffer 是反向代理帮后端抗压很重要的点,也需要根据使用场景来配置
版权声明: 本文为 InfoQ 作者【BUG侦探】的原创文章。
原文链接:【http://xie.infoq.cn/article/775d768a25697ef02bef4aa8c】。文章转载请联系作者。
评论