Cómo crear contenedores Docker para desplegar aplicaciones
Si eres un desarrollador, un administrador de sistemas, trabajas en el sector de las tecnologías de la información o simplemente eres un entusiasta de la tecnología que ha desplegado alguna vez algún tipo de aplicación, seguro que te habrás encontrado con el mismo problema que nos hemos encontrado todos. Si estás pensando en dependencias, ¡bingo!
Al programar una aplicación, el desarrollador tiene un entorno muy específico, con un montón de librerías, una versión concreta del kernel, versiones específicas del lenguaje de programación con el que está trabajando, etc. Por ello, al compartir esta aplicación con otros programadores, colaboradores o incluso con algún cliente final, esta aplicación necesita una serie de dependencias, software específico que esta aplicación requiere para funcionar correctamente. Afortunadamente, para el cliente final en muchos casos se puede compilar el código, empaquetándolo en un fichero que es autocontenido y no necesita de dependencias externas. Flatpak o Snap son un buen ejemplo; algún día hablaremos de estos formatos que están tan de moda hoy en día.
Lamentablemente, compilar no siempre es una solución viable, sobre todo en el mundo web donde utilizamos páginas desarrolladas, por ejemplo, en PHP, que dependen de un servidor web.
Algo habitual ocurre cuando tenemos un equipo de desarrollo que está trabajando en la misma aplicación, en diferentes ordenadores y un equipo de Q/A que tiene que probar esta misma aplicación.
En estos casos, la gestión de dependencias puede ser un infierno si queremos usar esa aplicación en diferentes máquinas, sistemas operativos, etc., ya que tendríamos que definir una larga lista de dependencias, que en muchas ocasiones no están disponibles en todos los sistemas.
En estos casos tendríamos que buscar alternativas, compilar librerías o probar la aplicación con diferentes versiones de la librería con la que fue diseñada. Todo esto requiere mucho tiempo y esfuerzo a la hora de trabajar en equipo o desplegar en producción.
Por fortuna, estos dolores de cabeza son cosa del pasado. Hoy en día podemos desarrollar y desplegar contenedores Docker que estén preparados con las librerías que necesitamos. Sin importar la máquina en que se ejecuten, siempre tendrán las mismas librerías, misma configuración y se ejecutarán exactamente igual que en la máquina del desarrollador. Una maravilla, ¿no? Me dirás: “Sí, pero eso podía hacerlo antes con una máquina virtual”. Y estás en lo correcto, pero gestionar y mantener máquinas virtuales es mucho más difícil que mantener un contenedor; eso sin hablar del tiempo de despliegue, que en el caso de un contenedor hablamos de unos pocos segundos en la mayoría de los casos.
Es por eso que te vamos a enseñar a crear tus propios contenedores Docker para testear y desplegar tus aplicaciones. También puedes ver el vídeo que hemos subido a nuestro canal de Youtube:
¿Por qué usar contenedores Docker?
Para este tutorial utilizaremos Docker como nuestro gestor de contenedores. ¿Por qué?
La respuesta es muy simple: es una herramienta muy extendida, bien documentada, con una gran compatibilidad y sobre todo es muy fácil de usar.
Para instalar Docker, dependiendo de qué sistema operativo utilices, solo hay que ir a su web de documentación oficial y seguir las instrucciones: https://docs.docker.com/install/#supported-platforms
Una vez instalado, podrás seguir los pasos exactamente igual sin importar qué plataforma (SO) uses.
Para probar el correcto funcionamiento de Docker abriremos una terminal en nuestro sistema y ejecutaremos este simple comando:
docker run hello-world
Tendrías que obtener un resultado similar a:
En caso que veas una pantalla similar a la nuestra ya podemos comenzar.
Dockerfiles
Para efectos de este tutorial utilizaré una aplicación que conozco muy bien, por lo que será muy sencillo identificar las dependencias necesarias y la dinámica de la aplicación. Y sí, has dado en el clavo, utilizaremos Pandora FMS en su versión Community para que todos podamos seguir los pasos, ya que el código de esta aplicación es totalmente OpenSource.
Todos los ficheros que utilizaremos en este tutorial los puedes conseguir en mi repositorio de GitHub. Y en el caso que no quieras pasar por el proceso de creación y quieras simplemente probar las imagenes Docker que hemos creado, puedes descargarlas de mi repo de DockerHub.
Para comenzar voy a dividir mi aplicacion en 2 imágenes de Docker diferentes, una para las dependencias -la cual no variará demasiado pero es más lenta de construir- y otra para la aplicación en sí, en este caso Pandora FMS, la cual partirá de la imagen de dependencias base y añadirá algunos paquetes extra. En este caso, solo aplicaremos los paquetes de instalación del código, así que será un proceso mucho más rápido, por lo que si necesito crear una imagen con otra versión de la aplicación o con cualquier modificación, simplemente partiremos de la imagen base que no se altera y añadiremos el paquete o código alterado en esta segunda imagen, sin necesidad de añadir todas las dependencias de nuevo en cada imagen generada.
Imagen base
Para la imagen base partiremos del siguiente fichero Dockerfile que analizaremos a continuación; si has clonado o descargado el repositorio GitHub encontrarás este archivo en la ruta PandoraFMS/pandorafms_community/base_image/
#docker build -t rameijeiras/pandorafms-base FROM centos:7 RUN { \ echo '[artica_pandorafms]'; \ echo 'name=CentOS7 - PandoraFMS official repo'; \ echo 'baseurl=http://firefly.artica.es/centos7'; \ echo 'gpgcheck=0'; \ echo 'enabled=1'; \ } > /etc/yum.repos.d/pandorafms.repo RUN yum install -y --setopt=tsflags=nodocs \ https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm \ http://rpms.remirepo.net/enterprise/remi-release-7.rpm \ yum-utils RUN yum-config-manager --enable remi-php72 # Install console RUN yum install -y --setopt=tsflags=nodocs \ php \ php-mcrypt \php-cli \ php-gd \ php-curl \ php-mysqli \ php-ldap \ php-zip \ php-fileinfo \ php-snmp \ php-mbstring \ php-pecl-zip \ php-xmlrpc \ libxslt \ wget \ php-xml \ httpd \ mod_php \ atk \ avahi-libs \ cairo \ cups-libs \ fribidi \ gd \ gdk-pixbuf2 \ ghostscript \ ghostscript-fonts \ graphite2 \ graphviz \ gtk-update-icon-cach \ gtk2 \ harfbuzz \ hicolor-icon-theme \ hwdata \ jasper-libs \ lcms2 \ libICE \ libSM \ libXaw \ libXcomposite \ libXcursor \ libXdamage \ libXext \ libXfixes \ libXft \ libXi \ libXinerama \ libXmu \ libXrandr \ libXrender \ libXt \ libXxf86vm \ libcroco \ libdrm \ libfontenc \ libglvnd \ libglvnd-egl \ libglvnd-glx \ libpciaccess \ librsvg2 \ libthai \ libtool-ltdl \ libwayland-client \ libwayland-server \ libxshmfence \ mesa-libEGL \ mesa-libGL \ mesa-libgbm \ mesa-libglapi \ pango \ perl-Net-Telnet \ pixman \ xorg-x11-fonts-75dpi \ xorg-x11-fonts-misc \ poppler-data # Install server RUN yum install -y http://www6.atomicorp.com/channels/atomic/centos/7/x86_64/RPMS/wmi-1.3.14-4.el7.art.x86_64.rpm --setopt=tsflags=nodocs RUN yum install -y --setopt=tsflags=nodocs \ vim \ fping \ nmap \ perl-IO-Compress \ nmap \ sudo \ mysql \ net-snmp-utils; yum clean all EXPOSE 80 443 41121 162/udp
Como podéis observar, hemos comenzado con una línea comentada; esta línea no es necesaria, por supuesto, solo es una ayuda para que a la hora de crear la imagen sepamos cuál es el comando correcto. Lo veremos más adelante, por lo que nuestra primera línea efectiva siempre será el FROM.
Las imágenes de Docker siempre parten de una imagen anterior. En este caso, partimos de CentOS en su versión 7 con la línea:
FROM centos:7
CentOS es el nombre de la imagen y lo que está después de los dos puntos (:) es el tag. Por lo general, el tag suele ir asociado a la versión, como es el caso de CentOS, pero no siempre es así, hay que estar atentos a la imagen que utilicemos como base.
Una buena práctica es siempre definir una versión de la imagen base, ya que si no definimos nada utilizará la última versión disponible y podemos tener problemas de inconsistencias de imágenes en el futuro; recordemos que la idea es que en todos los dispositivos que usemos nuestro contenedor sea idéntico.
La siguiente línea es un RUN. La ejecución de un run le indica al Docker los comandos que debe ejecutar para configurar la imagen; en este caso tenemos 6 definiciones RUN, para la creación del repositorio de artica_pandorafms y la instalación de los paquetes correspondientes.
Por poner un ejemplo, la línea:
RUN yum-config-manager --enable remi-php72
Ejecuta el comando “yum-config-manager –enable remi-php72” que habilita el repositorio remi para la instalación de PHP7 en CentOS.
Cada una de las ejecuciones creará una capa en la imagen Docker, por lo que una buena práctica es ejecutar primero los comandos que menos variarán a lo largo del tiempo.
Por último, vemos la línea:
EXPOSE 80 443 41121 162/udp
Expone los puertos que definamos en el contenedor; en este caso, el 80 para HTTP, el 443 para HTTPS, el 41121 para el protocolo Tentacle de Pandora FMS y por último el 162 UDP para la recepción de traps SNMP.
Muy bien, ahora ya tenemos un Dockerfile para crear nuestra imagen base que contenga todas las dependencias necesarias. Solo nos hace falta ejecutar el comando docker build para comenzar la creación de la imagen Docker.
El comando docker build tiene varios parámetros opcionales, pero para efectos de este tutorial lo ejecutaremos de una forma muy simple. Para ello, tenemos que estar en el mismo directorio que nuestro fichero Dockerfile y ejecutar:
docker build -t rameijeiras/pandorafms-base .
Con esto comenzará un proceso de ejecución donde, si todo ha ido bien, se creará la imagen que hemos declarado, en mi caso con el nombre rameijeiras/pandorafms-base pero vosotros podéis poner la etiqueta que os guste más; tenedla en mente porque con esta etiqueta referenciamos esta imagen en la creación de nuestra siguiente imagen que será la de la aplicación Pandora FMS. Recordad que esta primera imagen nos servirá como base de dependencias para no tener que volver a ejecutar todos estos pasos con las diferentes pruebas o versiones de la aplicación final.
Imagen Pandora FMS
Para la definición del Dockerfile de la imagen de Pandora FMS, lo primero que haremos es crear un directorio desde el que trabajaremos; si habéis hecho un clon de mi repositorio de git, ese directorio será: PandoraFMS/pandorafms_community/pandorafms
Una vez que estemos en este directorio crearemos el fichero Dockerfile, que contendrá:
FROM rameijeiras/pandorafms-base ENV DBNAME=pandora ENV DBUSER=pandora ENV DBPASS=pandora ENV DBHOST=pandora ENV DBPORT=3306 ENV PASSPAN=P@ndora_2018 ENV SLEEP=5 ENV RETRIES=1 workdir /tmp # Install the Pandora server RUN rm -rf /etc/localtime ; ln -s /usr/share/zoneinfo/Europe/Madrid /etc/localtime RUN wget https://downloads.sourceforge.net/project/pandora/Tools%20and%20dependencies%20%28All%20versions%29/DEB%20Debian%2C%20Ubuntu/phantomjs ; \ chmod +x phantomjs; \ ln -s phantomjs /usr/bin/ COPY sources/ /tmp # Install phantomjs RUN chmod +x /tmp/run_pandora.sh RUN yum install -y https://downloads.sourceforge.net/project/pandora/Pandora%20FMS%207.0NG/740/RHEL_CentOS/pandorafms_console-7.0NG.740-1.noarch.rpm?r=https%3A%2F%2Fsourceforge.net%2Fprojects%2Fpandora%2Ffiles%2FPandora%2520FMS%25207.0NG%2F740%2FRHEL_CentOS%2Fpandorafms_console-7.0NG.740-1.noarch.rpm; \ yum install -y https://downloads.sourceforge.net/project/pandora/Pandora%20FMS%207.0NG/740/RHEL_CentOS/pandorafms_server-7.0NG.740-1.noarch.rpm; \ yum clean all EXPOSE 80 443 41121 162/udp CMD sh /tmp/run_pandora.sh
Como podéis apreciar, este fichero es ligeramente diferente al de la creación de la imagen base. Vamos a analizar este fichero más en profundidad, ya que es el que generará la imagen de nuestra aplicación en un contenedor Docker.
Para empezar, como siempre, un Dockerfile debe partir como base de otra imagen. En este caso la que creamos anteriormente, que yo he llamado rameijeiras/pandorafms-base
FROM rameijeiras/pandorafms-base
Seguido de la sentencia FROM tenemos varias sentencias de tipo ENV, las cuales asignan variables de entorno que estarán disponibles en la imagen a crear y por consiguiente en el contenedor Docker que levantemos partiendo de esta imagen. Una ventaja de las variables de entorno es que pueden ser sobreescritas en el momento de la ejecución del contenedor, lo cual nos permitirá modificar el comportamiento del mismo (lo veremos más en profundidad al levantar nuestro contenedor Docker).
Pero, ¿de qué me sirven estas variables? Pues por lo general los contenedores Docker tienen un entrypoint o un cmd que es lo que se ejecuta por defecto al levantar el contenedor. En este caso -y en muchos otros- este entrypont o cmd es un script que hace uso de las variables de entorno para configurar ciertos aspectos de la aplicación. Nosotros usaremos variables de entorno para definir a qué base de datos se conectará nuestro contenedor y otros aspectos de autoprovisión. Lo veremos en detalle cuando lleguemos a la sentencia CMD.
La siguiente sentencia es WORKDIR. Esta sentencia está muy relacionada con la sentencia RUN y funciona de forma muy sencilla. Básicamente, todo lo que ejecutamos a partir de definir un workdir se ejecutará en este directorio. Es similar a que Docker hiciera un cd a este directorio y desde allí continuara con la ejecución de todos los siguientes pasos.
A continuación, declaramos un RUN que ya conocemos y que ejecuta un comando wget para descargar el binario del phantomjs (una aplicación que necesita la consola de Pandora FMS para generar informes) y le asigna los permisos correspondientes con el comando chmod. Seguidamente crea un enlace simbólico del fichero en el directorio actual a /usr/bin/ que es donde la consola espera encontrarlo por defecto, y así no tenemos que preocuparnos de cambiar la configuración una vez tengamos la consola accesible.
La sentencia siguiente es un COPY que, como su nombre indica, copiará desde nuestro directorio definido en el ordenador hacia dentro de la imagen los ficheros que declaremos. En este caso copiará el contenido de la carpeta source en el directorio /tmp de la imagen que generaremos. En esta carpeta podemos meter los recursos que el contenedor necesitará para iniciar. Para esta imagen podéis encontrar los recursos en mi repositorio de GitHub, en el directorio sources, que contiene el script de inicio y autoprovisión de Pandora FMS (run_pandora.sh) y el script que levantará el servidor web Apache (run-httpd.sh).
Continuamos con otra sentencia RUN para la instalación de los paquetes pandora_console y pandora_server, en este caso la versión 740, desde nuestro repositorio de Sourceforge.
Y casi por último, la sentencia EXPOSE, que al igual que la imagen anterior declara los puertos necesarios para Pandora FMS.
Para finalizar el archivo tenemos la sentencia CMD, que define lo que ejecutará por defecto el contenedor Docker cuando lo inicializamos. Nótese que la imagen anterior no tiene esta sentencia y es correcto, ya que esa imagen fue diseñada como imagen de partida y no teníamos interés en que ejecutara nada al inicializarse. De cualquier forma, la sentencia CMD puede ser sobreescrita en la inicialización del contenedor Docker, en caso de ser requerido.
En este caso, la ejecución del CMD es un script que utiliza las variables de entorno que hemos definido al comienzo del Dockerfile con la etiqueta ENV para conectarse al servidor de base de datos, crear la base de datos en caso de no existir y auto configurarse para conectarse a ella. Si, por el contrario, la base de datos definida ya existe y tiene un esquema válido para Pandora FMS, se conectará a esta base de datos automáticamente haciendo uso de esta.
En este punto podemos ver la utilidad de sobreescribir las variables de entorno, ya que usando la imagen que construiremos podremos levantar infinitas instancias conectadas a diferentes bases de datos, sin necesidad de modificar nada ni de volver a generar la imagen, solo declarándolo en la ejecución.
Ahora que tenemos nuestro Dockerfile completo y nuestro directorio sources que contiene los scripts en el mismo directorio que nuestro Dockerfile, nos ubicamos en este. En el caso de clonar el repositorio github, el directorio sería, PandoraFMS/pandorafms_community/pandorafms, y ejecutamos:
docker build -t rameijeiras/pandorafms-community:740 .
En mi caso, he llamado a mi imagen rameijeiras/pandorafms-community y he asignado la etiqueta (tag) 740 con el símbolo dos puntos (:) después del nombre, ya que estoy usando la versión 740 de la aplicación. En un futuro, cuando genere imágenes de nuevas versiones, las etiquetaré con la versión correspondiente.
Si todo ha ido bien, tenemos nuestra imagen Docker de Pandora FMS Community Edition versión 740 y podremos ejecutarla en cualquier ordenador que tenga Docker, exactamente igual, sin problemas de dependencias, empaquetado en un contenedor que correrá igual siempre. Pero, ¿cómo lo ejecutamos? Pues con un no tan sencillo comando.
Para esta imagen de Pandora FMS, lo primero que necesitamos es una base de datos a la que conectarnos, bien sea en Docker o física, y luego ejecutamos:
docker run --name pandora_community --rm \ -p 8085:80 \ -p 41121:41121 \ -p 162:162 \ -e DBHOST=mysqlhost.local \ -e DBNAME=pandora \ -e DBUSER=pandora \ -e DBPASS=pandora \ -e DBPORT=3306 \ -e SLEEP=5 \ -e RETRIES=3 \ -e INSTANCE_NAME=pandora_community \ -ti rameijeiras/pandorafms-community:740
Con la ejecución de este comando levantaremos un contenedor Docker llamado pandora_community, el cual se conectará a mi servidor de bases de datos mysqlhost.local en el puerto 3306, a la base de datos pandora (que tengo creada pero vacía), con el usuario pandora y contraseña pandora. Llamaremos a la instancia pandora_community y definiremos las opciones RETRIES y SLEEP que serán la cantidad de veces que intenta conectar con la base de datos en caso de fallar y cuánto esperará antes de cada reintento.
Veréis una salida similar a esta:
En este comando hemos declarado con el parámetro -p la redirección del puerto 8085 de nuestro ordenador o servidor Docker al puerto 80 del contenedor, y los puertos 41121 y 162 de nuestro ordenador directamente a los puertos homónimos en el contenedor, por lo que si accedemos a la url (sin espacio antes de ip): < ip_host>:8085/pandora_console veremos la pantalla de login de Pandora FMS, a la que podremos acceder con las credenciales admin:pandora
Ahora tenemos creada nuestra imagen para ejecutar contenedores Docker con Pandora FMS de forma rápida y uniforme en cualquier entorno, y también podemos aplicar los conceptos aprendidos para crear contenedores de cualquier aplicación propia o de terceros.
La ejecución del comando docker run puede ser un poco compleja de entender si es la primera vez que se utiliza, sobre todo si vamos añadiendo más parámetros y variables de entorno.
En futuras entradas os enseñaré cómo gestionar vuestros contenedores Docker de forma sencilla usando Docker Compose para crear un stack de Pandora FMS + Base de datos en un solo comando que inicializará todo el entorno en unos pocos segundos. Así que quédate atento a los posts que vienen, para aprender a sacar el mayor partido de tus aplicaciones en contenedores Docker.
Finalmente, recuerda que si cuentas con más de 100 dispositivos para monitorizar puedes contactar con el equipo de Pandora FMS a través del siguiente formulario.
Además, si tus necesidades de monitorización son más limitadas tienes a tu disposición la versión OpenSource de Pandora FMS. Encuentra más información aquí.
No dudes en enviar tus consultas. ¡El equipo de Pandora FMS estará encantado de atenderte!
Jefe de proyectos en Pandora FMS. Más de 15 años de experiencia en el mundo IT. Amante de la tecnología y el open source, cultura DevOps. Fan y promotor de la automatización, contenerización y la orquestación de contenedores. Project manager in Pandora FMS. More than 15 years of experience in the IT world. Technology and open source lover, DevOps culture. Fan and promoter of the automation, containment and orchestration of containers.