El mundo de la integración continua fue concebido con la idea de generar artefactos de software que se pudieran compilar, es decir, que a través de un proceso de compilación resultara un archivo binario que se pudiera desplegar y correr en algún servidor. No obstante con el reciente boom de los lenguajes de scripting (lenguajes que no generan archivos binarios) este concepto se rompe un poco y los DevOps del mundo se han puesto imaginativos y a falta de un archivo binario deciden “dockerizar” las aplicaciones, entonces ahora en lugar de un archivo binario tenemos una imagen de docker que al correr sirve nuestra aplicación.
El propósito de este post es ejemplificar cómo podemos dockerizar una aplicación hecha con node.js para que posteriormente pueda ser utilizada en un proceso de integración continua o distribuida a través de un artifactory.
Tomemos como ejemplo la siguiente aplicacion sencilla:
package.json
{ "name": "docker_web_app", "version": "1.0.0", "description": "Node.js en Docker", "author": "RicardoGeek", "main": "server.js", "scripts": { "start": "node server.js" }, "dependencies": { "express": "~4.13.3" } }
server.js
'use strict'; const express = require('express'); const PORT = 8080; const HOST = '0.0.0.0'; const app = express(); app.get('/', (req, res) => { res.send('Hola Docker\n'); }); app.listen(PORT, HOST); console.log(`Aplicación corriendo en: http://${HOST}:${PORT}`);
Creando el Dockerfile
Ahora es necesario que en el mismo directorio creemos un archivo llamado Dockerfile (sin extensión) y lo abramos en nuestro editor favorito.
La primera línea de este Dockerfile sera:
FROM node
Esta es una imagen de docker disponible en el dockerhub que ya viene con node, npm y yarn listos para usarse, esta línea descarga la imagen y con las siguientes la vamos a modificar un poco para que sirva nuestra mini-aplicación, en otras palabras usamos como base la imagen de node.
En la siguiente línea definimos el WORKSPACE que es por default el directorio (dentro del S.O de la imagen) sobre el cual trabajaremos.
WORKDIR /usr/src/app
En las siguientes líneas comenzamos el proceso de instalar nuestra aplicación en la imagen base:
COPY package.json . RUN npm install
El comando COPY copia cosas de nuestro sistema operativo al sistema operativo virtualizado, en este caso copiamos el archivo package.json al workspace antes definido. Y luego instalamos las dependencias con npm install.
Una vez instaladas las dependencias copiamos el resto de la aplicación.
COPY . .
Y le indicamos que puerto exponer, es decir en que puerto del container debería escucharse la aplicación.
EXPOSE 8080
Y finalmente le decimos cómo correr la aplicación:
CMD [ "npm", "start" ]
Al final de todo su Dockerfile deberia verse asi:
FROM node:boron WORKDIR /usr/src/app COPY package.json . RUN npm install COPY . . EXPOSE 8080 CMD [ "npm", "start" ]
En el mismo directorio se debe crear un archivo .dockerignore que sirve para que el comando COPY no copie a la imagen cosas que son propias de nuestro ambiente de desarrollo local no dockerizado y para paz mental 🙂
node_modules npm-debug.log
Construir la imagen
Ahora construir dicha imagen es tan simple como abrir una terminal, navegar al directorio de nuestra app donde esta el Dockerfile y correr el siguiente comando:
docker build -t <prefijo o usuario>/node-web-app .
al terminar su imagen debería poder ser listada con docker images.
$ docker images # Example REPOSITORY TAG ID CREATED node node 5dfb64df5fb4 1 minute ago <usuario>/node-web-app latest 943ofkbdsdvv 1 minute ago
Correr la aplicacion dockerizada
Para correr la imagen corremos el siguiente comando en la terminal
docker run -p 80:8080 -d <usuario>/node-web-app
la etiqueta -p indica que el puerto 8080 del container tiene que escucharse en el puerto 80 de nuestro sistema operativo anfitrión. y la etiqueta -d indica que se corra en modo “daemon” de lo contrario tendríamos que tener la terminal abierta siempre que se quiera correr.
para probar basta con poner un curl en la terminal:
$ curl -i localhost HTTP/1.1 200 OK X-Powered-By: Express Content-Type: text/html; charset=utf-8 Content-Length: 12 Date: Sun, 02 Jun 2013 03:53:22 GMT Connection: keep-alive Hola Docker
Ahora… manos al codigo!
[…] Especifico vs Propósito General: Los agents pueden ser específicos; por ejemplo diferentes agents pueden correr proyectos en java 8 y otros distintos corren proyectos en java 7. Cuando se trata de agents de propósito general, estos actuan como hosts de Docker y compilan basándose en un flujo dentro de un contenedor docker […]