El mes pasado hablé en ApacheCon sobre Cgroups. Parece que muy pocos usuarios de Linux (incluyéndome a mí hasta no hace mucho) están familiarizados con Cgroups y su poder. Esto es una pena, porque Cgroups son muy potentes, y te permiten asignar recursos en tus servidores de una manera mucho más granular que cualquier otra herramienta disponible en el conjunto de herramientas de Linux. Además, está integrado directamente en el kernel y viene listo para usar con la mayoría de las distribuciones de Linux.
En este artículo explicaré cómo usar Cgroups con Docker como una especie de continuación de otra charla que di en el Docker Meetup en Austin, Texas.
Este artículo requiere cierta comprensión básica de Cgroups. Si eres completamente nuevo en Cgroups, te recomiendo que eches un vistazo a mi presentación de diapositivas de ApacheCon, así como a los recursos al final. Pero no te preocupes, no profundizaremos demasiado. Si solo echas un vistazo rápido a la presentación, deberías ser capaz de comprender los conceptos básicos.
Docker y Cgroups
Docker viene con dos controladores diferentes: LXC y libcontainer. El controlador LXC es el controlador heredado, y libcontainer es el controlador nuevo y predeterminado. En la mayoría de los casos, libcontainer es el controlador preferido, ya que ahí es donde ocurre la innovación (por ejemplo, docker exec no funciona con el controlador LXC).
Sin embargo, es importante tener en cuenta que hay dos controladores diferentes, ya que no todas las capacidades de Cgroup se han portado a libcontainer todavía (o al menos no se han expuesto a Docker). Al usar el controlador LXC, simplemente pasas los argumentos de LXC directamente, mientras que con libcontainer hay argumentos de política de Cgroup explícitos expuestos a Docker. Tendrás que configurar explícitamente un controlador cuando inicies el demonio de Docker, por lo que no podrás ejecutar los dos controladores simultáneamente.
Aquí tienes un ejemplo de cómo puedes comprobarlo:
[bash light=”true”] # Con el controlador LXC$ docker run -d –name=’lxc_test’ \
–lxc-conf="lxc.cgroup.cpu.shares=50" \
busybox
# Con el controlador libcontainer
$ docker run -d –name=’libcontainer_test’ \
–cpu-shares=50 \
busybox
[/bash]
Aquí tienes una descripción general de algunas de las características de Cgroup y cómo se mapean entre los dos controladores:
| Característica | Libcontainer | LXC |
|---|---|---|
| Uso compartido relativo de CPU | -c, –cpu-shares | –lxc-conf=”lxc.cgroup.cpu.shares” |
| Bloquear a un núcleo de CPU | –cpuset-cpus | –lxc-conf=”lxc.cgroup.cpuset.cpus” |
| Limitar memoria | -m, –memory | –lxc-conf=”lxc.cgroup.cpuset.mems” |
LXC
Si deseas utilizar el controlador LXC para Docker, primero deberás habilitarlo. El método para hacer esto variará según tu distribución de Linux, pero aquí tienes las instrucciones sobre cómo habilitar el controlador LXC en Ubuntu 14.04.
Como se mencionó anteriormente, estás renunciando a una serie de características al hacer esto. Por lo tanto, a menos que realmente necesites una funcionalidad que aún no esté expuesta en Docker usando libcontainer, realmente deberías quedarte con el controlador predeterminado.
Algunas políticas útiles de Cgroup que aún no se han expuesto en libcontainer incluyen la limitación de E/S (tratada en la presentación), que puede ser muy útil para ciertas aplicaciones.
Si has decidido utilizar el controlador LXC, agregar argumentos es muy sencillo. Todo lo que necesitas hacer es agregar el argumento --lxc-conf y pasar la política de Cgroup que deseas configurar.
Libcontainer
Como puedes ver en la tabla anterior, las políticas básicas de Cgroup ya están expuestas en la versión actual de Docker (1.6 al momento de escribir esto).
Usar estas políticas es muy sencillo. Si, por ejemplo, deseas bloquear un contenedor Docker al primer núcleo de CPU, añadirías --cpuset-cpus=0 a tu comando docker run.
También puedes usar el argumento --cgroup-parent con libcontainer y configurar manualmente restricciones de recursos más granulares. Luego lo mapearías a ese grupo usando dicho argumento.
Demostración: Docker con Cgroups
En el screencast de abajo, utilizaremos dos contenedores Docker (‘low_prio’ y ‘high_prio’). Usamos el contenedor base ‘busybox’ y ejecutamos md5sum /dev/urandom para simular un proceso que consume mucha CPU. Por defecto, esto consumiría todos los recursos de CPU disponibles. Sin embargo, aplicaremos dos políticas de Cgroup para administrar los recursos. Primero usamos ‘cpuset.cpus’ para bloquear los contenedores en el mismo núcleo de CPU (núcleo 0).
A continuación, usamos ‘cpu.shares’ para asignar una cuota de CPU relativa. Le damos al contenedor ‘low_prio’ un valor de 20 y a ‘high_prio’ un valor de 80. Esto significa que el 20% de la CPU se asignará al contenedor ‘low_prio’, y el 80% de la CPU se asignará al contenedor ‘high_prio’. Tenga en cuenta, sin embargo, que la cuota relativa es una escala arbitraria (también podríamos haber usado 2 y 8 como valores).
Después de haber demostrado que la administración de recursos realmente funciona, lanzamos el mismo conjunto de contenedores sin ninguna política de Cgroup para ver cómo se comportan.
Como referencia, aquí están los comandos utilizados para lanzar los contenedores.
[bash light=”true”] $ docker run -d \–name=’low_prio’ \
–cpuset-cpus=0 \
–cpu-shares=20 \
busybox md5sum /dev/urandom
$ docker run -d \
–name=’high_prio’ \
–cpuset-cpus=0 \
–cpu-shares=80 \
busybox md5sum /dev/urandom
[/bash]
Resumen
Si está administrando múltiples contenedores Docker en el mismo host, usar Cgroups para administrar los recursos entre los contenedores tiene mucho sentido. Por ejemplo, tal vez tenga algunas tareas de procesamiento en segundo plano ejecutándose en un contenedor y otro contenedor que sirve contenido al usuario. En ese caso, puede usar sus nuevos conocimientos para asegurarse de priorizar los contenedores orientados al usuario antes que las tareas en segundo plano.
Comentarios
Aún no hay comentarios. Sea el primero.