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ón | Descripció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-swappiness | Por 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-reservation | Te 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-disable | Por 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-swapse configura con un entero positivo, entonces deben establecerse tanto--memorycomo--memory-swap.--memory-swaprepresenta la cantidad total de memoria y swap que se puede utilizar, y--memorycontrola 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-swapse configura en0, la configuración se ignora y el valor se trata como no establecido.Si
--memory-swapse configura con el mismo valor que--memory, y--memoryestá configurada con un entero positivo, el contenedor no tendrá acceso al swap. Consulta Evitar que un contenedor utilice swap.Si
--memory-swapno está configurada y--memorysí 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-swapno está establecida, el contenedor puede utilizar 600m en total de memoria y swap.Si
--memory-swapse 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
freereportan el swap disponible en el host, no el disponible dentro del contenedor. No confíes en la salida defreeo 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ón | Descripció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-cpus | Limita 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-shares | Configura 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.
WarningLa 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ón | Descripción |
|---|---|
--cap-add=sys_nice | Otorga 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.