Introducción

En Capítulo 3, Trabajando con imágenes de Docker , vimos cómo se pueden utilizar los Dockerfiles para crear imágenes que consisten en diferentes servicios/software. Más tarde, en Capítulo 4, Administración de redes y datos para contenedores , vimos cómo un contenedor de Docker puede comunicarse con el mundo exterior en cuanto a datos y redes. En Capítulo 5, Casos de uso de Docker , exploramos diferentes casos de uso de Docker, y en el Capítulo 6, APIs y SDKs de Docker , vimos cómo utilizar las APIs remotas para conectarnos al host de Docker remoto.

La facilidad de uso es muy buena, pero antes de ir a producción, el rendimiento es uno de los aspectos clave que se consideran. En este capítulo, veremos las características de Docker que impactan el rendimiento y qué enfoque podemos seguir para evaluar el rendimiento de diferentes subsistemas. Al realizar la evaluación del rendimiento, debemos comparar el rendimiento de Docker con lo siguiente:

  • Hardware sin procesar

  • Máquina virtual

  • Docker ejecutándose dentro de una máquina virtual

En este capítulo, solo veremos un enfoque que podemos seguir para realizar la evaluación del rendimiento, en lugar de números de rendimiento recopilados de ejecuciones para realizar una comparación. Sin embargo, señalaré las comparaciones de rendimiento realizadas por diferentes empresas, a las que puedes referirte.

Veamos primero algunas de las características de Docker que impactan el rendimiento:

  • Volúmenes : Al implementar cualquier carga de trabajo de clase empresarial, te gustaría ajustar el almacenamiento subyacente en consecuencia. No debes utilizar el sistema de archivos principal utilizado por los contenedores para almacenar datos. Docker proporciona la capacidad de adjuntar/montar almacenamiento externo a través de volúmenes. Como vimos en Capítulo 4, Administración de redes y datos para contenedores , hay dos tipos de volúmenes:

  • Volúmenes que se montan a través de la máquina host utilizando la opción -volume

  • Volúmenes que se montan a través de otro contenedor utilizando la opción -volumes-from

  • Controladores de almacenamiento : Vimos diferentes controladores de almacenamiento en el Capítulo 1, Introducción e instalación , que son vfs, aufs, btrfs, zfs, devicemapper y overlayFS. Puedes verificar los controladores de almacenamiento admitidos actualmente y su prioridad de selección si no se elige nada en el momento de inicio de Docker en https://github.com/moby/moby/blob/master/daemon/graphdriver/driver_linux.go.

Si estás ejecutando Fedora, CentOS o RHEL, entonces el device mapper será el controlador de almacenamiento predeterminado. Puedes encontrar algunas opciones de ajuste específicas del device mapper en https://github.com/moby/moby/tree/master/daemon/graphdriver/devmapper.

Puedes cambiar el controlador de almacenamiento predeterminado con la opción -s del demonio de Docker. Puedes actualizar el archivo de configuración específico de la distribución/sistema para realizar cambios para el reinicio del servicio. Para Fedora/RHEL/CentOS, tendrás el campo storage-driver en /etc/docker/daemon.json – algo como lo siguiente para que puedas utilizar el backend btrfs:

"storage-driver": "btrfs"

El siguiente gráfico te muestra cuánto tiempo se tarda en iniciar y detener 1.000 contenedores con diferentes configuraciones de controlador de almacenamiento:

Diagrama

Como puedes ver, overlayFS tiene un mejor rendimiento que otros controladores de almacenamiento.

  • --net=host : Como sabemos, por defecto, Docker crea un puente y asigna direcciones IP de él a los contenedores. Utilizar --net=host expone la pila de red del host al contenedor omitiendo la creación de un espacio de nombres de red para el contenedor. Según las pruebas, esta opción siempre ofrece un mejor rendimiento en comparación con la configuración de puente.

Esto nos da algunas limitaciones, como no poder tener dos contenedores o aplicaciones del host que escuchen en el mismo puerto.

  • cgroups : El controlador de ejecución predeterminado de Docker, libcontainer, expone diferentes perillas de cgroups que se pueden utilizar para ajustar el rendimiento del contenedor. Algunas de ellas son:

  • Acciones de CPU : Con esto, podemos dar un peso proporcional a los contenedores para que el recurso se comparta. Por defecto, todos los contenedores obtienen la misma proporción de ciclos de CPU. Para modificar la proporción del valor predeterminado de 1024, utiliza la bandera -c o --cpu-shares para establecer el peso en 2 o más. La proporción solo se aplicará cuando se ejecuten procesos intensivos de CPU. Cuando las tareas en un contenedor estén inactivas, otros contenedores pueden utilizar el tiempo de CPU restante. Considera el siguiente ejemplo:

$ docker container run -it -c 100 alpine ash
  • CPUSets : Utilizar CPUSets nos permite limitar el contenedor a ejecutarse solo en los núcleos de CPU seleccionados. Por ejemplo, el siguiente código solo ejecutará hilos dentro de un contenedor en el núcleo 0 y 3:
$ docker container run -it --cpuset=0,3 alpine ash
  • Límites de memoria : Podemos establecer límites de memoria para un contenedor. Por ejemplo, el siguiente comando limitará el uso de memoria a 512 MB para el contenedor:
$ docker container run -it -m 512M alpine ash
  • Configuraciones de sysctl y ulimit : En algunos casos, es posible que debas cambiar algunos de los valores de sysctl dependiendo del caso de uso para obtener un rendimiento óptimo, como cambiar el número de archivos abiertos. Puedes cambiar la configuración de ulimit con el siguiente comando:
$ docker container run -it --ulimit data=8192 alpine ash

El comando anterior sería una configuración específica del contenedor. También podemos establecer algunas de estas configuraciones a través del archivo de configuración del demonio de Docker, que será aplicable a todos los contenedores de forma predeterminada. Por ejemplo, al mirar el archivo de configuración de Docker, verás algo como lo siguiente en /etc/docker/daemon.json :

{
"default-ulimits": {
"nofile": "1048576",
"nproc": "1048576",
"core": "-1"
}
}

No dudes en cambiar los valores para que se adapten a tu caso de uso. Si estás utilizando Docker para Mac o Windows, cambiarías estos valores desde el menú de preferencias.

Puedes aprender sobre el rendimiento de Docker estudiando el trabajo realizado por otros. A continuación, se presentan algunos de los estudios relacionados con el rendimiento de Docker publicados por algunas empresas:

Para realizar la evaluación del rendimiento, debemos ejecutar una carga de trabajo similar en diferentes entornos (hardware sin procesar/VM/Docker) y luego recopilar los resultados con diferentes estadísticas de rendimiento. Para simplificar las cosas, podemos escribir scripts de benchmarking comunes que se pueden ejecutar en diferentes entornos. También podemos crear Dockerfiles para crear contenedores con scripts de generación de carga de trabajo. Por ejemplo, en el artículo Análisis de rendimiento de Docker en Red Hat Enterprise Linux, que se mencionó anteriormente (https://github.com/redhat-performance/docker-performance/blob/master/Dockerfiles/Dockerfile), el autor utilizó un Dockerfile para crear una imagen de CentOS y utilizó la variable de entorno del contenedor para seleccionar un entorno de Docker y no Docker para el script de benchmarking run-sysbench.sh .

De manera similar, Dockerfiles y scripts relacionados se publican por IBM y están disponibles en https://github.com/thewmf/kvm-docker-comparison.

Utilizaremos algunos de los Dockerfiles y scripts mencionados anteriormente en las recetas de este capítulo.