
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 , , , 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 . 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 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 “«.
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 jenkinsAsí 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 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 -gEsta 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 . Todos los fuentes están abiertos y se encuentran en .
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 . Pero de esto hablaré en el futuro.
Fuente: habr.com
