nginx线程卡死导致OOM排障

203 阅读5分钟

故障现象

周巡检,发现生产nginx挂掉,oom。由于部署两台nginx及keepalived构成高可用,因此没有发生服务不可用故障,流量切换到另一台服务器。 image.png 主机监控,发现内存水位很高,主机内存64GB,并发生跳变。 image.png

排查过程及解决方案

第一层

使用下面这个命令,可以看到有很多shutting down进程,卡死进程数147,正常进程数56。

ps -C nginx -o pid,rss,vsz,cmd

root@idc-alb-nginx-p2:~# ps -C nginx -o pid,rss,vsz,cmd
PID RSS VSZ CMD
315602 15320 48316 nginx: master process /usr/local/openresty/nginx/sbin/nginx -g daemon on; master_process on;
2621335 296392 319752 nginx: worker process is shutting down
2621337 296524 319624 nginx: worker process is shutting down
2621338 296540 319624 nginx: worker process is shutting down
2621341 296260 319624 nginx: worker process is shutting down
2621343 296608 319752 nginx: worker process is shutting down
……
2873866 303692 331324 nginx: worker process
2873867 303668 331324 nginx: worker process
2873868 303820 331324 nginx: worker process
2873869 303816 331324 nginx: worker process
……

单个worker线程300MB内存,147个,差不多就是40G+,基本可以锁定问题就出在这里。

第二层

问下gpt worker进程卡死原因。推测以下答案靠谱:

频繁执行 nginx -s reload,旧 worker 收不到退出信号或处理慢,导致大量僵尸 worker 堆积。

公司自动化运维,可以通过CICD来更新nginx配置(需要devops审批),在周末发生比较频繁。

reload会批量杀死并创建线程,也符合内存异常跳变现象。

查询CICD记录和监控内存跳变时刻基本对得上,至此可以判断出来是更新nginx配置,引起线程卡死。

临时解决方案

  1. kill掉所有卡死进程
ps -C nginx -o pid,state,cmd | grep 'shutting down' | head -5 | awk '{print $1}' | while read pid; do
  echo "Killing nginx worker PID $pid"
  kill -9 $pid
done

内存确实减少到15G左右,不用担心再次OOM。

  1. 比较粗暴永久解决方案,在nginx.conf增加配置 worker_shutdown_timeout 60s;

worker_shutdown_timeout 用来控制 worker 进程在接收到退出信号后,最多等待多少时间完成关闭,超过这个时间会被强制杀掉。

默认是 0,表示无限等待,worker 不会超时强制退出,可能导致 shutting down 状态卡住。

第三层

下一步定位下,为什么reload导致线程卡死。 通过以下命令获取nginx进程,并通过FD取反查网络连接。

strace -p [pid]
lsof -n -p 2828695 |grep -E '172|182'

image.png

2883是ob端口,ip也符合ob的ip地址。至此,可以断定ob阻止了线程释放。

这组nginx是L4和L7混用的,而配置经常重载遇上数据库长连接保持,阻止了worker释放,因为不是所有ob连接集中在某几个worker,可能在任意worker。最终,导致卡死。

根本解决方案

  1. 申请专门数据库nginx组成lb,与业务域名nginx相互隔离

其他收获

用到其他命令:

# 查看内存占用高的进程
ps aux --sort=-rss | head -20
---
root@idc-alb-nginx-p2:~# cat /proc/sys/net/ipv4/tcp_rmem
16384   1048576 12582912
root@idc-alb-nginx-p2:~# cat /proc/sys/net/ipv4/tcp_wmem
16384   1048576 12582912

TCP 内核缓冲参数:
tcp_rmem: 最小 16 KB,默认 1 MB,最大 12 MB
tcp_wmem: 最小 16 KB,默认 1 MB,最大 12 MB
---
root@idc-alb-nginx-p2:~# ss -s
Total: 6460
TCP:   5767 (estab 5669, closed 83, orphaned 6, timewait 83)
参数用途影响范围内存影响
proxy_buffer_size响应头部缓冲区每个连接一次性分配较小
proxy_buffers响应主体缓冲区每个连接按需分配(可能多个)较大,影响显著
root@idc-alb-nginx-p2:~# ss -s
Total: 6460
TCP:   5767 (estab 5669, closed 83, orphaned 6, timewait 83)

Transport Total     IP        IPv6
RAW       2         2         0        
UDP       292       291       1        
TCP       5684      5681      3        
INET      5978      5974      4        
FRAG      0         0         0
说明
RAW原始套接字(Raw sockets),常用于网络工具、抓包等直接操作协议层数据的情况。IPv4有2个,IPv6无。
UDPUDP协议套接字,传输无连接的报文。IPv4有291个,IPv6有1个。
TCPTCP协议套接字,面向连接的传输协议。IPv4有5681个,IPv6有3个。
INET表示IPv4和IPv6的套接字总和,5974(IPv4)+4(IPv6)=5978总数。
FRAGIP分片套接字,用于处理IP数据包的分片和重组。这里为0。