Compartir comentarios
Las respuestas se generan en base a la documentación.

Restricciones de recursos

Por defecto, un contenedor no tiene restricciones de recursos y puede utilizar tanto de un recurso dado como lo permita el programador del kernel del host. Docker proporciona formas de controlar cuánta memoria o CPU puede utilizar un contenedor, configurando banderas en tiempo de ejecución del comando docker run. Esta sección detalla cuándo debes configurar dichos límites y las posibles implicaciones de establecerlos.

Muchas de estas características requieren que tu kernel admita capacidades de Linux. Para comprobar si son compatibles, puedes utilizar el comando docker info. Si una capacidad está deshabilitada en tu kernel, es posible que veas una advertencia al final de la salida como la siguiente:

WARNING: No swap limit support

Consulta la documentación de tu sistema operativo para habilitarlas. Consulta también la guía de resolución de problemas de Docker Engine para obtener más información.

Memoria

Comprender los riesgos de quedarse sin memoria

Es importante no permitir que un contenedor en ejecución consuma demasiada memoria de la máquina host. En hosts Linux, si el kernel detecta que no hay suficiente memoria para realizar funciones importantes del sistema, lanza una excepción OOME o Out Of Memory Exception y comienza a finalizar procesos para liberar memoria. Cualquier proceso está sujeto a ser finalizado, incluyendo Docker y otras aplicaciones importantes. Esto puede provocar la caída de todo el sistema si se finaliza el proceso equivocado.

Docker intenta mitigar estos riesgos ajustando la prioridad OOM en el demonio de Docker para que sea menos probable que sea finalizado en comparación con otros procesos del sistema. La prioridad OOM en los contenedores no se ajusta. Esto hace que sea más probable que se finalice un contenedor individual antes que el demonio de Docker u otros procesos del sistema. No debes intentar eludir estas salvaguardas configurando manualmente --oom-score-adj a un número negativo extremo en el demonio o en un contenedor, ni configurando --oom-kill-disable en un contenedor.

Para obtener más información sobre la gestión de OOM del kernel de Linux, consulta Out of Memory Management.

Puedes mitigar el riesgo de inestabilidad del sistema debido a OOME mediante:

  • Realizar pruebas para comprender los requisitos de memoria de tu aplicación antes de llevarla a producción.
  • Asegurarte de que tu aplicación se ejecute únicamente en hosts con recursos adecuados.
  • Limitar la cantidad de memoria que puede utilizar tu contenedor, como se describe a continuación.
  • Prestar atención al configurar el espacio de intercambio (swap) en tus hosts Docker. El swap es más lento que la memoria física, pero puede proporcionar un colchón contra la falta de memoria del sistema.
  • Considerar la conversión de tu contenedor en un servicio, y utilizar restricciones a nivel de servicio y etiquetas de nodo para asegurar que la aplicación se ejecute solo en hosts con suficiente memoria.

Limitar el acceso a la memoria de un contenedor

Docker puede aplicar límites de memoria estrictos (hard) o flexibles (soft).

  • Los límites estrictos impiden que el contenedor utilice más de una cantidad fija de memoria.
  • Los límites flexibles permiten al contenedor utilizar tanta memoria como necesite a menos que se cumplan ciertas condiciones, como cuando el kernel detecta poca memoria o contención en la máquina host.

Algunas de estas opciones tienen efectos diferentes cuando se usan solas o cuando se configura más de una opción.

La mayoría de estas opciones aceptan un entero positivo, seguido de un sufijo b, k, m, g, para indicar bytes, kilobytes, megabytes o gigabytes.

OpciónDescripción
-m o --memory=La cantidad máxima de memoria que el contenedor puede utilizar. Si configuras esta opción, el valor mínimo permitido es 6m (6 megabytes). Es decir, debes establecer el valor en al menos 6 megabytes.
--memory-swap*La cantidad de memoria que este contenedor tiene permitido intercambiar a disco. Consulta detalles de --memory-swap.
--memory-swappinessPor defecto, el kernel del host puede intercambiar un porcentaje de páginas anónimas utilizadas por un contenedor. Puedes configurar --memory-swappiness a un valor entre 0 y 100 para ajustar este porcentaje. Consulta detalles de --memory-swappiness.
--memory-reservationTe permite especificar un límite flexible menor que --memory, el cual se activa cuando Docker detecta contención o poca memoria en la máquina host. Si utilizas --memory-reservation, debe configurarse a un valor inferior a --memory para que tenga precedencia. Al ser un límite flexible, no garantiza que el contenedor no supere el límite.
--oom-kill-disablePor defecto, si ocurre un error de falta de memoria (OOM), el kernel finaliza los procesos dentro del contenedor. Para cambiar este comportamiento, utiliza la opción --oom-kill-disable. Solo deshabilita el OOM killer en contenedores donde también hayas configurado la opción -m/--memory. Si la bandera -m no está configurada, el host puede quedarse sin memoria y el kernel podría necesitar finalizar procesos del sistema host para liberar memoria.

