Cómo ejecutar múltiples servicios en un contenedor Docker

Publicado: 2022-06-30

Ilustración que muestra el logotipo de Docker

Docker es una tecnología para empaquetar componentes de su pila como contenedores aislados. Es una práctica común ejecutar cada uno de sus procesos en su propio contenedor, creando una clara división entre los componentes. Esto mejora la modularidad y le permite acceder a los beneficios de escalabilidad de la contenedorización.

Todavía puede haber situaciones en las que desee ejecutar múltiples servicios dentro de un solo contenedor. Si bien esto no es algo natural en el ecosistema de Docker, le mostraremos algunos enfoques diferentes que puede usar para crear contenedores con más de un proceso de larga duración.

Identificando el problema

Los contenedores Docker ejecutan un solo proceso en primer plano. Esto está definido por las instrucciones ENTRYPOINT y CMD de la imagen. ENTRYPOINT se establece dentro del Dockerfile de una imagen, mientras que CMD se puede anular al crear contenedores. Los contenedores se detienen automáticamente cuando sale su proceso de primer plano.

Puede iniciar otros procesos desde el CMD , pero el contenedor solo seguirá ejecutándose mientras el proceso original en primer plano esté activo. Mantener el contenedor en funcionamiento a lo largo de la vida útil combinada de dos servicios independientes no es posible directamente con el mecanismo ENTRYPOINT/CMD .

Envolviendo múltiples procesos en un punto de entrada

Los scripts de envoltura son la solución más simple al problema. Puede escribir un script que inicie todos sus procesos y espere a que finalicen. Establecer el script como su Docker ENTRYPOINT lo ejecutará como el proceso de primer plano del contenedor, manteniendo el contenedor en ejecución hasta que salga uno de los scripts envueltos.

 #!/bin/bash

/opt/primer proceso &

/opt/segundo proceso &

espera -n

salir $?

Este script inicia los archivos binarios /opt/first-process y /opt/second-process dentro del contenedor. El uso de & permite que el script continúe sin esperar a que finalice cada proceso. wait se utiliza para suspender el script hasta que finalice uno de los procesos. El script luego sale con el código de estado emitido por el script terminado.

Este modelo da como resultado que el contenedor ejecute tanto el first-process como second-process hasta que uno de ellos salga. En ese momento, el contenedor se detendrá, aunque el otro proceso aún se esté ejecutando.

Para usar este script, modifique el ENTRYPOINT y CMD de su imagen de Docker para convertirlo en el proceso de primer plano del contenedor:

 PUNTO DE ENTRADA ["/bin/sh"]
CMD ["./ruta/a/secuencia de comandos.sh"]

La opción de contenedor --init

Un desafío con la gestión de procesos de contenedores es la limpieza efectiva a medida que salen. Docker ejecuta su CMD como ID de proceso 1, lo que lo hace responsable de manejar señales y eliminar zombis. Si su secuencia de comandos no tiene estas capacidades, podría terminar con procesos secundarios huérfanos que persisten dentro de su contenedor.

El comando docker run tiene un indicador --init que modifica el punto de entrada para usar tini como PID 1. Esta es una implementación de proceso de inicio mínima que ejecuta su CMD , maneja el reenvío de señales y cosecha zombis continuamente.

Vale la pena usar --init si espera generar muchos procesos y no quiere manejar manualmente la limpieza. Tini es un sabor inicial ligero que está diseñado para contenedores. Es mucho más pequeño que las alternativas completas como systemd y upstart .

Uso de un administrador de procesos dedicado

Las secuencias de comandos manuales rápidamente se vuelven subóptimas cuando tiene muchos procesos que administrar. Adoptar un administrador de procesos es otra forma de ejecutar varios servicios dentro de sus contenedores Docker. El administrador de procesos se convierte en su ENTRYPOINT y tiene la responsabilidad de iniciar, mantener y limpiar después de sus procesos de trabajo.

Hay varias opciones disponibles al implementar este enfoque. supervisord es una opción popular que se configura fácilmente a través de un archivo /etc/supervisor/conf.d/supervisord.conf :

 [programa:apache2]
