記一場由docker容器“ java.lang.OutOfMemoryError”引發的環境“雪崩”

問題描述:

2019.9.16下午2:40左右發現環境出現故障,功能無法正常運行。

馬上進行排查
1、基礎服務端口運行都是正常的
2、查看環境上最近有新發版的三個微服務,發現都在不同頻率的打印這句日誌:

2019-09-16 14:42:41,626  INFO [DubboMonitor.java:80] :  [DUBBO] Send statistics to monitor zookeeper://192.168.1.101: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有關的內容過濾出來,發現了這樣的信息(部分日誌):

Sep 16 14:43:07 rancher-node dockerd-current: time="2019-09-16T14: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: \"\""
Sep 16 14:45:04 rancher-node journal: Suppressed 1116 messages from /system.slice/docker.service
Sep 16 14:45:05 rancher-node dockerd-current: time="2019-09-16T14:45:05.410928493+08:00" level=info msg="Processing signal 'terminated'"
Sep 16 14:45:05 rancher-node journal: time="2019-09-16T06: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容器也都一起掛掉了:

Sep 16 14:45:05 rancher-node dockerd-current: time="2019-09-16T14:45:05.410928493+08:00" level=info msg="Processing signal 'terminated(處理信號的終止)'"
Sep 16 14:45:05 rancher-node journal: time="2019-09-16T06: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?" 

可是爲什麼分配不到內存呢?
查看了主機,內存還有10G的。

2、查看業務日誌

後面細查新發版的三個微服務的業務的日誌,發現在14:03分的時候,有一個叫cud的服務有“java.lang.OutOfMemoryError”的報錯:

2019-09-16 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)

原來是這個叫cud服務的內存溢出了引發的故障,導致了docker服務被kill掉,所有的docker容器瞬間全部掛掉!

故障原因總結

和開發一起對故障進行了分析,發現有兩個原因:
1、這個服務有一個線程池,在代碼裏面設置的最小是8,最大限制是2147483647 ,用完的線程要1分鐘之後才能回收。這就存在兩個問題:
一、業務在持續不斷的發送請求,這個服務就會一直創建線程,而因爲給定的線程最大值過大,相當於可以無限制的創建線程了,會一直消耗資源;
二、用完的線程1分鐘之後纔會回收,時間過長。
在這兩點的影響下,程序跑一段時間,就會出現創建大量的線程,過度的消耗內存資源.

2、由於docker容器在最初的時候沒有做容器的內存限制,所以默認情況下容器使用的資源是不受限制的。
也就是可以使用主機內核調度器所允許的最大資源,因此當主機發現內存不夠用的時候,也會拋出內存溢出的錯誤。而且會開始殺死一些進程用於釋放內存空間。可怕的是任何進程都可能成爲內核獵殺的對象,包括 docker daemon 和宿主機上的其它一些重要的程序。更危險的是如果某個支持系統運行的重要進程被kill掉了,整個系統也就宕掉了。
這次的docker服務進程就被殺掉了。

解決方案

1、開發優化代碼,包括限制線程池的最大線程數量和線程回收的時間,重新發布代碼打補丁,後面觀察到目前,沒有再出現類這個問題了;
2、限制docker內存。重新優化了docker容器,限制了docker內存的使用量,減少docker容器過度佔用宿主機資源的風險;
3、加強對docker容器的監控與告警;

總結

1、docker限制內存,非常重要!
2、限制內存的方式(放一個別人寫的修改內存的步驟):

方法一:靜態修改 -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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章