Estoy trabajando con OpenJDK 11 y una aplicación SpringBoot muy simple que casi lo único que tiene es el actuador SpringBoot habilitado para que pueda llamar / actuador / de la salud , etc.
También tengo un clúster kubernetes en GCE muy simple con sólo una vaina con un recipiente (que contiene esta aplicación por supuesto)
Mi configuración tiene algunos puntos clave que quiero destacar, que tiene algunos requisitos y límites
resources:
limits:
memory: 600Mi
requests:
memory: 128Mi
Y tiene una sonda disposición
readinessProbe:
initialDelaySeconds: 30
periodSeconds: 30
httpGet:
path: /actuator/health
port: 8080
También estoy estableciendo un JVM_OPTS como (que mi programa está utilizando obviamente)
env:
- name: JVM_OPTS
value: "-XX:MaxRAM=512m"
El problema
Lanzo esto y se pone OOMKilled en aproximadamente 3 horas cada vez!
Nunca estoy llamando nada a mí mismo la única llamada es sondear la disposición cada 30 segundos que kubernetes hace, y eso es suficiente para agotar la memoria? Tampoco he implementado nada fuera de lo, sólo un método Get ordinaria que dice hola mundo a lo largo de todas las importaciones SpringBoot a tener los actuadores
Si me quedo vaina superior kubectl XXXXXX En realidad veo cómo llegar gradualmente más y más grande
He probado un montón de diferentes configuraciones, consejos, etc, pero nada parece funcionar con una aplicación básica SpringBoot
¿Hay una manera de limitar realmente difícil la memoria de una manera que Java puede plantear una excepción OutOfMemory? o para evitar que esto suceda?
Gracias por adelantado
EDIT: Después de 15 horas de funcionamiento
NAME READY STATUS RESTARTS AGE
pod/test-79fd5c5b59-56654 1/1 Running 4 15h
describir la vaina dice ...
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
Ese último lapso de tiempo es de aproximadamente 4 horas y sólo tienen 483 llamadas a / actuador / de la salud, al parecer eso fue suficiente para hacer java superar la indirecta MaxRAM?
EDIT: Casi 17h
su punto de morir de nuevo
$ kubectl top pod test-79fd5c5b59-56654
NAME CPU(cores) MEMORY(bytes)
test-79fd5c5b59-56654 43m 575Mi
EDIT: perder ninguna esperanza a las 23h
NAME READY STATUS RESTARTS AGE
pod/test-79fd5c5b59-56654 1/1 Running 6 23h
describir las vainas;
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
EDIT: Un nuevo hallazgo
Ayer por la noche que estaba haciendo una lectura interesante:
https://developers.redhat.com/blog/2017/03/14/java-inside-docker/ https://banzaicloud.com/blog/java10-container-sizing/ https://medium.com/adorsys/ -memoria-Valores de JVM-in-a-envase-entorno-64b0840e1d9e
TL; DR decidí quitar el límite de memoria e iniciar el proceso de nuevo, el resultado fue bastante interesante (después de 11 horas como en ejecución)
NAME CPU(cores) MEMORY(bytes)
test-84ff9d9bd9-77xmh 218m 1122Mi
Así que ... WTH con la CPU? Yo como esperando un número grande en el uso de memoria, pero lo que sucede con la CPU?
Lo único que se me ocurre es que la GC se ejecuta como una locura pensar que el MaxRAM es 512m y él está utilizando más de 1G. Me pregunto, se detecta la ergonomía de Java correctamente? (Estoy empezando a dudar de ella)
Para probar mi teoría se establece un límite de 512m y desplegar la aplicación de esta manera y me encontré con que desde el principio hay una carga de la CPU inusual que tiene que ser la GC funcionando con mucha frecuencia
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 es demasiada CPU virtual, por lo que mi siguiente paso lógico era asegurarse de que estoy usando el GC más apropiado para este caso, he cambiado los JVM_OPTS de esta manera:
env:
- name: JVM_OPTS
value: "-XX:MaxRAM=512m -Xmx128m -XX:+UseSerialGC"
...
resources:
requests:
memory: 256Mi
cpu: 0.15
limits:
memory: 700Mi
Y eso es llevar el uso de CPU virtual a un estado razonable de nuevo, después de kubectl top pod
NAME CPU(cores) MEMORY(bytes)
test-84f4c7445f-kzvd5 13m 305Mi
Messing con -Xmx tener MaxRAM es, obviamente, afectando la JVM pero ¿no es posible controlar la cantidad de memoria que tenemos en contenedores virtualizados? Sé que free
comando reportará el anfitrión RAM disponible, pero debe ser OpenJDK usando cgroups rihgt ?.
Todavía estoy supervisión de la memoria ...
EDIT: Una nueva esperanza
Hice dos cosas, la primera fue de nuevo para eliminar el límite de mi contenedor, quiero analizar cuánto va a crecer, también he añadido una nueva bandera para ver cómo el proceso está utilizando la memoria nativa -XX:NativeMemoryTracking=summary
Al principio todo era normal, el proceso comenzó a consumir como 300 MB a través kubectl top pod
, así que dejamos correr alrededor de 4 horas y luego ...
kubectl top pod
NAME CPU(cores) MEMORY(bytes)
test-646864bc48-69wm2 54m 645Mi
tipo de espera, ¿verdad? pero luego he comprobado el uso de memoria nativa
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)
Esperar lo ? 2,1 GB reservado para hilos? y 222 MB están utilizando, ¿qué es esto? Yo actualmente no sé, acabo de ver que ...
Necesito tiempo tratando de entender por qué ocurre esto
Finalmente encontré mi problema y quiero compartirlo para que otros puedan beneficiarse de alguna manera de esto.
Como ya he encontrado en mi última edición que tenía un problema hilo que estaba causando todo el consumo de memoria en el tiempo, en concreto que estaba usando un método asíncrono de una biblioteca de terceros sin cuidar adecuadamente esos recursos (asegurar esas llamadas se terminan correctamente en este caso ).
Yo era capaz de detectar el problema porque he usado un límite de memoria en mi despliegue kubernete desde el principio (que es una buena práctica en entornos de producción) y luego objeto de riguroso control de mi consumo de memoria de aplicaciones utilizando herramientas como jstat, jcmd, visualvm, kill -3
y lo más importante la -XX:NativeMemoryTracking=summary
bandera que dio me tanto detalle en este sentido.