Continuous Delivery en profundidad: pipelines

Continuous Delivery en profundidad: pipelines
Sin comentarios Facebook Twitter Flipboard E-mail

Articulo original publicado en el blog de Stratio.

En esta primera entrega, explicaremos cómo usar los pipelines de Jenkins con Stratio Big Data para obtener un rastreo de ciclo de vida completo, desde el equipo de desarrollo al entorno de productivo final.

Durante el “Lunch & Learn” sobre Continuos Delivery de Stratio vimos algunos de los problemas y ahora trataremos de explicarlos para que podáis comprender sin problema la naturaleza de los principales bugs y la solución que hemos implementado (algo que explicaremos en la segunda parte).

Los pipelines son código

Cada uno de nuestros pipeline está en un grupo privado de github bajo Stratio’s organization repo, donde contamos con varios elementos:

  • l.groovy
  • libvars.groovy
  • libpipeline.groovy
  • dev-project.groovy

l.groovy es el directorio principal para los métodos compartidos y se utiliza para analizar los archivos, comprobar el código, hacer compilaciones, ejecutar pruebas y crear imágenes de docker. Más de 70 métodos, la mayoría privados. El pipeline de Jenkins nos permite cargarlo de forma automática desde un repositorio jenkins interno, pero para hacerlo más fácil, nos saltamos esa opción y dejamos el archivo en github.

libvars.groovy es el directorio para las variable compartidas. Groovy permite variables sin tipo, pero algunas son con tipo para tener un mejor mantenimiento. Algunas de esas variables son constantes, comos las urls (internal nexus, gitolite o registro de docker), canales en slack y versiones predeterminadas.

libpipeline.groovy es el método principal. Decidirá qué tipo de operaciones se van a llevar a cabo en la tarea actual. Volveremos a hablar sobre este archivo más adelante.

dev-project.groovy es el verdadero pipeline. Verdadero porque carga los tres archivos anteriores, establece los valores de las variables e invoca el método principal previo. A modo de ejemplo, podemos echarle un vistazo a uno de los proyectos en código abierto de Stratio (Stratio Crossdata) que viene con comentarios sobre su objetivo:



import groovy.transform.Field

@Field String lib

node('master') { //Common element load
    def cdpwd = pwd().split('/').reverse()[0].replaceAll('@.*', '')
    lib = load "../${cdpwd}@script/l.groovy"
    l.vars = load "../${cdpwd}@script/libvars.groovy"
    l.pipeline = load "../${cdpwd}@script/libpipeline.groovy"
}

// Some metadata for checking out, identifying and messaging abouts warnings/errors
l.v.MODULE = 'crossdata' 
l.v.MAIL = 'crossdata@stratio.com'
l.v.REPO = 'Stratio/crossdata'
l.v.ID = 'xd'
l.v.SLACKTEAM = 'stratiocrossdata'
l.v.FAILFAST = true 

// Stratio is polyglot, and so are its developments. 
// We need to know what build tool we have to use
l.v.BUILDTOOL = 'maven' 

// Should we deploy to sonatype oss repository (so maven artifacts become public)
l.v.FOSS = true 

// Each PR gets statuses, as soon as each run action passes or fails
l.v.PRStatuses = ['Compile', 'Unit Tests', 'Integration Tests', 'Code Quality'] 

l.v.MERGETIMEOUT = 70  // Timeous for each kind of operation we could perform
l.v.PRTIMEOUT = 30
l.v.RELEASETIMEOUT = 30
l.v.SQUASHTIMEOUT = 15

l.v.MERGEACTIONS = { // If our git hook sent a payload related to a PR being merged 
                  l.doBuild()

                  parallel(UT: {
                     l.doUnitTest()
                  }, IT: {
                     l.doIntegrationTest()
                  }, failFast: l.v.FAILFAST)

                  l.doPackage() //Might be a tgz, deb, jar, war
                  // java-scaladocs are published to our s3 bucket 
                  // (such as http://stratiodocs.s3-website-us-east-1.amazonaws.com/cassandra-lucene-index/3.0.6.1/)
                  l.doDoc() 

                  parallel(CQ: {
                     // Static code analysis with Sonarqube 
                     // and coveralls.io (for some FOSS projects)
                     l.doCodeQuality() 
                  }, DEPLOY: {
                     l.doDeploy()
                  }, failFast: l.v.FAILFAST)

                  // And push it to our internal docker registry
                  //, for a later usage in tests and demos
                  l.doDockerImage() 
                  // A Marathon cluster deploys the previously build image
                  l.doMarathonInstall('mc1') 
                  l.doAcceptanceTests(['basic', 'auth', cassandra', 'elasticsearch', 'mongodb', mesos', 'yarn'])
                 }

l.v.PRACTIONS = { // If our git hook sent a payload about a PR being opened or synced
               l.doBuild()

               parallel(UT: {
                  l.doUnitTest()
               }, IT: {
                  l.doIntegrationTest()
               }, failFast: l.v.FAILFAST)

               l.doCodeQuality()
               // We deploy a subset of our wannabe packages to an staging repo            
               l.doStagePackage()
               // Work as packer, building a temporal docker image, so a container can be used for testing 
               l.doPackerImage() 
               l.doAcceptanceTests(['basic', 'auth', cassandra', 'elasticsearch', 'mongodb', mesos', 'yarn'])
              }

l.v.BRANCHACTIONS = { // We could receive a hook signalling a branch to be forged
                   l.doBranch()
                  }

l.v.RELEASEACTIONS = { // So we could release a final version
                    l.doRelease()
                    l.doDoc()
                    l.prepareForNextCycle()
                    // This time the image is the real deal
                    // It will end @ Docker Hub (https://hub.docker.com/r/stratio/)
                    l.doDockerImage() 

                    // Deploying again, to a production Marathon cluster
                    l.doMarathonInstall('mc2')
                    // Let the world know a new version is released, and spread its changelog
                    l.doReleaseMail() 
                   }

l.v.SQUASHACTIONS = {
                   // Currently just checks a PR statuse, rebases it, 
                   // invokes l.v.PRACTIONS, and merges the PR
                   l.doSquash() 
                  }

l.pipeline.roll()

Volviendo al libpipeline.groovy, podemos ver cómo se utilizan algunas de las variables previamente configuradas:

Algunas de las opciones que no se mencionan son las más brillantes: Antes de realizar los tests de integración y aceptación, se seleccionan, ejecutan y configuran varias imágenes de docker. Cuando finalizan los tests se destruyen los contenedores, pudiendo disfrutar de un entorno limpio para hacer pruebas.

Como ya te podrás imaginar, se pueden consultar tanto los repositorios públicos como los privados en diferentes proveedores de gits (github, gitlab, bitbucket). Podemos trabajar con maven y hacer proyectos.

Y como la mayoría de los elementos pueden ser definidos por cada equipo de desarrollo, algunos elementos pueden leerse desde cada repositorio git:

Javier Delgado es evangelista no oficial del uso de Jenkins para tareas continuas (inspection, testing, delivery), apasionado de la automatización y orador en diferentes conferencias sobre estas materias. Ingeniero informático y fiel seguidor del aprendizaje continuo, actualmente trabaja como ingeniero DevOps en Stratio, compañía de big data española-americana pionera en ofrecer a grandes empresas una transformación digital completa en torno a sus datos a través de un único producto.

Comentarios cerrados
Inicio