comando=/usr/sbin/apache2 -EN PRIMER PLANO

[programa: mysqld]
comando=/usr/sbin/mysqld_safe

Este archivo de configuración configura supervisord para iniciar Apache y MySQL. Para usarlo en un contenedor Docker, agregue todos los paquetes requeridos a su imagen, luego copie su archivo de configuración supervisord en la ubicación correcta. Configure supervisord como el CMD de la imagen para ejecutarlo automáticamente cuando se inicien los contenedores.

 DESDE ubuntu: más reciente
EJECUTE apt-get install -y apache2 mysql-server supervisor
COPIAR supervisord.conf /etc/supervisor/conf.d/supervisord.conf
PUNTO DE ENTRADA ["/bin/sh"]
CMD ["/usr/bin/supervisord"]

Debido a que supervisord se ejecuta continuamente, no es posible detener el contenedor cuando finaliza uno de sus procesos monitoreados. Una opción alternativa es s6-overlay que tiene esta capacidad. Utiliza un modelo de servicio declarativo en el que coloca scripts de servicio directamente en /etc/services.d :

 # Agregue s6-overlay a su imagen
AGREGAR https://github.com/just-containers/s6-overlay/releases/download/v3.1.0.0/s6-overlay-noarch.tar.xz /tmp
EJECUTAR tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz

EJECUTAR printf "#!/bin/shn/usr/sbin/apache2 -DFOREGROUND" > /etc/services.d/first-service/run
EJECUTAR chmod +x /etc/services.d/first-service/run

# Use s6-overlay como punto de entrada de su imagen
PUNTO DE ENTRADA ["/init"]

Puede agregar un script de finish ejecutable dentro de sus directorios de servicio para manejar la detención del contenedor con docker stop . s6-overlay ejecutará automáticamente estos scripts cuando su proceso reciba una señal TERM debido al comando de stop .

Los scripts de finalización reciben el código de salida de su servicio como primer argumento. El código se establece en 256 cuando el servicio se cancela debido a una señal no captada. El script necesita escribir el código de salida final en /run/s6-linux-init-container-results/exitcode ; s6-overlay lee este archivo y sale con el valor que contiene, lo que hace que ese código se use como el código de detención de su contenedor.

 #!/bin/sh
echo "$1" > /run/s6-linux-init-container-results/exitcode

¿Cuándo debe ejecutar múltiples procesos en un contenedor?

Esta técnica se utiliza mejor con procesos estrechamente acoplados que no se pueden separar para ejecutarlos como contenedores independientes. Es posible que tenga un programa que se base en una utilidad auxiliar en segundo plano o una aplicación monolítica que realice su propia gestión de procesos individuales. Las técnicas que se muestran arriba pueden ayudarlo a contener estos tipos de software.

Todavía se debe evitar ejecutar múltiples procesos en un contenedor siempre que sea posible. Cumplir con un solo proceso de primer plano maximiza el aislamiento, evita que los componentes interfieran entre sí y mejora su capacidad para depurar y probar piezas específicas. Puede escalar los componentes individualmente mediante orquestadores de contenedores, lo que le brinda la flexibilidad de ejecutar más instancias de sus procesos con mayor uso de recursos.

Conclusión

Los contenedores suelen tener un proceso en primer plano y se ejecutan mientras esté activo. Este modelo se alinea con las mejores prácticas de contenedorización y le permite obtener la mayor cantidad de beneficios de la tecnología.

En algunas situaciones, es posible que necesite varios procesos para ejecutar en un contenedor. Dado que, en última instancia, todas las imágenes tienen un único punto de entrada, debe escribir un script contenedor o agregar un administrador de procesos que asuma la responsabilidad de iniciar los archivos binarios de destino.

Los administradores de procesos le brindan todo lo que necesita, pero inflan sus imágenes con paquetes y configuraciones adicionales. Los scripts de contenedor son más simples, pero es posible que deban combinarse con el indicador --init de Docker para evitar la proliferación de procesos zombis.