En esta entrada crearemos un sencillo microservicio que tenga dos servicios. El microservicio se hará con Node JS y se implementará Integración Continua y Despliegue Continuo por medio de GitLab. El despliegue se realiza con Heroku.

El repositorio de código se encuentra público en el siguiente enlace:

https://gitlab.com/arturoverbel/microservice-node

Integración Continua

También conocido como CI (Continuous Integration) se centra en combinar los avances individuales de los desarrolladores con más frecuencias, con menos errores y conflictos más fáciles de resolver. 

La Integración Continua se basa en suites de prueba y una ejecución de prueba automatizada. El objetivo es refinar la integración en una tarea de desarrollo repetible que servirá para reducir los costos de construcción y revelar defectos al principio del ciclo.

Despliegue Continuo

El objetivo es entregar el producto al cliente lo antes posible. Lo que puede entenderse como la Integración Continua pero para el despliegue de aplicaciones. El despliegue continuo nos garantiza que cada cambio debe estar disponible para ser desplegado en producción.

Microservicio

El microservicio se realizará con Node usando el framework express. El repositorio se creará en Gitlab.

https://gitlab.com/arturoverbel/microservice-node

Corremos el comando

express --view=twig microservice-node
npm install

Si necesita instalar express o los módulos necesarios, en esta entrada muestro cómo instalar y hacer un microservicio en Node JS.

Creamos el primer endpoint, por método GET, que multiplica dos números:

router.get('/suma/:num1/:num2', function(req, res, next) {
  var num1 = parseInt(req.params.num1);
  var num2 = parseInt(req.params.num2);

  var suma = num1 + num2;

  res.setHeader('Content-Type', 'application/json');
  res.end(JSON.stringify({ result: suma }));
});

Creamos otro pero por el método POST, el cual cuente cuántas veces está una palabra en una frase:

router.post('/count-words', function(req, res, next) {
  var words = req.body.text.split(' ');
  let count = 0;

  words.forEach(function(val){
    if (val == req.body.find) {
      count += 1;
    }
  });

  res.setHeader('Content-Type', 'application/json');
  res.end(JSON.stringify({ result: count }));
});

Y se realiza una página web que consuma estos dos servicios, la página luce como:

Figura 1. Página web que consume los servicios

Test

Se usará la librería de mocha y chai para realizar las pruebas. Se instalan con los siguientes comandos:

npm install mocha
npm install chai
npm install chai-http

Se crea una carpeta en la raiz del proyecto y se crean un archivo para incluir los test:

mkdir test
cd test
touch test.js

En el archivo entonces incluimos el siguiente archivo:

let chai = require('chai');
let chaiHttp = require('chai-http');
let app = require('../app');
let should = chai.should();

chai.use(chaiHttp);

describe("Test Services", function() {

  it("Index response OK", function(done) {
    chai.request(app)
      .get('/')
      .end((err, res) => {
          res.should.have.status(200);
          done();
      });
  });

  it("Add two numbers", function(done) {
    chai.request(app)
      .get('/suma/45/50')
      .end((err, res) => {
          res.should.have.status(200);
          res.body.result.should.be.eql(95);
          done();
      });
  });

  it("Find word on text", function(done) {
    let request = {
    	"text": "hi everyone hi everybody",
    	"find": "hi"
    }
    chai.request(app)
      .post('/count-words')
      .send(request)
      .end((err, res) => {
          res.should.have.status(200);
          res.body.result.should.be.eql(2);
          done();
      });
  });

});

Este archivo corre tres pruebas. En la linea 10 a la linea 17 se preba que la página principal responda con el estado 200.  En la prueba “Add two numbers” comprobamos la suma entre 45 y 50 y la última validamos la petición POST y que cuente las palabras en la frase de prueba.

Por último agregamos en el package.json el comando para correr las pruiebas:

"scripts": {
  "start": "node ./bin/www",
  "test": "mocha"
},

Si corremos los test con:

npm test

Se obtiene el siguiente resultado en consola:

