Angular + Docker... really?
Tags: angular, docker, javascript, microservices
Published: 2019-08-26 17:55:55

> Este es un tutorial básico de docker para Front end developers, de igual forma los conceptos explicados acá, se pueden usar como base para dockerizar cualquier otra app creada en otro lenguaje ó framework.

Cuando el Big Bang de microservicios sucedió, se dice que surgió de las profundidas un concepto que en su tiempo fue innovador y de gran utilidad, los dioses le llamaron "Máquinas virtuales (VM)".  Las máquinas virtuales tienen un sistema operativo completo con su propia administración de memoria instalada con la sobrecarga asociada de los controladores de dispositivos virtuales. En una máquina virtual, se emulan recursos valiosos para el SO huésped y el hipervisor, lo que hace posible ejecutar muchas instancias de uno o más sistemas operativos en paralelo en una sola máquina (o host). Cada sistema operativo invitado se ejecuta como una entidad individual del sistema host.

Todo iba funcionando de maravilla, pero como siempre, nada es perfecto. Costos de mantenimiento del hardware, dinero, esos demonios que rodean nuestros desarrollos, comenzaron a salir de la oscuridad y abrumar el paraíso de las máquinas virtuales. Las VMs son hardware dependientes, lo cual las hacen más $$ de usar e implementar, además al tener un sistema operativo invitado, estaban consumiendo más memoria de lo esperado. Cuando todos empezaban a desesperarse, desde el mismo océano, surgió la esperanza, una pequeña ballena trajo la respuesta: contenedores. 

