Kubernetes,简单的SpringBoot应用OOMKilled

时间:2019-02-26 19:46:42

标签: java docker kubernetes

我正在使用OpenJDK 11和一个非常简单的SpringBoot应用程序,它几乎唯一具有的功能是启用了SpringBoot执行器,因此我可以调用 / actuator / health 等。

我在GCE上也有一个非常简单的kubernetes集群,只有一个带有容器的Pod(当然也包含这个应用程序)

我的配置有一些要强调的要点,它有一些要求和限制

resources:
  limits:
    memory: 600Mi
  requests:
    memory: 128Mi

它有一个就绪探测器

readinessProbe:
  initialDelaySeconds: 30
  periodSeconds: 30
  httpGet:
    path: /actuator/health
    port: 8080

我还设置了JVM_OPTS,例如(我的程序显然正在使用)

env:
- name: JVM_OPTS
  value: "-XX:MaxRAM=512m"

问题

我启动了这个程序,每次大约3个小时它就会被OOM杀死!

我从不给自己打电话,唯一的电话就是kubernetes每30秒进行一次就绪探针,这足以耗尽内存吗?我也没有实现任何异常,只是实现了一个Get方法,该方法在所有SpringBoot导入中都向您问好,以显示执行器

如果我运行kubectl顶部吊舱XXXXXX,我实际上会看到它逐渐变得越来越大

我尝试了许多不同的配置,技巧等,但是似乎所有基本SpringBoot应用程序都可以使用

是否有一种方法可以通过Java引发OutOfMemory异常的方式来硬限制内存?或防止这种情况发生?

预先感谢


编辑:运行15小时后

NAME                        READY   STATUS    RESTARTS   AGE
pod/test-79fd5c5b59-56654   1/1     Running   4          15h

描述豆荚说...

State:          Running
  Started:      Wed, 27 Feb 2019 10:29:09 +0000
Last State:     Terminated
  Reason:       OOMKilled
  Exit Code:    137
  Started:      Wed, 27 Feb 2019 06:27:39 +0000
  Finished:     Wed, 27 Feb 2019 10:29:08 +0000

最后一个时间跨度约为4小时,仅对/ actuator / health进行了483次调用,显然足以使Java超过MaxRAM提示吗?


编辑:将近17h

它又要死了

$ kubectl top pod test-79fd5c5b59-56654

NAME                    CPU(cores)   MEMORY(bytes)   
test-79fd5c5b59-56654   43m          575Mi      

编辑:在23h失去希望

NAME                        READY   STATUS    RESTARTS   AGE
pod/test-79fd5c5b59-56654   1/1     Running   6          23h

描述豆荚:

State:          Running
      Started:      Wed, 27 Feb 2019 18:01:45 +0000
    Last State:     Terminated
      Reason:       OOMKilled
      Exit Code:    137
      Started:      Wed, 27 Feb 2019 14:12:09 +0000
      Finished:     Wed, 27 Feb 2019 18:01:44 +0000

编辑:一个新发现

昨天晚上,我在读一些有趣的文章:

https://developers.redhat.com/blog/2017/03/14/java-inside-docker/ https://banzaicloud.com/blog/java10-container-sizing/ https://medium.com/adorsys/jvm-memory-settings-in-a-container-environment-64b0840e1d9e

TL; DR我决定删除内存限制并再次启动该过程,结果非常有趣(运行11个小时后)

NAME                    CPU(cores)   MEMORY(bytes)   
test-84ff9d9bd9-77xmh   218m         1122Mi  

那么...那个CPU有什么用?我希望内存使用量很大,但是CPU会怎样?

我能想到的一件事是,GC正在疯狂运行,以为MaxRAM为512m,而他正在使用超过1G的内存。我想知道,Java是否正确检测到人体工程学? (我开始对此表示怀疑)

为了验证我的理论,我将限制设置为512m并以这种方式部署应用程序,我发现从一开始就存在异常的CPU负载,它必须非常频繁地运行GC

kubectl create ...

limitrange/mem-limit-range created 
pod/test created

kubectl exec -it test-64ccb87fd7-5ltb6 /usr/bin/free
              total        used        free      shared  buff/cache   available
Mem:        7658200     1141412     4132708       19948     2384080     6202496
Swap:             0           0           0

kubectl top pod ..
NAME                    CPU(cores)   MEMORY(bytes)   
test-64ccb87fd7-5ltb6   522m         283Mi    