Figura 2. Resultado en consola de correr las pruebas

Si modificamos el archivo de las pruebas. Cambiamos la linea 24 por:

res.body.result.should.be.eql(100);

Y corremos las pruebas obtenemos el siguiente resultado:

Figura 3. Los test fallan cuando los resultados no coinciden

En la figura 3 nos indica que fallaron los test, cuál test falló, qué resultado esperaba y qué recibió. Dicen todo lo que se necesita para realizar las correciones en el código.

Pipelines

Es momento de configurar los pipelines. Gitlab (como muchos otros servicios de repositorios) viene con un sistema de proceso para poder cargar nuestro código en diferentes fases, se le conoce como Pipelines. Las fases que nosotros programemos las correra el repositorio. Aquí es donde implementaremos el CI y el CD. En el pipelines para gitlab le decimos que corra los test cada vez que se hace un push al repositorio, de esta manera comprobamos que la integración de los siguientes commits no afectará al proyecto como tal.

Para configurar el pipelines creamos un archiuvo .gitlab-ci.yml que tendrá el siguiente código:

image: node:latest

stages:
  - build
  - test

cache:
  paths:
    - node_modules/

install_modules:
  stage: build
  script:
    - npm install
  artifacts:
    paths:
        - node_modules/

testing:
  stage: test
  script: npm test

Al subir esta versión podemos ver lo siguiente en nuestro repositorio de Gitlab:

Figura 4. Resultados del pipelines por cada commit

En la figura 4 se observa que al lado de cada commit podemos ver los resultados del CI. Los que están vacios es porque no tenía configurado el pipelines.

Al entrar a CI/CD de Gitlab y hacemos click en el último podemos evidenciar cada fase y sus resultados como en la figura 5.

Figura 5. Fases del pipelines

Despliegue

Por último se realiza el despliegue, si las fases del pipelines termina satisfactoriamente, el proyecto hará un despliegue en los servidores. De esta manera tendremos automatizado la integración, las pruebas y el despliegue.

Existen mcuahs aplicaciones para hacer despliegue de proyectos, acá se usará uno muy fácil de usar llamado Heroku. Creamos una cuenta en este portal y le hacemos clic en crar una app.

Figura 6. Creando un app en Heroku

Ahora nos dirigimos a las configuraciones de la cuenta creamos una nueva autorización

Figura 7. Creando autorización en Heroku

La KEY generada en la autorización la almacenamos y la guardamos en las variable de entorno de Gitlab, para que pueda ser usada en las fases del pipelines, así como lo muestrea la figura 8:

Figura 8. Almacenando la key en el ambiente de gitlab

Ahora solo falta agregar al pipelines una fase más. Agregamos al final del archico .gitlabci.yml las siguientes lineas:

production:
  type: deploy
  stage: production
  image: ruby:latest
  script:
    - apt-get update -qy
    - apt-get install -y ruby-dev
    - gem install dpl
    - dpl --provider=heroku --app=microservice-node --api-key=$HEROKU_API_KEY
  only:
    - master

En esta fase de despliegue se define ña imagen en la cual queremos que corra los scripts deseados para subir nuestro repositorio a Heroku, en nuestro caso necesitamos una imagen de Ruby. Utilizamos la variable de ambiente para obtener la key que almacenamos en las variables de Gitlab.

Al hacer deploy, podemos observar que cada fase están corriendo en Gitlab, como se ve en la figura 9:

Figura 9. Pipeline corriendo en gitlab

Incluso se puede observar en gitlab las comandos y lo sresultados que se corren al hacer deploy, como en la figura 10:

Figura 10. Comandos del CD

Si todo corre bien, si pasa las pruebas, entonces podemos ver en Heroku el siguiente resultado:

Figura 11. Resultados del deploy en Heroku.

Resultado

Y podemos ver el resultado final en el endpoint que nos brinda Heroku:

https://microservice-node.herokuapp.com/

Repositorio del microservicio:

https://gitlab.com/arturoverbel/microservice-node