jueves, 16 de agosto de 2018

Laboratorio - Integración Contínua (VI)

Sexta entrada en la creación de nuestro laboratorio CI. Recuerdo los pasos:

  1. Instalación de Ubuntu en VMWare Player en Windows 7
  2. Instalación de Jenkins en Ubuntu
  3. Instalación GitLab en Ubuntu
  4. Instalación de Docker Minikube en Ubuntu
  5. Desarrollo de un microservicio en Eclipse Windows (1) y (2)
    1. Angular 6 frontend
    2. SpringBoot 2.x backend
    3. Mongo Replicaset (3 replicas)
  6. Despliegue CI en Minikube utilizando la infraestructura configurada
    1. Pipeline - Jenkinsfile
    2. GitLab webhook
    3. Dockerfile
    4. Deploy y Service yaml
    5. Secret
    6. Ingress

Vayamos pues con la el desarrollo de una aplicación de ejemplo. Lo haremos en al menos dos etapas/entradas, probablemente tres.

En esta primera dejaremos (sexta del total de CI) la aplicación operativa en forma reducida. Sólo la compilaremos y la invocaremos desde "http://localhost:8080".



Estructura base
La aplicación consistirá simplemente en un gestor/administrador de usuarios. Puede usarse, a partir de ahí, como gestor de cualquier otro elemento de cualquier web con formato semejante.

Esta será la estructura de la aplicación de ejemplo para esta primera entrada:

    ci-root

    ci-backend


    ci-frontend

Y este es un resumen del contenido del artefacto único a desplegar:



Veremos más abajo como automatizar la inclusión de los contenidos estáticos de angular en el jar spring boot.

Trabajaremos sobre el root y el backend con eclipse, y con Visual Studio Code sobre el frontend. No obstante para este último habremos de realizar algunas configuraciones desde eclipse. La idea es que el root lo "vea" como módulo maven, por lo que aunque la estructura de carpetas y demás sea la típica de un proyecto angular, incorporará un pom.xml para darle esa característica de proyecto maven.



Root
Desde eclipse creamos un proyecto root maven. Esto se hace indicando en el wizard de creación que el packaging es de tipo pom. Podemos darle el nombre que queramos, en nuestro caso lo llamaremos "ci-root"



Una vez creado podemos borrar la carpeta "src".


Backend
Creamos un proyecto maven ("new maven module") dentro del proyecto ci-root anterior con nombre "ci-backend":





Frontend
El módulo frontend será una aplicación angular. Por ello, primero crearemos dicho proyecto angular y después lo configuraremos como módulo maven sobre el ci-root.

Mediante la consola windows (cmd) vamos al directorio donde tenemos el proyecto root. En dicho directorio lanzamos el siguiente comando:
    ng new ci-frontend

Desde eclipse hacemos un refresh del proyecto ci-root. Veremos que aparece en su interior la carpeta "ci-frontend". Ahora creamos un fichero "pom.xml" dentro del proyecto angular "ci-frontend". Añadimos el siguiente contenido a dicho pom.xml:

    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">     <modelVersion>4.0.0</modelVersion>
        <parent>
    <groupId>es.gincol.blog</groupId>
    <artifactId>ci-root</artifactId>
    <version>1.0.0</version>
        </parent>
        <artifactId>ci-frontend</artifactId>

    </project>

A continuación, abrimos el fichero pom.xml del proyecto ci-root. En la pestaña "overview" pulsamos sobre "Add...", navegamos dentro del poyecto ci-root y seleccionamos el proyecto ci-frontend. Tras aceptar así nos debe quedar:



Ya tenemos los tres proyectos creados, el root como contenedor del backend (springboot) y el frontend (angular). Si a modo de prueba lanzamos un clean install desde eclipse (o un "mvn clean install" desde línea de comandos) veremos la siguiente salida:

    [INFO] ci-root ................................................. SUCCESS [  0.443 s]
    [INFO] ci-frontend ......................................... SUCCESS [  0.888 s]

    [INFO] ci-backend .......................................... SUCCESS [  0.040 s]
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS

    [INFO] ------------------------------------------------------------------------

No obstante aún falta para tener un artefacto completo y usable para desplegar en nuestro laboratorio. Veremos a continuación como "enlazarlos" para que formen un sólo artefacto.

Una nota importante, es que el orden de compilación de los módulos será primero "ci-frontend", segundo "ci-backend". Eso simplemente lo conseguimos con el orden de los modulos en el pom.xml del root.


Configuración extra
Hasta ahora hemos comentado la configuración base. Detallamos ahora la configuración extra para generar un único artefacto y así evitar despliegues del frontend y backend de forma separada.

Para este manual vamos a trabajar con tres entornos, siempre locales. Dejo al margen los entornos corporativos donde vaya a desplegarse la aplicación final. Serán, pues, estos:
  • loc: entorno local de toda la vida. Se ejecutará bien desde eclipse "Run as Spring Boot App", bien con "mvn spring-boot:run", bien con "java -jar ci-backend.jar".
  • des: entorno local con docker. Utilizaremos un docker-compose.yml y lo lanzaremos mediante "docker-compose up -d --build". Usaremos un fichero ".bat" que nos permitirá hacer un lanzamiento sencillo desde eclipse usando profiles maven.
  • dev: entrono minikube en ubuntu CI (usando git, kenkins y gitlab)

Frontend
Hay que hacer algunas modificaciones para que el código angular se integre en el jar del backend sprin boot. Veamos.

