Cómo recopilar proyectos en Jenkins si se necesitan muchos entornos diferentes

Cómo recopilar proyectos en Jenkins si se necesitan muchos entornos diferentes

En Habr hay muchos artículos sobre Jenkins, pero son pocos los que describen un ejemplo del funcionamiento de Jenkins y los agentes Docker. Todas las herramientas populares de construcción de proyectos como Drone.io, Bitbucket Pipeline, GitLab, GitHub actions y otros, pueden construir todo en contenedores. ¿Pero qué hay de Jenkins?

Hoy en día existe una solución para el problema: Jenkins 2 sabe trabajar maravillosamente con agentes Docker. En este artículo quiero compartir mi experiencia y mostrarte cómo puedes hacerlo tú mismo.

¿Por qué me ocupé de resolver este problema?

Como en la empresa Citronium utilizamos muchas tecnologías diferentes, en la máquina de construcción debemos mantener diferentes versiones de Node.JS, Gradle, Ruby, JDK y más. Pero a menudo no se puede evitar los conflictos de versiones. Sí, tendrás razón si dices que existen varios gestores de versiones como nvm, rvm, pero no todo es tan sencillo con ellos y estas soluciones tienen problemas:

  • grandes volúmenes de runtimes que los desarrolladores olvidan limpiar;
  • hay conflictos entre diferentes versiones de los mismos runtimes;
  • cada desarrollador necesita un conjunto diferente de componentes.

Existen otros problemas, pero prefiero hablarte de la solución.

Jenkins en Docker

Como ahora Docker está bien integrado en el ámbito del desarrollo, casi todo se puede ejecutar mediante Docker. Mi solución es que Jenkins esté en Docker y pueda ejecutar otros contenedores Docker. Esta cuestión se planteó por primera vez en 2013 en el artículo “Docker can now run within Docker«.

En resumen, simplemente es necesario instalar Docker en el contenedor de trabajo y montar el archivo /var/run/docker.sock.

Aquí tienes un ejemplo de Dockerfile que resultó para Jenkins.

FROM jenkins/jenkins:lts

USER root

RUN apt-get update && 

apt-get -y install apt-transport-https 
     ca-certificates 
     curl 
     gnupg2 
     git 
     software-properties-common && 
curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey && 
add-apt-repository 
   "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") 
   $(lsb_release -cs) 
   stable" && 
apt-get update && 
apt-get -y install docker-ce && 
usermod -aG docker jenkins

RUN curl -L https://github.com/docker/compose/releases/download/1.25.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose 

RUN apt-get clean autoclean && apt-get autoremove —yes && rm -rf /var/lib/{apt,dpkg,cache,log}/

USER jenkins

Así obtuvimos un contenedor Docker que puede ejecutar comandos Docker en la máquina host.

Configuración de la construcción

No hace mucho, Jenkins obtuvo la capacidad de describir sus reglas utilizando Pipeline de sintaxis, lo que permite cambiar el script de construcción con bastante facilidad y almacenarlo en un repositorio.

Por eso, coloquemos en el propio repositorio un Dockerfile especial que contenga todas las bibliotecas necesarias para la construcción. De esta manera, el desarrollador puede preparar un entorno reproducible y no será necesario pedir al OPS que instale una versión específica de Node.JS en el host.

FROM node:12.10.0-alpine

RUN npm install yarn -g

Esta imagen de construcción es adecuada para la mayoría de las aplicaciones de Node.JS. ¿Y si, por ejemplo, necesita una imagen para un proyecto de JVM con un escáner Sonar incluido? Ustedes mismos pueden elegir los componentes necesarios para la construcción.

FROM adoptopenjdk/openjdk12:latest

RUN apt update 
    && apt install -y 
        bash unzip wget

