OOMkiller en Docker es m谩s dif铆cil de lo que piensas

Hola de nuevo. En previsi贸n del inicio del curso, "Java Developer" prepar贸 una traducci贸n de otro peque帽o material.




Recientemente, uno de los usuarios de Plumbr APM tuvo un extra帽o problema con una parada de emergencia del contenedor acoplable con c贸digo 137. La configuraci贸n era simple con varios contenedores anidados y m谩quinas virtuales, similar a una mu帽eca anidada:

  • propio servidor de hierro con Ubuntu;
  • muchos contenedores acoplables con Ubuntu adentro;
  • M谩quina virtual Java dentro de contenedores acoplables.

Durante la investigaci贸n del problema, encontramos documentaci贸n de Docker sobre este tema. Qued贸 claro que la raz贸n era una parada manual del contenedor, o una falta de memoria y la posterior intervenci贸n de oomkiller (asesino sin memoria).

Observamos syslog y vemos que, de hecho, se llam贸 oomkiller :

[138805.608851] java invoked oom-killer: gfp_mask=0xd0, order=0, oom_score_adj=0
[138805.608887] [<ffffffff8116d20e>] oom_kill_process+0x24e/0x3b0
[138805.608916] Task in /docker/264b771811d88f4dbd3249a54261f224a69ebffed6c7f76c7aa3bc83b3aaaf71 killed as a result of limit of /docker/264b771811d88f4dbd3249a54261f224a69ebffed6c7f76c7aa3bc83b3aaaf71
[138805.608902] [<ffffffff8116da84>] pagefault_out_of_memory+0x14/0x90
[138805.608918] memory: usage 3140120kB, limit 3145728kB, failcnt 616038
[138805.608940] memory+swap: usage 6291456kB, limit 6291456kB, failcnt 2837
[138805.609043] Memory cgroup out of memory: Kill process 20611 (java) score 1068 or sacrifice child

Como puede ver, el proceso de Java alcanz贸 un l铆mite de 3145728 KB (aproximadamente 3 GB), lo que provoc贸 que el contenedor se detuviera. Esto es bastante extra帽o, ya que Docker se lanz贸 con un l铆mite de 4 GB (en el archivo docker-compose).

Probablemente sepa que la JVM tambi茅n impone sus limitaciones en el uso de la memoria. Aunque el docker se estableci贸 en un l铆mite de 4 GB, JVM se lanz贸 con la opci贸n Xmx=3GB. Esto puede ser a煤n m谩s confuso, pero tenga en cuenta que la JVM puede usar m谩s memoria de lo que -Xmx indic贸 (consulte el art铆culo sobre el an谩lisis del uso de memoria en la JVM ).

Hasta que entendamos lo que est谩 sucediendo. Docker deber铆a permitir el uso de 4 GB. Entonces, 驴por qu茅 OOMkiller trabaj贸 en 3 GB? Una b煤squeda adicional de informaci贸n nos llev贸 al hecho de que hay otra limitaci贸n de memoria en el sistema operativo, que se implement贸 en el hardware.

Decir gracias a cgroups (grupos de control). cgroups es el motor del kernel de Linux para restringir, controlar y contabilizar el uso de recursos por parte de los grupos de procesos. En comparaci贸n con otras soluciones (equipo niceo /etc/security/limits.conf), cgroups ofrece m谩s flexibilidad ya que pueden trabajar con (debajo) conjuntos de procesos.

En nuestra situaci贸n, cgroups limit贸 el uso de memoria a 3 GB (v铆a memory.limit_in_bytes). 隆Tenemos algunos progresos!

Un estudio de la memoria y los eventos de GC con Plumbr mostr贸 que la mayor铆a de las veces la JVM usaba alrededor de 700 MB. La excepci贸n fue solo inmediatamente antes de la parada, cuando hubo un aumento en la asignaci贸n de memoria. Fue seguido por una larga pausa de GC. Entonces parece que sucedi贸 lo siguiente:

  • El c贸digo Java que se ejecuta dentro de la JVM est谩 tratando de obtener mucha memoria.
  • La JVM, despu茅s de verificar que el l铆mite Xmx de 3 GB a煤n est谩 lejos, solicita asignar memoria para el sistema operativo.
  • Docker tambi茅n comprueba y ve que tampoco se ha alcanzado su l铆mite de 4 GB.
  • El n煤cleo del sistema operativo comprueba el l铆mite de cgroup de 3 GB y elimina el contenedor.
  • La JVM se detiene con el contenedor antes de que pueda procesar el suyo OutOfMemoryError.

Entendiendo esto, hemos configurado todas las limitaciones de 2.5 GB para docker y 1.5 para java. Despu茅s de eso, la JVM podr铆a manejar OutOfMemoryErrory lanzar una excepci贸n OutOfMemoryError. Esto permiti贸 a Plumbr hacer su magia: obtener una instant谩nea de la memoria con los volcados correspondientes de la pila y mostrar que hab铆a una consulta en la base de datos, que en cierta situaci贸n intent贸 cargar casi toda la base de datos.

recomendaciones


Incluso en una situaci贸n tan simple, hab铆a tres limitaciones de memoria:

  • JVM mediante par谩metro -Xmx
  • Docker a trav茅s de las opciones en el archivo docker-compose
  • Sistema operativo a trav茅s de par谩metros memory.limit_in_bytes cgroups

Por lo tanto, cuando encuentre un asesino OOM, debe prestar atenci贸n a todas las limitaciones de memoria involucradas.

Otra conclusi贸n para los desarrolladores de docker. Parece que no tiene sentido permitir que tales "mu帽ecas anidadas" se ejecuten en las cuales el l铆mite de memoria del contenedor incluido es m谩s alto que el l铆mite de cgroups. Una simple comprobaci贸n de esto cuando se inicia el contenedor con la advertencia adecuada puede ahorrar cientos de horas de depuraci贸n para sus usuarios.

Eso es todo. Te esperamos en el curso .

All Articles