¡Despleguemos con Docker y Nginx!
El objetivo de este laboratorio es crear una imagen Docker que contenga todo lo necesario para desplegar una aplicación web HelloWorld en un servidor Nginx. Esta imagen estaría lista para ser desplegada en nuestro entorno de producción.
Para comenzar este laboratorio vamos a partir de una aplicación web HelloWorld que hemos creado para la ocasión.
Es una aplicación muy básica realizada con Angular. En la barra superior muestra inicialmente el típico saludo "Hello World!!". En la zona central de la pantalla, justo debajo de la barra superior, aparece un formulario que nos pide que introduzcamos nuestro nombre. Si lo introducimos y pulsamos el botón "Saludar", la aplicación tomará el nombre introducido y se lo enviará al servidor para que este le devuelva un saludo personalizado. Si todo va bien, el saludo obtenido sustituirá al saludo existente en la barra superior. Si por cualquier motivo no se pudiera obtener el saludo personalizado se utilizaría un saludo personalizado por defecto.
Vamos a clonar el proyecto HelloWorld en la misma máquina virtual que hemos usado en los laboratorios anteriores. Para ello ejecutaremos el siguiente comando en la carpeta home del usuario:
cd
git clone https://github.com/Accenture/openathon-2019-docker-angular-app.git
Como resultado obtendremos lo siguiente:
$ git clone https://github.com/Accenture/openathon-2019-docker-angular-app.git
Cloning into 'openathon-2019-docker-angular-app'...
remote: Enumerating objects: 61, done.
remote: Counting objects: 100% (61/61), done.
remote: Compressing objects: 100% (46/46), done.
remote: Total 61 (delta 14), reused 60 (delta 13), pack-reused 0
Unpacking objects: 100% (61/61), done.
Cuando finalice la descarga, tendremos una nueva carpeta llamada openathon-2019-docker-angular-app. Accederemos a su interior con el siguiente comando:
cd openathon-2019-docker-angular-app
Para construir la imagen Docker que contenga nuestra aplicación y el servidor web Nginx vamos a utilizar como base otras dos imágenes:
- Imagen de Node.js: Contiene una instalación de Node.js que vamos a necesitar para "compilar" la aplicación.
- Imagen de Nginx: Contiene una instalación de Nginx que usaremos como servidor web para publicar nuestra aplicación.
Para hacer el pull de ambas imágenes tan solo tenemos que ejecutar los siguientes comandos en nuestro terminal:
docker pull node
docker pull nginx
Si todo va bien, después de completar la descarga, ambas imágenes deben aparecer en nuestra lista tras ejecutar el siguiente comando:
docker images
Como resultado obtendremos lo siguiente:
[node1] (local) root@192.168.0.48 ~/openathon-2019-docker-angular-app
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 4152a9608752 2 days ago 126MB
node latest 1a77bcb355eb 2 weeks ago 933MB
Para que nuestra aplicación Angular se despliegue y funcione correctamente necesitaremos un fichero de configuración básico para el servidor web Nginx. En este fichero, llamado nginx.conf, se especifican cosas como el puerto en el que se publicará, la ruta del directorio raíz, las rutas permitidas, etc. En esta URL puedes obtener más detalles sobre su estructura.
Este fichero ya se encuentra en la carpeta raíz de la aplicación. Puedes consultarlo en https://github.com/Accenture/openathon-2019-docker-angular-app/blob/master/nginx.conf. Su contenido se muestra a continuación:
worker_processes 1;
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html index.htm;
include /etc/nginx/mime.types;
location / {
try_files $uri $uri/ /index.html;
}
location /assets/img/ {
}
}
}
También necesitamos un fichero Dockerfile para indicar a Docker cuál será el contenido de la nueva imagen que vamos a crear. En esta URL puedes obtener información detallada sobre los ficheros Dockerfile.
Este fichero ya se encuentra en la carpeta raíz de la aplicación. Puedes consultarlo en https://github.com/Accenture/openathon-2019-docker-angular-app/blob/master/Dockerfile. Su contenido se muestra a continuación:
# Utilizamos la imagen de node como base ya que la necesitamos para "compilar" los fuentes del proyecto Angular. Denominaremos a esta imagen "build"
FROM node as build
# Recogemos el argumento de entrada si existe, si no usaremos el valor por defecto (localhost)
ARG ARG_API_URL=localhost
# Asignamos a la variable de entorno API_URL el valor del argumento de entrada definido en el paso anterior.
# Esta variable de entorno la utiliza el compilador de Angular para establecerla en el momento de la "compilación"
ENV API_URL=$ARG_API_URL
# Copiamos el fichero package.json a una nueva carpeta de trabajo
COPY ./package.json /usr/angular-workdir/
WORKDIR /usr/angular-workdir
# Lanzamos el comando npm install para que node se descargue todas las dependencias
# definidas en nuestro fichero package.json
RUN npm install
# Copiamos todo el código del proyecto a la carpeta de trabajo
COPY ./ /usr/angular-workdir
# Ahora que tenemos todas las dependencias y todo el código podemos generar
# nuestro entregable ejecutando el siguiente comando.
RUN npm run buildProd
# Llega el momento de preparar el servidor web, para ello usaremos la imagen base
# de Nginx
FROM nginx
# Copiamos el fichero nginx.conf a la ruta adecuada en la imagen nginx
COPY ./nginx.conf /etc/nginx/nginx.conf
# Borramos todos los ficheros que pudieran existir en la ruta donde desplegaremos
# el desplegable que hemos generado antes
RUN rm -rf /usr/share/nginx/html/*
# Finalmente copiamos nuestro entregable desde la imagen de node a la ruta de despliegue
# en la imagen de Nginx
COPY --from=build /usr/angular-workdir/dist/HelloWorld /usr/share/nginx/html
En este fichero Dockerfile se especifican los pasos que hay que dar para generar la imagen tal y como la queremos. Los comentarios incluidos en el fichero te indican en que consiste cada paso y cuál es su finalidad.
En esta URL puedes consultar las distintas instrucciones y mejores prácticas para escribir Dockerfiles.
Con todos los ingredientes que tenemos, ahora tan solo nos queda pedirle a Docker que siga los pasos descritos en el Dockerfile para elaborar la imagen, como si de una receta de cocina se tratase. Para ello usaremos el comando build con una serie de parámetros que detallamos a continuación:
- -t: Nos permite asignar un tag (nombre) a la imagen. En este caso el nombre será "helloworld"
- --build-arg: Nos permite especificar un parámetro de entrada que se utilizará en la ejecución del Dockerfile. En este caso nuestro Dockerfile recoge un parámetro opcional denominado ARG_API_URL en el que debemos indicar el nombre del host al que la aplicación se intentará conectar para obtener el saludo personalizado. Para obtener el nombre del host adecuado para usarlo en Play with Docker debemos construirlo siguiendo los siguientes pasos:
- Obtenemos el identificador de nuestra instancia de Play with Docker que podemos obtener de la primera sección (hasta la @) de la URL para la conexión mediante SSH. En la siguiente imagen se muestra un ejemplo remarcando la sección que representa el identificador. Atención: No confundir con el identificador mostrado justo encima de la IP. Son parecidos pero no iguales.
- Concatenarle la siguiente cadena al final: -8080.direct.labs.play-with-docker.com
El resultado final debería tener un aspecto parecido a del siguiente ejemplo:
Ahora tan solo nos queda construir el comando completo sustituyendo la etiqueta < HOST > por el identificador que acabamos de construir y lo ejecutamos en la consola:
ATENCION: Asegurate de construir correctamente el identificador y sustituirlo por < HOST > en el comando, de lo contrario la imagen que se generará será incapaz de conectarse al servidor cuando la usemos en los próximos laboratios.
docker build -t helloworld --build-arg ARG_API_URL=<HOST> .
La ejecución puede tardar más de un minuto e irá mostrando el resultado de la ejecución de cada uno de los pasos incluidos en el fichero Dockerfile.
Es posible que se muestre algún mensaje de Warning pero podemos ignorarlos.
Si todo va bien, nuestra nueva imagen aparecera, junto a las de node y nginx, en nuestra lista de imágenes si ejecutamos el siguiente comando:
docker images
Como resultado obtendremos lo siguiente:
[node1] (local) root@192.168.0.48 ~/openathon-2019-docker-angular-app
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
helloworld latest 889203080254 13 minutes ago 127MB
<none> <none> 4c002681fcca 13 minutes ago 1.27GB
nginx latest 4152a9608752 2 days ago 126MB
node latest 1a77bcb355eb 2 weeks ago 933MB
La imagen identificada como < none > es una imagen temporal que se ha creado en este proceso.
Ahora que tenemos lista nuestra nueva imagen docker tan solo nos queda ejecutarla y crear así nuestro contenedor que arrancará el servidor web Nginx con nuestro HelloWorld.
Para ellos vamos a ejecutar el siguiente comando:
docker run -d -p 80:80 --name "helloWorld" helloworld
Si todo va bien, en el panel superior de la máquina virtual se mostrará junto al botón OPEN PORT un nuevo enlace indicando que la aplicación está publicada en el puerto 80. Si pulsamos el enlace se abrirá una nueva pestaña donde podremos ver nuestra aplicación HelloWorld.
¡HelloWorld desplegado en Nginx!
También podemos ver que nuestro nuevo contenedor está en ejecución con el siguiente comando:
docker ps
Como resultado obtendremos lo siguiente:
[node1] (local) root@192.168.0.48 ~/openathon-2019-docker-angular-app
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7ed0c8cec8e3 helloworld "nginx -g 'daemon of…" 7 minutes ago Up 7 minutes 0.0.0.0:80->80/tcp xenodochial_archimedes
Como paso final, después de haber disfrutado de esta magnífica aplicación :), vamos a parar y eliminar el contenedor con el siguiente comando:
docker rm -f helloWorld
Y verificamos que ya no está en ejecución con este otro:
docker ps
Hemos clonado una aplicación web hecha con Angular. Hemos creado una nueva imagen a partir otras dos (Node y Nginx) y en ella hemos incluido un servidor Nginx que publica nuestra aplicación web. Luego hemos generado un contenedor a partir de esa imagen y hemos comprobado que la aplicación se publica tal y como esperábamos.
< Lab - 03. Una pequeña práctica, un "Hola Mundo" por supuesto. | Lab 03. Backend >