Primeros ejemplos
Creación del primer proyecto freestyle
Creamos el primer proyecto de Jenkins. Comprueba que Jenkins puede llamar a docker. Para ello crea un nuevo proyecto tipo freestyle.
En la sección Build, añade un bloque Execute shell. Pega estos comandos:
whoami
git --version
java -version
docker -v
Guarda los cambios. Haz clic sobre Build now. Haz clic sobre la bolita verde para ver el la salida por consola.
Por consola se visualiza el resultado de ejecutar los comandos dentro del contenedor. Como puedes ver, git y java están instalados, venían ya en la imagen de jenkins:lts de la que hemos partido en la definición del Dockerfile. Además, docker también está disponible, se ha instalado correctamente mediante la definición incluida en el Dockerfile.
Creación del primer pipeline
Pipeline sobre el nodo master
Creamos el primer proyecto de Jenkins tipo pipeline. Vamos a darle el nombre hello-maven-pipeline-on-master-node. Este pipeline se ejecutará sobre el nodo (agent) master, es decir, sobre el mismo contenedor que está ejecutando Jenkins.
Copia la siguiente definición de pipeline en el bloque Pipeline, Pipeline script.
pipeline {
agent any
tools {
maven 'Default Maven' (1)
}
stages {
stage('Build') {
steps {
sh '''
java -version
mvn -v
''' (2)
}
}
}
}
| 1 | Utiliza la instalación de Maven que hemos hecho en la sección anterior, y le dimos el nombre Default Maven. |
| 2 | En la shell ejecutará los comandos para mostrar las versiones de Java y Maven. |
Haz clic sobre Build now, y visualiza la consola. Como parte de la salida, se debe visualizar algo así (puede que los números de versiones varíen):
[Pipeline] sh
+ java -version
openjdk version "11.0.14" 2022-01-18
OpenJDK Runtime Environment Temurin-11.0.14+9 (build 11.0.14+9)
OpenJDK 64-Bit Server VM Temurin-11.0.14+9 (build 11.0.14+9, mixed mode)
+ mvn -v
Apache Maven 3.8.4 (9b656c72d54e5bacbed989b64718c159fe39b537)
Maven home: /var/jenkins_home/tools/hudson.tasks.Maven_MavenInstallation/Default_Maven
Java version: 11.0.14, vendor: Eclipse Adoptium, runtime: /opt/java/openjdk
Default locale: en, platform encoding: UTF-8
OS name: "linux", version: "5.11.0-1029-gcp", arch: "amd64", family: "unix"
|
Lanzar las construcciones de proyectos en el nodo máster puede acarrear problemas de seguridad. De hecho, Jenkins avisa de que esta configuración es inadecuada, y recomienda configurar agentes independientes donde lanzar las ejecuciones de los proyectos.
Fig. 4. Aviso de evitar ejecuciones en el nodo máster de Jenkins
|
Pipeline con un contenedor como agente
Otra alternativa es que el pipeline se ejecute, en lugar de en el nodo master, en un nodo tipo "agente" que se creará ex profeso a partir de un contenedor Docker disponible de DockerHub.
A continuación creamos el segundo proyecto de Jenkins tipo pipeline. Vamos a darle el nombre hello-maven-pipeline-on-container-node
pipeline {
agent {
docker {
image 'maven:3.6-openjdk-8' (1)
args '-v $HOME/.m2:/root/.m2'
}
}
stages {
stage('Build') {
steps {
sh '''
java -version
mvn -v
'''
}
}
}
}
| 1 | Entre las imágenes de Maven disponibles están 3.6-openjdk-11, 3.6-openjdk-15, etc |
Te dará error. En la máquina Jenkins, sobre el S.O. host, hay que abrir permisos en el socket de Docker para que desde dentro del contenedor Jenkins permita crear otros contenedores hermanos. Para ello modifica los permisos así:
sudo chmod 666 /var/run/docker.sock
Tras ello deben construirse correctamente. Sin embargo, abrir permisos a ese archivo supone ciertos problemas de seguridad: Avoid workarounds like this which could be a big potential security threat. The result of your chmod practically gives all local users read and write permissions to the docker-socket which allows anyone to interfere with your docker images. (fuente).
|
Para que tras reiniciar la máquina se mantengan los permisos del socket de Docker: Crea el archivo
Por último, dale los permisos adecuados al archivo
Tras ello reinicia la máquina. Al volver a entrar, comprueba que el socket de Docker tiene los permisos adecuados:
|
Otros ejemplos similares con contenedores NodeJS están disponibles en la documentación de Jenkins
Usando varios contenedores como agente
Es habitual tener varias tecnologías en un mismo proyecto. Por ejemplo, un repositorio puede tener tanto un back-end basado en Java como un front-end basado en JavaScript. Combinar Docker y Pipeline permite usar diferentes agentes en diferentes fases (stages) del pipeline:
pipeline {
agent none
stages {
stage('Back-end') {
agent {
docker { image 'maven:3.8.1-adoptopenjdk-11' }
}
steps {
sh 'mvn --version'
}
}
stage('Front-end') {
agent {
docker { image 'node:16.13.1-alpine' }
}
steps {
sh 'node --version'
}
}
}
}
Conexión con la máquina de despliegue
Para automatizar el despliegue sobre la instancia que tenemos creada para ello, deberás permitir que Jenkins ejecute comandos sobre la máquina de despliegue a través de SSH. Para ello, la instancia Jenkins debe poder conectarse a la instancia de despliegue mediante una conexión SSH basada en autenticación por pareja de claves pública/privada, que ha demostrado ser más seguro sobre la autenticación estándar de nombre de usuario/contraseña.
Para ello, los pasos que se detallan a continuación permiten:
-
generar una nueva pareja de claves que usaremos para el despliegue,
-
copiar la clave pública generada en la instancia de despliegue,
-
y por último probar que la conexión se realiza correctamente.
Ejecuta los siguientes pasos:
Generar la nueva pareja de claves de despliegue
-
Conecta por SSH a la máquina Jenkins:
ssh ubuntu@instancia-jenkins
-
Crea la carpeta donde se va a guardar la nueva pareja de claves:
mkdir /home/ubuntu/jenkins_home/.ssh -
Crea una pareja de claves ssh de despliegue:
ssh-keygen -t rsa -b 4096 -
Cuando pida el nombre, escribe el nuevo nombre id_rsa_deploy junto con la ubicación donde Jenkins va a buscar las claves de forma predeterminada, que es:
/home/ubuntu/jenkins_home/.ssh/id_rsa_deploy -
Por último, deja la contraseña en blanco (pulsa ENTER):
Enter passphrase (empty for no passphrase):
Esto crea la clave privada en /home/ubuntu/jenkins_home/.ssh/id_dsa_deploy y una clave pública asociada en /home/ubuntu/jenkins_home/.ssh/id_dsa_deploy.pub. Esta nueva pareja de claves la usaremos exclusivamente para el despliegue de nuestros proyectos. Al haberlos guardado en la carpeta /home/ubuntu/jenkins_home/ los archivos están accesibles dentro del contenedor, porque recuerda que esa carpeta la habíamos mapeado con la carpeta /var/jenkins_home del contenedor.
Copiar la clave pública a la instancia de despliegue
-
Muestra el contenido de la clave pública:
cat /home/ubuntu/jenkins_home/.ssh/id_rsa_deploy.pub -
Copia el contenido: con el ratón, selecciona el contenido de la clave, desde “ssh-rsa” hasta el final, y pulsa ENTER (o CTRC+C)
|
Debido a que algunos terminales añaden saltos delinea al copiar texto desde el terminal, como ocurre con cloud shell de GCP, es recomendable copiar el contenido de la clave pública en cualquier editor de texto "plano" (Notepad++, Sublime, VS Code, etc) y eliminar los saltos de línea, si los hubiera. |
-
Ahora pégalo en tu PC, lo necesitaremos más adelante.
-
Desconecta de la máquina Jenkins:
exit -
Conecta por ssh a la instancia de despliegue
-
Edita el archivo
authorized_keys:nano home/ubuntu/.ssh/authorized_keys -
Ese archivo ya tenía una clave pública, la correspondiente a tu pareja de claves personal que inyectamos en la creación de la instancia con Terraform (por eso has podido conectar por ssh a esa máquina). Pega el contenido de la clave pública de despliegue. Ahora debe tener 2 claves públicas.
-
Ya puedes desconectar de la instancia de despliegue.
Prueba de la conexión desde jenkins a despliegue
Vamos a probar que funciona:
-
Conecta de nuevo a la instancia jenkins y prueba la conexión ssh a la instancia de despliegue. Recuerda que puesto que Jenkins se está ejecutando como un contenedor, debes probar la conexión ssh desde dentro del contenedor:
docker exec -it jenkins-docker ssh ubuntu@instancia_deploy -i /var/jenkins_home/.ssh/id_rsa_deploy
En el comando anterior:
-
docker exec -itindica ejecutar un comando desde dentro del contenedor -
jenkins-dockeres el nombre del contenedor -
ssh ubuntu@instancia_deploy -i /var/jenkins_home/.ssh/id_rsa_deployes el comando a ejecutar en el contenedor. En este caso,sshcon el parámetro-i …para indica la clave privada que debe usar para conectar. -
Recuerda que
/var/jenkins_homees la carpeta HOME del usuario jenkins dentro del contenedor, y jenkins es el usuario del contenedor que ejecuta Jenkins.
-
La primera vez que realizas una conexión ssh desde un usuario en una máquina origen a una destino, te pregunta si deseas almacenar la clave de host de destino en la lista de hosts conocidos (
known_hosts) de tu máquina origen. Contesta:yes
-
Si todo ha ido bien, la conexión se ha debido realizar. Sal con
exit. Si no ha sido así, verifica que la ruta al archivo de la clave privada es correcta, y que el nombre de la máquina de despliegue es correcto. -
Comprueba que la clave de host de la máquina de destino (despliegue) se ha guardado en la máquina origen (jenkins) en el archivo
~/.ssh/known_hostsdel usuario que ha ejecutado el comando ssh, en nuestro caso, del usuario jenkins de contenedor:docker exec -it jenkins-docker cat /var/jenkins_home/.ssh/known_hosts
-
Puedes comprobar también el contenido de known_hosts en el archivo
/home/ubuntu/jenkins_home/.ssh/known_hosts, ya que recuerda que hay un volumen mapeado entre la carpeta local/home/ubuntu/jenkins_homey la carpeta del contenedor/var/jenkins_home.
-
Ahora que la conexión por SSH entre la máquina Jenkins y la máquina de despliegue es correcta, vamos a hacer que Jenkins automatice la ejecución de comandos sobre la máquina de despliegue: entra en Jenkins y añade el siguiente comando al proyecto hello_docker existente, sustituyendo MAQUINA_DEPLOY por el nombre DNS de la máquina de despliegue.
ssh -i ~/.ssh/id_rsa_deploy ubuntu@MAQUINA_DEPLOY "pwd && ls -la"
Como aclaración de este comando:
-
el parámetro
-iindica la clave privada que queremos usar en la conexión ssh -
"pwd && ls -la"son comandos básicos que ejecuta sobre la máquina remota. Hemos indicado estos comandos simplemente para probar que la conexión se realiza correctamente.
Tras ejecutar el proyecto en Jenkins, el resultado debe ser correcto.