记一场由OOM引发的环境“雪崩”

发布时间:2024年01月19日

湖蓝几何球体LinkedIn Banner.png
转载说明:如果您喜欢这篇文章并打算转载它,请私信作者取得授权。感谢您喜爱本文,请文明转载,谢谢。


问题描述

x年x月x日下午2:40左右发现某环境出现故障,某些功能无法正常运行。于是马上进行排查:
1、基础服务端口运行都是正常的
2、查看环境上最近有新发版的三个微服务,发现都在不同频率的打印这句日志:

INFO [DubboMonitor.java:80] :  [DUBBO] Send statistics to monitor zookeeper://192.169.1.111:2181/com.alibaba.dubbo.monitor.MonitorService?anyhost=true&application=dubbo-monitor&check=false&delay=-1&dubbo=crud&generic=false&interface=com.alibaba.dubbo.monitor.MonitorService&methods=lookup,collect&pid=11&revision=monitors&side=provider×tamp=1568598922300, dubbo version: crud, current host: 10.42.91.223

因为之前有一个微服务出现OutOfMemoryError的时候,也一直打印这些日志,因此将三个容器日志导出来查看,刚刚导了两个日志,正在导第三个日志的时候,发现docker命令无法执行,docker挂了…
先重新启动了docker服务,恢复了业务,然后查看docker挂掉的原因。

故障排查分析

1. 查看/var/log/messages日志

将messages文件中跟docker有关的内容过滤出来,发现了这样的信息(部分日志):

14:43:07 rancher-node dockerd-current: time="20xx-0x-xxT14:43:07.982713104+08:00" level=error msg="collecting stats for 587cf4938bed5e3172868d85ae41db3af37e9c1a6cd8192f1cfa22a4e969d53b: rpc error: code = 2 desc = fork/exec /usr/libexec/docker/docker-runc-current: cannot allocate memory: \"\""
14:45:04 rancher-node journal: Suppressed 1116 messages from /system.slice/docker.service
14:45:05 rancher-node dockerd-current: time="20xx-0x-xxT14:45:05.410928493+08:00" level=info msg="Processing signal 'terminated'"
14:45:05 rancher-node journal: time="20xx-0x-xxT06:45:05Z" level=error msg="Error processing event &events.Message{Status:\"kill\", ID:\"af42628b1354b74d08b195c0064d8c5d760c826626a3ad36501a85c824d2204d\", From:\"prod.locmn.cn/prod/locmn-drols-query-chq:latest\", Type:\"container\", Action:\"kill\", ..... Error: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?" 

就是说在14:45的时候,docker就已经无法分配到内存了

然后信号终止,docker进程被杀掉,因此docker的命令无法运行,所有docker容器也都一起挂掉了:

14:45:05 rancher-node dockerd-current: time="20xx-0x-xxT14:45:05.410928493+08:00" level=info msg="Processing signal 'terminated(处理信号的终止)'"
14:45:05 rancher-node journal: time="20xx-0x-xxT06:45:05Z" level=error msg="Error processing event &events.Message{Status:\"kill\", ID:\"af42628b1354b74d08b195c0064d8c5d760c826626a3ad36501a85c824d2204d\", From:\"registry.locman.cn/sefon-online/locman-drools-query-chq:latest\", Type:\"container\", Action:\"kill\", ......Error: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?" 

2. 查看业务日志

后面细查新发版的三个微服务的业务的日志,发现在14:03分的时候,有一个叫xxcud的服务有**“java.lang.OutOfMemoryError”**的报错:

20xx-0x-xx 14:03:10,554 ERROR [ExceptionFilter.java:87] :  [DUBBO] Got unchecked and undeclared exception which called by 10.42.83.124. service: com.run.locman.api.crud.service.AlarmInfoCrudService, method: add, exception: java.lang.OutOfMemoryError: unable to create new native thread, dubbo version: crud, current host: 10.42.91.223
java.lang.OutOfMemoryError: unable to create new native thread
  at java.lang.Thread.start0(Native Method)
......(省略部分日志内容)
Exception in thread "pool-1-thread-3" java.lang.OutOfMemoryError: unable to create new native thread
  at java.lang.Thread.start0(Native Method)
  at java.lang.Thread.start(Thread.java:714)

原来是这个叫xxcud服务的内存溢出了引发的故障,导致了docker服务被kill掉,所有的docker容器瞬间全部挂掉…

故障排查结论

和研发一起对故障进行了分析,发现有两个原因:
1、 这个服务有一个线程池,在代码里面设置的最小是8,最大限制是2147483647 ,用完的线程要1分钟之后才能回收。这就存在两个问题:

1)业务在持续不断的发送请求,这个服务就会一直创建线程,而因为给定的线程最大值过大,相当于可以无限制的创建线程了,会一直消耗资源;

2)用完的线程1分钟之后才会回收,时间过长。

在这两点的影响下,程序跑一段时间,就会出现创建大量的线程,过度的消耗内存资源。

2、由于该环境不属于重要环境,当初搭建时比较随意,docker容器没有做容器的内存限制,所以默认情况下容器使用的资源是不受限制的。

docker可以使用主机内核调度器所允许的最大资源,因此当主机发现内存不够用的时候,也会抛出内存溢出的错误。而且会开始杀死一些进程用于释放内存空间。可怕的是任何进程都可能成为内核猎杀的对象,包括 docker daemon 和宿主机上的其它一些重要的程序。更危险的是如果某个支持系统运行的重要进程被kill掉了,整个系统也就宕掉了。

这次的docker服务进程就被杀掉了。

解决方案

研发运维双管齐下:
1、研发优化代码,包括限制线程池的最大线程数量和线程回收的时间,重新发布代码打补丁,后面观察到目前,没有再出现类这个问题了;

2、运维优化,限制docker内存。重新优化了docker容器,限制了docker内存的使用量,减少docker容器过度占用宿主机资源的风险;

3、加强对docker容器的监控与告警;

总结教训

1、docker限制内存,非常重要!
2、docker限制内存的方式:

方法一:静态修改 -m
-m参数:限制docker容器最大使用内存
例如:

$ docker run -it -m 300M --memory-swap -1 --name con1 u-stress /bin/bash

上面的 docker run 命令中通过 -m 选项限制容器使用的内存上限为 300M。
同时设置 memory-swap 值为 -1,它表示容器程序使用内存的受限,而可以使用的 swap 空间使用不受限制(宿主机有多少 swap 容器就可以使用多少)。

方法二:动态修改 docker update
docker update 动态修改docker容器内存
例如:把一个运行着gitlab 的容器内存限制在2048M以内

docker update --memory 2048m --memory-swap -1 gitlab
文章来源:https://blog.csdn.net/WF_crystal/article/details/135688599
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。