RUN mkdir -p /usr/local/sonarscanner 
    && cd /usr/local/sonarscanner 
    && wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-3.3.0.1492-linux.zip 
    && unzip sonar-scanner-cli-3.3.0.1492-linux.zip 
    && mv sonar-scanner-3.3.0.1492-linux/* ./ 
    && rm sonar-scanner-cli-3.3.0.1492-linux.zip 
    && rm -rf sonar-scanner-3.3.0.1492-linux 
    && ln -s /usr/local/sonarscanner/bin/sonar-scanner /usr/local/bin/sonar-scanner

ENV PATH $PATH:/usr/local/sonarscanner/bin/
ENV SONAR_RUNNER_HOME /usr/local/sonarscanner/bin/

Hemos descrito el entorno de construcción, pero, ¿qué tiene que ver Jenkins con esto? Los agentes de Jenkins pueden trabajar con estas imágenes de Docker y realizar la construcción en su interior.

stage("Construir proyecto") {
    agent {
        docker {
            image "project-build:${DOCKER_IMAGE_BRANCH}"
            args "-v ${PWD}:/usr/src/app -w /usr/src/app"
            reuseNode true
            label "build-image"
        }
    }
    steps {
        sh "yarn"
        sh "yarn build"
    }
}

Directiva agente utiliza la propiedad docker, donde puede especificar:

  • el nombre del contenedor de construcción de acuerdo con su política de nomenclatura;
  • los argumentos necesarios para ejecutar el contenedor de construcción, donde en nuestro caso montamos el directorio actual como un directorio dentro del contenedor.

Y ya en los pasos de construcción especificamos qué comandos ejecutar dentro del agente Docker de construcción. Esto puede ser cualquier cosa, de esta manera también realizo el despliegue de aplicaciones utilizando ansible.

A continuación, quiero mostrar un Jenkinsfile general que puede construir una aplicación sencilla de Node.JS.

def DOCKER_IMAGE_BRANCH = ""
def GIT_COMMIT_HASH = ""

pipeline { 
    options {
        buildDiscarder(
            logRotator(
                artifactDaysToKeepStr: "",
                artifactNumToKeepStr: "",
                daysToKeepStr: "",
                numToKeepStr: "10"
            )
        )
        disableConcurrentBuilds()
    }

    agent any

    stages {

        stage("Preparar imagen de construcción") {
            steps {
                sh "docker build -f Dockerfile.build . -t project-build:${DOCKER_IMAGE_BRANCH}"
            }
        }

        stage("Construir proyecto") {
            agent {
                docker {
                    image "project-build:${DOCKER_IMAGE_BRANCH}"
                    args "-v ${PWD}:\/usr\/src\/app -w \/usr\/src\/app"
                    reuseNode true
                    label "build-image"
                }
            }
            steps {
                sh "yarn"
                sh "yarn build"
            }
        }

    post {
        always {
            step([$class: "WsCleanup"])
            cleanWs()
        }
    }

}

¿Qué ha salido de esto?

Gracias a este enfoque hemos resuelto los siguientes problemas:

  • el tiempo de configuración del entorno de construcción se reduce a 10 - 15 minutos por proyecto;
  • entorno de construcción de la aplicación completamente reproducible, ya que se puede construir de la misma manera en una computadora local;
  • no hay problemas con conflictos de diferentes versiones de herramientas de construcción;
  • siempre un espacio de trabajo limpio, que no se llena.

La solución en sí es simple y obvia, y permite obtener solo ventajas. Sí, el umbral de entrada se ha elevado un poco en comparación con los simples comandos de construcción, pero ahora hay garantía de que siempre se podrá construir, y el desarrollador puede elegir todo lo que necesita para su proceso de construcción.

También puedes utilizar la imagen que he construido Jenkins + Docker. Todos los fuentes están abiertos y se encuentran en rmuhamedgaliev/jemkins_docker.

Durante la escritura del artículo se generó una discusión sobre el uso de agentes en servidores remotos, para no sobrecargar el nodo maestro utilizando el plugin docker-plugin. Pero de esto hablaré en el futuro.

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster