Ejecutar múltiples procesos en un contenedor
El proceso de ejecución principal de un contenedor es el ENTRYPOINT y/o CMD al final del Dockerfile. La mejor práctica consiste en separar las áreas de responsabilidad utilizando un servicio por contenedor. Ese servicio puede bifurcarse (fork) en múltiples procesos (por ejemplo, el servidor web Apache inicia múltiples procesos de trabajo). Está bien tener múltiples procesos, pero para obtener el mayor beneficio de Docker, evita que un solo contenedor sea responsable de múltiples aspectos de tu aplicación global. Puedes conectar múltiples contenedores utilizando redes definidas por el usuario y volúmenes compartidos.
El proceso principal del contenedor es responsable de gestionar todos los procesos que inicia. En algunos casos, el proceso principal no está bien diseñado y no maneja correctamente la recolección ("reaping" o detención) de procesos hijos de manera adecuada cuando el contenedor finaliza. Si tu proceso entra en esta categoría, puedes utilizar la opción --init al ejecutar el contenedor. La bandera --init inserta un pequeño proceso de inicialización (init-process) en el contenedor como el proceso principal, y se encarga de la detención de todos los procesos cuando el contenedor finaliza. Manejar este tipo de procesos de esta forma es preferible a utilizar un proceso de inicialización completo como sysvinit o systemd para gestionar el ciclo de vida de los procesos dentro de tu contenedor.
Si necesitas ejecutar más de un servicio dentro de un contenedor, puedes lograrlo de diferentes maneras.
Usar un script wrapper
Coloca todos tus comandos en un script wrapper, completado con información de prueba y depuración. Ejecuta el script wrapper como tu CMD. A continuación se muestra un ejemplo básico. Primero, el script wrapper:
#!/bin/bash
# Iniciar el primer proceso
./my_first_process &
# Iniciar el segundo proceso
./my_second_process &
# Esperar a que finalice cualquier proceso
wait -n
# Salir con el estado del proceso que finalizó primero
exit $?A continuación, el Dockerfile:
# syntax=docker/dockerfile:1
FROM ubuntu:latest
COPY my_first_process my_first_process
COPY my_second_process my_second_process
COPY my_wrapper_script.sh my_wrapper_script.sh
CMD ./my_wrapper_script.shUsar los controles de trabajos de Bash
Si tienes un proceso principal que necesita iniciarse primero y permanecer en ejecución, pero temporalmente necesitas ejecutar algunos otros procesos (tal vez para interactuar con el proceso principal), entonces puedes usar el control de trabajos de bash. Primero, el script wrapper:
#!/bin/bash
# activar el control de trabajos de bash
set -m
# Iniciar el proceso principal y ponerlo en segundo plano
./my_main_process &
# Iniciar el proceso auxiliar
./my_helper_process
# el proceso my_helper_process podría necesitar saber cómo esperar a que
# el proceso principal se inicie antes de realizar su trabajo y finalizar
# ahora se trae el proceso principal de vuelta al primer plano
# y se deja ahí
fg %1# syntax=docker/dockerfile:1
FROM ubuntu:latest
COPY my_main_process my_main_process
COPY my_helper_process my_helper_process
COPY my_wrapper_script.sh my_wrapper_script.sh
CMD ./my_wrapper_script.shUsar un gestor de procesos
Utiliza un gestor de procesos como supervisord. Esto es más complejo que las otras opciones, ya que requiere que incluyas supervisord y su configuración en tu imagen (o que bases tu imagen en una que incluya supervisord), junto con las diferentes aplicaciones que gestiona. Luego inicias supervisord, que se encarga de gestionar tus procesos por ti.
El siguiente ejemplo de Dockerfile muestra este enfoque. El ejemplo asume que estos archivos existen en la raíz del contexto de compilación:
supervisord.confmy_first_processmy_second_process
# syntax=docker/dockerfile:1
FROM ubuntu:latest
RUN apt-get update && apt-get install -y supervisor
RUN mkdir -p /var/log/supervisor
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY my_first_process my_first_process
COPY my_second_process my_second_process
CMD ["/usr/bin/supervisord"]Si quieres asegurarte de que ambos procesos dirijan su salida stdout y stderr a los logs del contenedor, puedes añadir lo siguiente al archivo supervisord.conf:
[supervisord]
nodaemon=true
logfile=/dev/null
logfile_maxbytes=0
[program:app]
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true