SupervisorD: Gestionando Procesos en Docker


Antes de comenzar, quería contarles que hay una promoción en DigitalOcean donde te dan un crédito de USD 100.00 durante 60 días para que puedas probar los servicios que este Proveedor Cloud ofrece. Lo único que tienes que hacer es suscribirte a DigitalOcean con el siguiente botón:

DigitalOcean Referral Badge

O a través del siguiente enlace: https://bit.ly/digitalocean-itsm


¿Alguna vez te has encontrado con el escenario donde necesitas ejecutar más de 1 proceso en un mismo contenedor?

A pesar que Docker provee la herramienta Docker Compose para levantar aplicaciones en distintos contenedores, Docker Compose necesita de ser configurado mediante un archivo YAML y poder ejecutar esos servicios. A veces necesitamos (aunque no es lo recomendable), ejecutar mas de 2 procesos en un mismo contenedor.

Recordemos que el objetivo de Docker es precisamente ejecutar un proceso aislado completamente de otro (un contenedor es un proceso del sistema) En este articulo utilizaremos una herramienta que nos permite lograr lo anteriormente expuesto: SupervisorD.

Uno de los problemas que enfrentamos al desplegar multiples servicios como SSH, Http es: ¿Cómo controlamos esos servicios? Al no tener scripts de inicio o systemd habilitado dentro de los contenedores, necesitaríamos un mecanismo para iniciar, detener y monitorear el estatus de los mismos.

/images/docker-supervisord/supervisord-0.png
Dos procesos en un contenedor con SupervisorD

Entonces, SupervisorD es una herramienta que nos permite gestionar un número diferente de procesos simultaneamente en Linux. supervisorD necesita de un archivo de configuración .conf donde le especificamos los procesos y las diferentes opciones relaciones para que ese proceso pueda iniciar, auto reiniciarse, la ubicación de los logs, etc.

Aunque como dije en la introducción, lo recomendado es que por cada servicio haya un contenedor, SupervisorD se puede utilizar en casos donde necesitemos tener otro proceso ejecutándose en el mismo contenedor, como por ejemplo, si tenemos una aplicación que necesite tener Django con Redis.

Sin embargo, la desventaja principal seria que no podriamos escalar nuestro servicio de manera Horizontal (es decir, colocar mas contenedores para que reciban mas tráfico tanto de la aplicación como de Redis), sino que tendría que ser de manera Vertical (asignar mas recursos de la máquina para que puedan ser consumidas por el contenedor)

En el ejemplo que voy a utilizar, vamos a instalar SSH sobre una imagen preexistente de Apache, y veremos como los dos procesos coexistirán en en mismo contenedor.

Primero, vamos a crear un Dockerfile con el siguiente contenido:

1
2
3
4
5
6
7
8
9
FROM httpd:alpine

RUN apk add --no-cache openssh supervisor

RUN ssh-keygen -A

COPY supervisor.conf /etc/supervisor.conf

CMD [ "supervisord", "-c", "/etc/supervisor.conf" ]

Como podemos ver, partimos de una imagen de httpd (apache para los entendidos) de la distro alpine (la más livianita que podemos encontrar)

Luego, vamos a instalar openssh y supervisor, este último para que se encargue del control de los procesos dentro del contenedor a ejecutar.

Copiamos el archivo de configuración de SupervisorD hacia la imagen y por ultimo el comando a ejecutar cada vez que el contenedor levante, será supervisord y con el argumento “-c” le vamos a decir que cargue el archivo de configuración que acabamos de copiar a la imagen (ubicado en /etc/supervisor.conf)

El archivo de supervisor.conf debe tener como mínimo el siguiente contenido:

1
2
3
4
5
6
7
8
[supervisord]
nodaemon=true

[program:sshd]
command=/usr/sbin/sshd -D

[program:apache2]
command=/usr/local/bin/httpd-foreground

El primer parámetro significa que no vamos a ejecutar supervisor como un servicio, sino que lo hará en background. En el segundo y tercer parámetro tenemos el comando de arranque de openssh y de apache.

Bueno, ahora lo que tenemos que hacer es construir la imagen Docker:

1
docker build -t supervisor .

Una vez construida, vamos a levantar el contenedor:

1
docker run supervisor

Como podemos detallar en la siguiente salida (ya que no hemos puesto el contenedor en modo detach), observamos que supervisord inició apache como pid 7 y sshd como pid 8:

1
2
3
4
5
6
2022-03-05 20:13:20,222 CRIT Supervisor is running as root.  Privileges were not dropped because no user is specified in the config file.  If you intend to run as root, you can set user=root in the config file to avoid this message.
2022-03-05 20:13:20,224 INFO supervisord started with pid 1
2022-03-05 20:13:21,226 INFO spawned: 'apache2' with pid 7
2022-03-05 20:13:21,228 INFO spawned: 'sshd' with pid 8
2022-03-05 20:13:22,247 INFO success: apache2 entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2022-03-05 20:13:22,247 INFO success: sshd entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)

Si ejecutáramos docker ps para ver los contenedores que se están ejecutando, veríamos el de supervisor:

1
docker ps
1
2
CONTAINER ID   IMAGE                      COMMAND                  CREATED         STATUS         PORTS     NAMES
eeb1bb74e289   supervisor                 "supervisord -c /etc…"   2 minutes ago   Up 2 minutes   80/tcp    happy_mestorf

Vamos a entrar al contenedor, y ver los procesos que está ejecutando:

1
docker exec -it eeb1bb74e289 sh

Si hacemos ps fax:

1
2
3
4
5
6
7
8
9
PID   USER     TIME  COMMAND
    1 root      0:00 {supervisord} /usr/bin/python3 /usr/bin/supervisord -c /etc/supervisor.conf
    7 root      0:00 httpd -DFOREGROUND
    8 root      0:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
   10 www-data  0:00 httpd -DFOREGROUND
   11 www-data  0:00 httpd -DFOREGROUND
   12 www-data  0:00 httpd -DFOREGROUND
   94 root      0:00 sh
  100 root      0:00 ps fax

Comprobaríamos entonces que los pid 7 y 8 están ejecutando tanto apache como ssh en el mismo contenedor.

Esto ha sido todo, espero les haya gustado y nos vemos en la próxima! Happy Dockering 😄


Si te pareció útil este artículo y el proyecto en general, considera brindarme un café :)

Buy me a coffeeBuy me a coffee