![Alt Text](https://i0.wp.com/www.vinsguru.com/wp-content/uploads/2018/04/docker-volume-mapping-header.gif)
<h6> <center> Tomado de https://www.testautomationguru.com </h6>


<h2>Contenedores, docker.</h2>

Los contenedores son más pequeños que las máquinas virtuales y permiten un arranque más rápido con un mejor rendimiento, menos aislamiento y una mayor compatibilidad posible debido al uso compartido del núcleo del host. La mejor opción para ejecutar una arquitectura de aplicación de microservicios son los contenedores de aplicaciones. En términos simples, Docker es una plataforma de software que simplifica el proceso de creación, ejecución, administración y distribución de aplicaciones. Lo hace virtualizando el sistema operativo de la computadora en la que está instalado y ejecutándose.

<h3>Conceptos básicos</h3>

- **Dockerfile**: Un Dockerfile es un documento de texto que contiene todos los comandos que un usuario podría llamar en la línea de comandos para ensamblar una imagen.

- **Docker image**: Es un archivo ejecutable que contiene todas las bibliotecas y configuraciones necesarias para ejecutar la aplicación. Tiene varias capas apiladas una encima de la otra y representadas como un solo objeto. Una imagen es creada, usando un Dockerfile.

- **Docker container**: Es una instancia en ejecución de un docker image. Puede haber muchos contenedores ejecutándose en base a la misma imagen.


<h3> "Contenerizar" un Angular app</h3>

Para motivos de este post, yo estoy suponiendo que usted ya tiene instalado NPM y Angular CLI, siendo así, vamos a crear un Angular app.

`ng new angular-docker`

Ok, vamos a crear un dockerfile y se tiene que guardar dentro de nuestra carpeta _angular-docker_. Este archivo no tiene extensión y se llama Dockerfile. A continuación agregar el siguiente código a dockerfile:

```
FROM node:latest as node

ARG ENV=prod
ARG APP=angular-docker

ENV ENV ${ENV}
ENV APP ${APP}

WORKDIR /app
COPY ./ /app/

# Instala y construye el Angular App
RUN npm ci
RUN npm run build --prod
RUN mv /app/dist/${APP}/* /app/dist/

# Angular app construida, la vamos a hostear un server production, este es Nginx

FROM nginx:1.13.8-alpine

COPY --from=node /app/dist/ /usr/share/nginx/html
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
```

Ok, hasta aquí muy lindo y todo ¿ Pero que rayos significa lo que está en el Dockerfile?

`FROM node:latest as node` : Las aplicaciones de Angular necesitan `node` and `npm` para instalar sus dependencias, de igual forma podrías usar `yarm` pero esto sería otra historia. Entonces, acá lo que estamos haciendo es usar la última version de node, la cual guardaremos en una `capa` llamada node (puedes escoger cualquier nombre).

`WORKDIR /app`: Este establece el directorio de trabajo para nuestro código en la imagen, es utilizado por todos los comandos posteriores como `COPY`, `RUN` y `CMD`.

`COPY ./ /app/`: Este comando copia todos los archivos existentes en el proyecto a la carpeta `/app` de nuestra imagen de docker. 

`RUN npm ci`: El comando `npm ci` tiene la misma funcionalidad de instalar dependencias como `npm install` pero a diferencia, que `npm ci` primero borra la carpeta `node_modules`, luego instala las versiones exactas de las dependencias del proyecto usando el`packaje.json.lock`. Este comando es recomendando cuando estás trabajando con herramientas de CI/CD como Travis, Jenkins, Concourse.

`RUN npm run build --prod`: El comando `build` crea una nueva carpeta llamada `dist` para su distribución. Estos son los archivos que podemos alojar en un servidor y que nuestra aplicación Angular cargará. El flag `--prod` crear una versión más optimizada y pequeña de nuestra app. 

`RUN mv /app/dist/${APP}/* /app/dist/` : Movemos todo lo creado por el comando anterior en la carpeta `app/dist`.

Hasta acá, nuestra imagen ya contiene la versión mas optimizada de nuestro proyecto, pero no basta con eso, recordemos que Angular es solo HTML, CSS y JS, necesita un server/proxy para poder hostearse y ser accesible para los usuarios. Mi server favorito para aplicaciones web, es NGINX.

`FROM nginx:1.13.8-alpine`: En este comando vamos a crear una segunda capa, la cual contiene la versión `1.13.8-alpine` de `nginx`.

`COPY --from=node /app/dist/ /usr/share/nginx/html`: En este comando sucede mucha magia, como decimos en Costa Rica, `pele el ojo`. Vamos a tomar los contenidos de la carpeta `app/dist/` generados en la primer capa llamada `node` y los vamos a mover al folder `/usr/share/nginx/html` de nuestro server NGINX. Acá estamos viendo la magia de los `multi-stages` de docker.

![Alt Text](https://media.giphy.com/media/Fkmgse8OMKn9C/giphy.gif)

`COPY ./nginx.conf /etc/nginx/conf.d/default.conf`: Por último, solamente copiamos un archivo de configuración de nginx en el folder del contenedor.  Les dejo el link de mi configuración https://github.com/gelopfalcon/kubernetes-fundamentals/blob/master/contact-list-master/nginx.conf

<h3> Construir la imagen </h3>

`docker build -t my-angular-docker  .`

El comando anterior nos permite crear una imagen en nuestro entorno local. Podemos asegurarnos de que exista, usando `docker images` la cual desplega las imagenes que hemos creado o descargado de algún registry.


<h3> Construir contenedor </h3>

`docker container run -p 8080:80 --name myapp my-angular-docker`.

Estamos creando un `container` llamado `myapp` en base a la imagen `my-angular-docker`. 

`-p 8080:80`: Nuestro `container` necesita ser accesible fuera de docker. Entonces las solicitudes hechas por el puerto 8080 de nuestro server host, serán escuchadas por el puerto 80 de nuestro container. Recordemos que nuestro container `myapp` tiene un server NGINX con un Angular app hosted, la cual es accesible por el 80. 

Asegúrese que su container está funcionando correctamente, `docker ps` mostrará los containers corriendo y sus status. Bueno, si no hay ningún problema, abrir tu browser favorito y acceder a tu container `http://localhost:8080`.

Si quieres detener el container solo debes `docker stop <NAME>` para nuestro caso sería `docker stop myapp`, una vez detenido el container, no deberías ser capaz de acceder tu app desde el browser, hasta que vuelvas a activarlo. Para hacer eso, basta con `docker start myapp`. 

![Alt Text](https://media.giphy.com/media/9Jcw5pUQlgQLe5NonJ/giphy.gif)

Gracias por su atención, estamos haciendo un gran esfuerzo para que la comunidad hispano-parlante también pueda colaborar con contenidos. Los invito a seguirme en twitter y también en comunidades como [GDG Pura vida](https://www.meetup.com/gdg-costarica) donde regularmente se hacen eventos gratuitos para la comunidad.