522m的vCPU太多了,所以我的下一步是确保针对这种情况使用最合适的GC,我以此方式更改了JVM_OPTS:

  env:
  - name: JVM_OPTS
    value: "-XX:MaxRAM=512m -Xmx128m -XX:+UseSerialGC"
  ...
    resources:
      requests:
        memory: 256Mi
        cpu: 0.15
      limits:
        memory: 700Mi

kubectl top pod

之后,这又使vCPU使用率再次回到合理的状态
NAME                    CPU(cores)   MEMORY(bytes)   
test-84f4c7445f-kzvd5   13m          305Mi 

使用具有MaxRAM的Xmx进行处理显然会影响JVM,但如何控制虚拟容器上的内存量又又如何呢?我知道free命令将报告主机可用的RAM,但是OpenJDK应该使用 cgroups rihgt?。

我仍在监视内存...


编辑:新希望

我做了两件事,第一件事是再次删除我的容器限制,我想分析它会增长多少,还添加了一个新标志来查看该进程如何使用本机内存-XX:NativeMemoryTracking=summary

起初,一切正常,开始通过kubectl top pod消耗300MB内存,因此我让它运行大约4个小时,然后...

kubectl top pod

NAME                    CPU(cores)   MEMORY(bytes)
test-646864bc48-69wm2   54m          645Mi

有点期望吧?但后来我检查了本机内存使用情况

jcmd <PID> VM.native_memory summary

Native Memory Tracking:

Total: reserved=2780631KB, committed=536883KB
-                 Java Heap (reserved=131072KB, committed=120896KB)
                            (mmap: reserved=131072KB, committed=120896KB)

-                     Class (reserved=203583KB, committed=92263KB)
                            (classes #17086)
                            (  instance classes #15957, array classes #1129)
                            (malloc=2879KB #44797)
                            (mmap: reserved=200704KB, committed=89384KB)
                            (  Metadata:   )
                            (    reserved=77824KB, committed=77480KB)
                            (    used=76069KB)
                            (    free=1411KB)
                            (    waste=0KB =0.00%)
                            (  Class space:)
                            (    reserved=122880KB, committed=11904KB)
                            (    used=10967KB)
                            (    free=937KB)
                            (    waste=0KB =0.00%)

-                    Thread (reserved=2126472KB, committed=222584KB)
                            (thread #2059)
                            (stack: reserved=2116644KB, committed=212756KB)
                            (malloc=7415KB #10299)
                            (arena=2413KB #4116)

-                      Code (reserved=249957KB, committed=31621KB)
                            (malloc=2269KB #9949)
                            (mmap: reserved=247688KB, committed=29352KB)

-                        GC (reserved=951KB, committed=923KB)
                            (malloc=519KB #1742)
                            (mmap: reserved=432KB, committed=404KB)

-                  Compiler (reserved=1913KB, committed=1913KB)
                            (malloc=1783KB #1343)
                            (arena=131KB #5)

-                  Internal (reserved=7798KB, committed=7798KB)
                            (malloc=7758KB #28415)
                            (mmap: reserved=40KB, committed=40KB)

-                     Other (reserved=32304KB, committed=32304KB)
                            (malloc=32304KB #3030)

-                    Symbol (reserved=20616KB, committed=20616KB)
                            (malloc=17475KB #212850)
                            (arena=3141KB #1)

-    Native Memory Tracking (reserved=5417KB, committed=5417KB)
                            (malloc=347KB #4494)
                            (tracking overhead=5070KB)

-               Arena Chunk (reserved=241KB, committed=241KB)
                            (malloc=241KB)

-                   Logging (reserved=4KB, committed=4KB)
                            (malloc=4KB #184)

-                 Arguments (reserved=17KB, committed=17KB)
                            (malloc=17KB #469)

-                    Module (reserved=286KB, committed=286KB)
                            (malloc=286KB #2704)

等等,什么?为线程保留2.1 GB?和222 MB正在使用,这是什么?我目前不知道,我只是看到了...

我需要时间来了解为什么会发生

1 个答案:

答案 0 :(得分:0)

我终于找到了自己的问题,并希望与他人分享,以便其他人可以从中以某种方式受益。

正如我在上一次编辑中发现的那样,我遇到了一个线程问题,该问题导致了一段时间后所有内存的消耗,特别是我们在使用第三方库中的异步方法时未适当照顾那些资源(确保这些调用正确结束)在这种情况下)。

之所以能够检测到该问题,是因为我从一开始就在kubernete部署中使用了内存限制(这在生产环境中是一种很好的做法),然后我使用{{1} },最重要的是jstat, jcmd, visualvm, kill -3标志在这方面给了我很多细节。