Al preparar una infraestructura con Docker para producción, pueden dartse problemas con ciertas imágenes que son suficientemente complejas como para no ser fácilmente extendibles, pero que tampoco cumplen todos nuestros requerimientos.
En este caso, lo más normal es extender esa imagen hasta sentirse cómodo con ella. Pero, ¿cómo lo hacemos si necesitamos incluir otros servicios? Modificar la capa ENTRYPOINT
puede ser un riesgo y un dolor de cabeza, y tendríamos que mantenernos al tanto de cada cambio en la imagen original. En lugar de ello, puede emplearse supervisord
.
Aprendiendo con ejemplos
Un ejemplo que he encontrado es el de Redmine. Esta imagen sirve por defecto la aplicación en el puerto 3000
usando HTTP
(de forma insegura). Necesitaremos un proxy inverso que encripte las conexiones y permita la comunicación por HTTPS
.
Lo lógico sería crear otro contenedor y unirlo a la misma red para que todas las comunicaciones con el contenedor de Redmine pasen por el proxy, pero sigamos el juego y extendamos la imagen de Redmine para añadirle un servidor nginx
:
FROM redmine:5 RUN apt-get update && \ apt-get install -y \ nginx \ apt-get clean && \ useradd nginx && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* COPY conf/default.conf /etc/nginx/conf.d/default.conf EXPOSE 80 EXPOSE 443
Donde default.conf
contiene lo siguiente:
server { listen 80; listen [::]:80; server_name redmine.example.com; return 301 https://$host$request_uri; } server { listen 443 ssl; listen [::]:443 ssl; server_name redmine.example.com; ssl_certificate /path/to/certificate.crt; ssl_certificate_key /path/to/certificate.key; location / { proxy_pass http://127.0.0.1:3000; } }
Hasta ahora bien, pero Nginx no se inicia en ningún momento. Para eso usaremos supervisord
. Si inspeccionamos el Dockerfile
de Redmine, podemos ver las siguientes líneas:
WORKDIR /usr/src/redmine ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["rails", "server", "-b", "0.0.0.0"]
Que son toda la información que necesitamos para configurar supervisord
para que inicie Redmine.
En primer lugar habrá que instalarlo en nuestra imagen, de forma que nuestro Dockerfile
quedaría:
FROM redmine:5 RUN apt-get update && \ apt-get install -y \ supervisor \ nginx \ apt-get clean && \ useradd nginx&& \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* COPY conf/supervisord.conf /etc/supervisor/conf.d/supervisord.conf COPY conf/default.conf /etc/nginx/conf.d/default.conf EXPOSE 80 EXPOSE 443 USER supervisor ENTRYPOINT ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
Con esto, el servicio que iniciamos es supervisord
, y es éste el que se encarga de levantar el resto de servicios que necesitemos. En nuestro caso Redmine y Nginx. Esto lo configuramos en el archivo supervisord.conf
que estamos copiando a la imagen, que contendría lo siguiente:
[supervisord] nodaemon=true [program:nginx] user=nginx command=nginx [program:redmine] user=redmine directory=/usr/src/redmine command=/docker-entrypoint.sh rails server -b 127.0.0.1
La primera sección configura el propio supervisord
para que corra en primer plano, lo que hará que el proceso del contenedor no termine sin más.
La segunda sección inicia Nginx empleando el archivo de configuración por defecto, que es el que hemos copiado al crear la imagen con el Dockerfile
.
Con la última sección se inicia Redmine, cambiando al directorio /usr/src/redmine
, tal y como se hacía en el Dockerfile
original de Redmine. Además, se inicia Redmine concatenando las capas ENTRYPOINT
y CMD
del Dockerfile
original. También restringimos las direcciones IP
en las que escucha Redmine al bucle local, evitando conexiones directas desde fuera del contenedor.