package.json
Aquí incluimos varios script's en su correspondiente apartado:
    "build-loc": "ng build --configuration=loc",
    "build-des": "ng build --configuration=des",
    "build-dev": "ng build --configuration=dev",




angular.json
Introducimos una configuración por cada entorno. Para el caso "loc":

     "configurations": {
"loc": {
  "fileReplacements": [
{
  "replace": "src/environments/environment.ts",
  "with": "src/environments/environment.loc.ts"
}
  ],
  "optimization": true,
  "outputHashing": "all",
  "sourceMap": false,
  "extractCss": true,
  "namedChunks": false,
  "aot": true,
  "extractLicenses": true,
  "vendorChunk": false,
  "buildOptimizer": true
},

Lo mismo para "des" y "dev", cambiando "loc" por el correspondiente entorno.


environments

Dentro de la carpeta "environments" crearemos un fichero por cada uno de los tres nuevos entornos.



En el interior, para la prueba final de esta entrada, pondermos por ejemplo para "loc":
export const environment = {
  production: false,
  entorno: 'loc',

};

Lo mismo, pero cambiando el valor de la variable "entorno", lo haremos en los otros ficheros.


Estilos
Importamos el módulo bootstrap 
    npm install --save bootstrap

Lo referenciamos en el fichero angular.json, apartado "projects... styles":




pom.xml
Usaremos el plugin "com.github.eirslett" para la compilación angular. Por extensión no lo copio entero, sólo se ha de resaltar que en el goal "npm", como atributo "arguments" se indica


    <arguments>run-script build-${entorno}</arguments>

donde "entorno" será una variable informada al lanzar el maven install del proyecto root. En concreto se lanzará, para el entorno "loc" de la siguiente forma:

    mvn clean install -Dentorno=loc



Cuando angular reciba el entorno "loc" lanzará el script "npm run-script build-loc" del fichero package.json, el cual a su vez, lanzará el build "ng build --configuration=loc", el cual reemplazará el contenido del fichero "environment.ts" con el contenido del fichero "environment.loc.ts".

Para el resto de entornos, el parámetro "-Dentorno" valdrá los valores correspondientes, "des" ó "dev", produciéndose el efecto correspondiente de reemplazamiento de contenidos.


Backend
Incluimos un par de plugins maven para traernos el resultado de la compilación angular 

El primero será de eliminación de contenidos

<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<filesets>
<fileset>
<directory>${basedir}/src/main/resources/static</directory>
<followSymlinks>false</followSymlinks>
</fileset>
</filesets>
</configuration>
</plugin>

El segundo, de copia de los contenidos angular hacia la carpeta resources:


<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/src/main/resources/static</outputDirectory>
<overwrite>true</overwrite>
<resources>
    <resource>
<directory>${project.parent.basedir}/ci-frontend/dist/ci-frontend</directory>
<filtering>true</filtering>
    </resource>
</resources>
</configuration>
</execution>
</executions>

</plugin>

Con ambos plugins se consigue que los ficheros generados en la carpeta "dist" del build del proyecto angular sean copiados a la carpeta "static" del resources de la aplicación backend, estando ahora disponible como si formase parte de dicha aplicación sping boot.

Cuando se genere el jar correspondiente, contendrá los ficheros estáticos como se ha visto en la captura de más arriba (contenido resumen del jar). De esta forma serán servidos desde el tomcat embebido en spring boot.



Prueba
Como prueba final de esta entrada, haremos correr la aplicación con sólo una página principal con la que poder validar que la compilación por entorno es válida.

Siempre lanzamos la compilación y generación del artefacto desde el proyecto ci-root, desde ahí lanzamos un "mvn clean install -Dentorno=loc". A continuación ejecutamos la aplicación ci-backend como proyecto spring boot y obtenemos (fijaos en "Entorno: loc" del footer):




Ahora lo repetimos con el resto de entornos. Paramos la aplicación y lanzamos "mvn clean install -Dentorno=des". Arrancamos de nuevo ("Entorno: des"):



Y finalmente para "dev" repetimos el proceso. Ahora el comando maven será: "mvn clean install -Dentorno=dev". Arrancamos de nuevo ("Entorno: dev"):






Conclusiones
Bueno, ya tenemos operativa una primer versión de la aplicación en un formato sencillo, pero funcional. Hemos validado que la linea de compilación de un único artefacto desde el ci-root funciona para los tres entornos con sólo cambiar el parámetro "-Dentorn" al ejecutar el comando maven. Ahora ya sabemos que todas las variables que pongamos en un fichero environment-entorno.ts de entorno, reemplazará el que usa realmente la aplicación en runtime, el "environment.ts". 

En resumen, sabemos cómo incluir la parte angular estática dentro del jar de spring boot, y sabemos como generar el jar correspondiente en cada entorno. Basta pasar la variable "-Dentorn=XXX" en el proceso de compilación del entorno que toque.

De momento con esto es suficiente para esta entrada. 

En la siguiente tendremos la aplicación completa, con la gestión de usuarios finalizada. La ejecutaremos en el entorno "loc", el local de toda la vida, el que hemos usado en esta entrada.

Mi objetivo será el de detallar también la ejecución en el entorno "des" (local con docker y docker-compose). Dejando para una posterior entrada la ejecución final en el entorno "dev", minikube con el uso, ahora si, del entorno de Integración contínua que hemos instalado y configurado en entradas anteriores.


Código
El estado actual del código lo tenéis publicado en mi cuenta de gitlab "https://gitlab.com/gincol-blog/ci-root.git"




Anterior 

No hay comentarios:

Publicar un comentario