Kubernetes, простое приложение SpringBoot OOMKilled - PullRequest
0 голосов
/ 26 февраля 2019

Я работаю с OpenJDK 11 и очень простым приложением SpringBoot, которое почти единственное, что у него есть, это активированный привод SpringBoot, поэтому я могу вызывать / привод / здоровье и т. Д.

У меня также есть кластер kubernetes на GCE, очень простой, с пакетом с контейнером (конечно, с этим приложением)

В моей конфигурации есть несколько ключевых моментов, которые я хочу выделить, есть некоторые требования и ограничения

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"

Проблема

Я запускаю это, и он получает OOMKilled примерно через 3 часа каждый раз!

Я никогда ничего не вызываю сам, единственный вызов - это проверка готовности каждые 30 секунд, что kubernetesделает, и этого достаточно, чтобы исчерпать память?Я также не реализовал ничего необычного, просто метод Get, который говорит привет всем по всему импорту SpringBoot, чтобы иметь исполнительные механизмы

Если я запускаю kubectl top pod 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 часов и имеет только 483 вызова / actator / health, очевидно, этого было достаточно, чтобы java превысил подсказку MaxRAM?


РЕДАКТИРОВАТЬ: почти 17 ч

это примерночтобы умереть снова

$ kubectl top pod test-79fd5c5b59-56654

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

РЕДАКТИРОВАТЬ: потерять всякую надежду в 23 часа

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  

Итак ... WTH с этим процессором?Я ожидаю большого количества использования памяти, но что происходит с процессором?

Единственное, что я могу подумать, это то, что GC работает как сумасшедший, думая, что MaxRAM составляет 512 м, и он использует более 1G.Мне интересно, правильно ли Java определяет эргономику?(Я начинаю сомневаться в этом)

Чтобы проверить свою теорию, я установил предел в 512 м и развернул приложение таким образом, и обнаружил, что с самого начала возникает необычная нагрузка на ЦП, которая должна быть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

И это снова приводит использование VCPU к разумному состоянию после того, как kubectl top pod

NAME                    CPU(cores)   MEMORY(bytes)   
test-84f4c7445f-kzvd5   13m          305Mi 

Беспорядок с Xmx с MaxRAM, очевидно, влияет на JVM, но как невозможно контролировать количествопамять у нас на виртуализированных контейнерах?Я знаю, что команда free сообщит о доступной оперативной памяти хоста, но OpenJDK должен использовать cgroups rihgt?.

Я все еще наблюдаю за памятью ...


РЕДАКТИРОВАТЬ: новая надежда

Я сделал две вещи, во-первых, чтобы снова удалить мой предел контейнера, я хочу проанализировать, насколько он будет расти, также я добавил новый флаг, чтобы увидеть, какпроцесс использует собственную память -XX:NativeMemoryTracking=summary

В начале все было нормально, процесс начал потреблять, как 300 МБ, через kubectl top pod, поэтому я позволил ему работать около 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 Гб зарезервировано для потоков?и 222 МБ используется, что это?В настоящее время я не знаю, я просто видел это ...

Мне нужно время, чтобы понять, почему это происходит

1 Ответ

0 голосов
/ 03 марта 2019

Я наконец нашел свою проблему и хочу поделиться ею, чтобы другие могли извлечь из этого пользу.

Как я обнаружил в своем последнем редактировании, у меня возникла проблема с потоком, которая приводила к тому, что все потребление памяти превышаловремя, в частности, мы использовали асинхронный метод из сторонней библиотеки без должной заботы об этих ресурсах (в этом случае убедитесь, что эти вызовы заканчиваются правильно).

Мне удалось обнаружить проблему, потому что я использовал памятьограничение на мое развертывание в kubernete с самого начала (что является хорошей практикой в ​​производственных средах), а затем я очень внимательно следил за потреблением памяти моего приложения, используя такие инструменты, как jstat, jcmd, visualvm, kill -3 и, что наиболее важно, флаг -XX:NativeMemoryTracking=summary, который дал мне так много деталей в этомотношение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...