Para obtener más información sobre cgroups y la memoria en general, consulta la documentación de Memory Resource Controller.

Detalles de --memory-swap

--memory-swap es una bandera modificadora que solo tiene sentido si también está configurada --memory. El uso de swap permite al contenedor escribir los requisitos de memoria excedentes en el disco cuando el contenedor ha agotado toda la memoria RAM disponible para él. Existe una penalización en el rendimiento para las aplicaciones que intercambian memoria a disco con frecuencia.

Su configuración puede tener efectos complejos:

  • Si --memory-swap se configura con un entero positivo, entonces deben establecerse tanto --memory como --memory-swap. --memory-swap representa la cantidad total de memoria y swap que se puede utilizar, y --memory controla la cantidad utilizada por la memoria física (sin swap). Por lo tanto, si --memory="300m" y --memory-swap="1g", el contenedor puede utilizar 300m de memoria y 700m (1g - 300m) de swap.

  • Si --memory-swap se configura en 0, la configuración se ignora y el valor se trata como no establecido.

  • Si --memory-swap se configura con el mismo valor que --memory, y --memory está configurada con un entero positivo, el contenedor no tendrá acceso al swap. Consulta Evitar que un contenedor utilice swap.

  • Si --memory-swap no está configurada y --memory sí lo está, el contenedor puede utilizar tanto swap como la configuración de --memory, siempre que el contenedor host tenga memoria swap configurada. Por ejemplo, si --memory="300m" y --memory-swap no está establecida, el contenedor puede utilizar 600m en total de memoria y swap.

  • Si --memory-swap se configura explícitamente en -1, el contenedor tiene permitido utilizar swap ilimitado, hasta la cantidad disponible en el sistema host.

  • Dentro del contenedor, herramientas como free reportan el swap disponible en el host, no el disponible dentro del contenedor. No confíes en la salida de free o herramientas similares para determinar si el swap está presente.

Evitar que un contenedor utilice swap

Si --memory y --memory-swap se configuran con el mismo valor, esto evita que los contenedores utilicen swap. Esto se debe a que --memory-swap es la cantidad combinada de memoria y swap que se puede utilizar, mientras que --memory es solo la cantidad de memoria física que se puede usar.

Detalles de --memory-swappiness

  • Un valor de 0 desactiva el intercambio de páginas anónimas.
  • Un valor de 100 establece todas las páginas anónimas como intercambiables.
  • Por defecto, si no configuras --memory-swappiness, el valor se hereda de la máquina host.

CPU

Por defecto, el acceso de cada contenedor a los ciclos de CPU de la máquina host es ilimitado. Puedes establecer varias restricciones para limitar el acceso de un contenedor dado a los ciclos de CPU de la máquina host. La mayoría de los usuarios utilizan y configuran el programador CFS por defecto. También puedes configurar el programador en tiempo real.

Configurar el programador CFS por defecto

El CFS es el programador de CPU del kernel de Linux para procesos normales de Linux. Varias banderas en tiempo de ejecución te permiten configurar la cantidad de acceso a los recursos de CPU que tiene tu contenedor. Cuando utilizas estas configuraciones, Docker modifica los ajustes para el cgroup del contenedor en la máquina host.

OpciónDescripción
--cpus=<value>Especifica qué cantidad de los recursos de CPU disponibles puede utilizar un contenedor. Por ejemplo, si la máquina host tiene dos CPUs y configuras --cpus="1.5", se garantiza al contenedor como máximo una CPU y media. Esto equivale a configurar --cpu-period="100000" y --cpu-quota="150000".
--cpu-period=<value>Especifica el período del programador CFS de CPU, que se utiliza junto con --cpu-quota. El valor predeterminado es 100000 microsegundos (100 milisegundos). La mayoría de los usuarios no cambian este valor. Para la mayoría de los casos de uso, --cpus es una alternativa más conveniente.
--cpu-quota=<value>Impone una cuota de CPU CFS al contenedor. El número de microsegundos por --cpu-period al que se limita el contenedor antes de ser restringido. De este modo actúa como el límite máximo efectivo. Para la mayoría de los casos de uso, --cpus es una alternativa más conveniente.
--cpuset-cpusLimita las CPUs o núcleos específicos que puede utilizar un contenedor. Una lista separada por comas o un rango separado por guiones de las CPUs que puede utilizar un contenedor, si dispones de más de una CPU. La primera CPU se numera como 0. Un valor válido podría ser 0-3 (para usar la primera, segunda, tercera y cuarta CPU) o 1,3 (para usar la segunda y cuarta CPU).
--cpu-sharesConfigura esta bandera con un valor superior o inferior al valor predeterminado de 1024 para aumentar o reducir el peso del contenedor y darle acceso a una proporción mayor o menor de los ciclos de CPU de la máquina host. Esto solo se aplica cuando los ciclos de CPU están limitados. Cuando hay suficientes ciclos de CPU disponibles, todos los contenedores utilizan tanta CPU como necesiten. De este modo, se trata de un límite flexible. --cpu-shares no impide que los contenedores se programen en modo Swarm. Prioriza los recursos de CPU del contenedor para los ciclos de CPU disponibles. No garantiza ni reserva ningún acceso de CPU específico.

Si tienes 1 CPU, cada uno de los siguientes comandos garantiza al contenedor como máximo el 50% de la CPU cada segundo.

$ docker run -it --cpus=".5" ubuntu /bin/bash

Lo cual equivale a especificar manualmente --cpu-period y --cpu-quota:

$ docker run -it --cpu-period=100000 --cpu-quota=50000 ubuntu /bin/bash

Configurar el programador en tiempo real

Puedes configurar tu contenedor para utilizar el programador en tiempo real para tareas que no pueden utilizar el programador CFS. Debes asegurarte de que el kernel de la máquina host esté configurado correctamente antes de poder configurar el demonio de Docker o configurar contenedores individuales.

Warning

La programación y priorización de la CPU son características avanzadas a nivel de kernel. La mayoría de los usuarios no necesitan cambiar estos valores de sus valores predeterminados. Configurar estos valores de forma incorrecta puede provocar que tu sistema host se vuelva inestable o inutilizable.

Configurar el kernel de la máquina host

Verifica que CONFIG_RT_GROUP_SCHED esté habilitado en el kernel de Linux ejecutando zcat /proc/config.gz | grep CONFIG_RT_GROUP_SCHED o comprobando la existencia del archivo /sys/fs/cgroup/cpu.rt_runtime_us. Para obtener orientación sobre cómo configurar el programador en tiempo real del kernel, consulta la documentación de tu sistema operativo.

Configurar el demonio de Docker

Para ejecutar contenedores utilizando el programador en tiempo real, ejecuta el demonio de Docker con la bandera --cpu-rt-runtime establecida en el número máximo de microsegundos reservados para tareas en tiempo real por período de tiempo de ejecución. Por ejemplo, con el período predeterminado de 1000000 microsegundos (1 segundo), establecer --cpu-rt-runtime=950000 garantiza que los contenedores que utilizan el programador en tiempo real puedan ejecutarse durante 950000 microsegundos por cada período de 1000000 de microsegundos, dejando al menos 50000 microsegundos disponibles para tareas que no son en tiempo real. Para hacer esta configuración permanente en sistemas que utilizan systemd, crea un archivo de unidad de systemd para el servicio docker. Por ejemplo, consulta las instrucciones sobre cómo configurar el demonio para usar un proxy con un archivo de unidad de systemd.

Configurar contenedores individuales

Puedes pasar varias banderas para controlar la prioridad de CPU de un contenedor cuando inicias el contenedor utilizando docker run. Consulta la documentación de tu sistema operativo o el comando ulimit para obtener información sobre los valores adecuados.

OpciónDescripción
--cap-add=sys_niceOtorga al contenedor la capacidad CAP_SYS_NICE, lo que le permite aumentar los valores nice del proceso, establecer políticas de programación en tiempo real, establecer la afinidad de la CPU y otras operaciones.
--cpu-rt-runtime=<value>El número máximo de microsegundos que el contenedor puede ejecutarse con prioridad en tiempo real dentro del período del programador en tiempo real del demonio de Docker. También necesitas la bandera --cap-add=sys_nice.
--ulimit rtprio=<value>La prioridad máxima en tiempo real permitida para el contenedor. También necesitas la bandera --cap-add=sys_nice.

El siguiente comando de ejemplo establece cada una de estas tres banderas en un contenedor debian:jessie.

$ docker run -it \
    --cpu-rt-runtime=950000 \
    --ulimit rtprio=99 \
    --cap-add=sys_nice \
    debian:jessie

Si el kernel o el demonio de Docker no están configurados correctamente, se produce un error.

GPU

Para obtener información sobre cómo acceder a las GPUs de NVIDIA desde un contenedor, consulta Acceso a la GPU.