Integración de Ansible con Jenkins: Automatizando su Pipeline de CI/CD
Integra Ansible con Jenkins para ejecutar playbooks desde pipelines CI/CD, gestionar credenciales SSH y realizar despliegues consistentes.
Integración de Ansible con Jenkins: Automatizando tu Pipeline CI/CD
Integrar Ansible con Jenkins permite que tu pipeline CI/CD construya un artefacto, ejecute pruebas y lo despliegue usando los mismos playbooks que utilizas fuera de Jenkins. El principal desafío es conectar credenciales, inventario y ejecución de playbooks sin convertir el pipeline en un montón de comandos shell.
Esta guía muestra una configuración práctica: Jenkins orquesta el pipeline, mientras que Ansible maneja el despliegue y la configuración en los hosts de destino. Los ejemplos asumen despliegues SSH en Linux, pero el mismo patrón funciona con inventarios y roles específicos para cada entorno.
Cómo encajan Jenkins y Ansible
Antes de escribir el pipeline, separa las responsabilidades:
- Jenkins: Revisa el código, construye artefactos, ejecuta pruebas, recopila logs y decide cuándo se ejecutan las etapas de despliegue.
- Ansible: Se conecta a los hosts de destino, copia artefactos, escribe configuraciones, gestiona servicios y mantiene los pasos de despliegue idempotentes.
¿Por qué integrar Jenkins con Ansible?
Esta división le da a tu equipo un límite claro:
- Jenkins almacena el flujo de lanzamiento en un
Jenkinsfile. - Ansible almacena las acciones de infraestructura en playbooks y roles.
- Los archivos de inventario deciden qué hosts pertenecen a staging, producción u otros entornos.
- Las credenciales de Jenkins protegen claves SSH, contraseñas de Vault y tokens.
Prerrequisitos para la Integración
Antes de comenzar, asegúrate de tener:
- Un controlador Jenkins en funcionamiento y al menos un agente que pueda ejecutar trabajos de despliegue.
- Ansible instalado en el agente Jenkins que ejecutará
ansible-playbook. - Máquinas de destino accesibles desde ese agente a través de SSH.
- Un par de claves SSH dedicado para la automatización.
- Código de la aplicación, inventario, playbooks y roles en control de versiones.
Configurando Jenkins para Ansible
Jenkins necesita dos cosas antes de poder ejecutar Ansible de forma segura: el runtime de Ansible en un agente y las credenciales para los hosts de destino.
1. Instalar el Plugin de Ansible
Puedes llamar a ansible-playbook directamente desde un paso shell, pero el plugin de Ansible para Jenkins puede facilitar la configuración y la gestión de la salida.
- Navega a
Manage Jenkins>Manage Plugins. - Ve a la pestaña
Availabley busca "Ansible". - Selecciona el plugin "Ansible" y haz clic en
Install without restartoDownload now and install after restart.
2. Configurar Credenciales SSH
Ansible usará SSH para conectarse a los servidores de destino. Almacena la clave privada en las credenciales de Jenkins, no en el repositorio.
- Ve a
Manage Jenkins>Manage Credentials. - Selecciona el almacén de credenciales utilizado por tus trabajos.
- Haz clic en
Add Credentials. - Elige
SSH Username with private key. - Establece un ID estable, por ejemplo
ansible-ssh-key. - Ingresa el nombre de usuario SSH utilizado en las máquinas de destino.
- Agrega la clave privada y guarda la credencial.
Consejo: Mejores Prácticas para la Gestión de Claves
- Nunca codifiques claves privadas SSH en un Jenkinsfile, inventario o playbook.
- Usa claves de automatización dedicadas, no claves personales.
- Rota las claves según un cronograma que coincida con la política de seguridad de tu equipo.
Diseñando tu Pipeline de Jenkins con Ansible
Usa un Pipeline Declarativo en un Jenkinsfile para que la lógica de despliegue sea revisada junto con el código de la aplicación.
Estructura Básica del Pipeline
Aquí tienes un ejemplo compacto para una construcción, despliegue en staging y despliegue en producción con compuerta:
// Jenkinsfile
pipeline {
agent any
environment {
// Define variables de entorno si es necesario
ANSIBLE_HOST_KEY_CHECKING = 'False' // Ten cuidado en producción, prefiere known_hosts
}
stages {
stage('Checkout Source') {
steps {
git 'https://your-scm-url/your-repo.git'
}
}
stage('Build Application') {
// Esta etapa podría construir un JAR, WAR, imagen Docker, etc.
// Ejemplo: construir un JAR de Spring Boot
steps {
sh 'mvn clean package'
}
}
stage('Run Unit Tests') {
steps {
sh 'mvn test'
}
}
stage('Deploy to Staging') {
steps {
script {
// Usa el agente SSH para que la clave privada esté disponible para Ansible
sshagent(credentials: ['ansible-ssh-key']) {
// Ejecuta el playbook de Ansible
sh 'ansible-playbook -i inventory/staging.ini playbooks/deploy_app.yml \n -e "app_version=$(cat target/VERSION)"'
}
}
}
}
stage('Deploy to Production') {
// Esta etapa podría requerir aprobación manual
input {
message "¿Proceder con el despliegue a Producción?"
ok "Desplegar a Producción"
}
steps {
script {
sshagent(credentials: ['ansible-ssh-key']) {
sh 'ansible-playbook -i inventory/production.ini playbooks/deploy_app.yml \n -e "app_version=$(cat target/VERSION)"'
}
}
}
}
}
post {
always {
echo 'Pipeline finalizado.'
}
success {
echo '¡Pipeline exitoso!'
// slackSend channel: '#deployments', message: "Despliegue exitoso: ${env.BUILD_URL}"
}
failure {
echo '¡Pipeline fallido!'
// slackSend channel: '#deployments', message: "Despliegue fallido: ${env.BUILD_URL}"
}
}
}
Detalles Clave del Pipeline
agent anyfunciona para una demo. En producción, usa una etiqueta de agente que garantice que Ansible esté instalado.sshagent(credentials: ['ansible-ssh-key'])expone la clave privada solo dentro de ese bloque.ansible-playbook -i inventory/staging.ini playbooks/deploy_app.ymlmantiene explícito el entorno de destino.- El bloque
inputde producción agrega una compuerta manual antes de un despliegue sensible. - Evita deshabilitar la verificación de clave de host en producción. Gestiona
known_hostsen el agente Jenkins en su lugar.
Ejemplo de Playbook de Ansible: Desplegando una Aplicación Web
Aquí tienes un inventory/staging.ini y playbooks/deploy_app.yml simplificados para una aplicación web Java. El nombre de tu artefacto, el gestor de servicios y las rutas pueden diferir.
inventory/staging.ini
[web_servers]
web1.example.com
web2.example.com
[database_servers]
db1.example.com
[all:vars]
ansible_user=ubuntu
playbooks/deploy_app.yml
---
- name: Desplegar Aplicación Web
hosts: web_servers
become: yes
vars:
app_name: my-webapp
app_path: /opt/{{ app_name }}
app_port: 8080
app_version: "{{ app_version | default('1.0.0') }}"
tasks:
- name: Asegurar que el directorio de la aplicación existe
ansible.builtin.file:
path: "{{ app_path }}"
state: directory
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0755'
- name: Copiar el JAR de la aplicación al destino
ansible.builtin.copy:
src: "target/{{ app_name }}-{{ app_version }}.jar"
dest: "{{ app_path }}/{{ app_name }}.jar"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0644'
notify: reiniciar servicio de la aplicación
- name: Asegurar que el archivo de servicio systemd existe
ansible.builtin.template:
src: templates/my-webapp.service.j2
dest: /etc/systemd/system/{{ app_name }}.service
owner: root
group: root
mode: '0644'
notify: reiniciar servicio de la aplicación
- name: Asegurar que el servicio de la aplicación está iniciado y habilitado
ansible.builtin.systemd:
name: "{{ app_name }}"
state: started
enabled: yes
handlers:
- name: reiniciar servicio de la aplicación
ansible.builtin.systemd:
name: "{{ app_name }}"
state: restarted
daemon_reload: yes
templates/my-webapp.service.j2 (Plantilla de servicio systemd)
[Unit]
Description={{ app_name }} Application
After=network.target
[Service]
User={{ ansible_user }}
ExecStart=/usr/bin/java -jar {{ app_path }}/{{ app_name }}.jar --server.port={{ app_port }}
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
Este playbook crea el directorio de la aplicación, copia el JAR construido, escribe una unidad systemd e inicia el servicio. El handler reinicia el servicio solo cuando el artefacto o el archivo de unidad cambian.
Mejores Prácticas y Consejos
- Mantén los playbooks idempotentes para que reejecutar un pipeline fallido no cause daños adicionales.
- Almacena las contraseñas de Vault en las credenciales de Jenkins y pásalas a través de un archivo de contraseña temporal o un enlace de credenciales aprobado.
- Organiza la lógica de despliegue en roles, como
webserver,databaseyapp_deploy. - Usa inventarios separados o
group_varspara staging y producción. - Ejecuta Ansible en agentes Jenkins dedicados, no en el controlador.
- Usa
-vo-vvsolo cuando necesites más detalle; evita filtrar secretos en los logs. - Prueba los playbooks antes de que Jenkins los ejecute, especialmente para roles de producción.
Cuándo Consultar a un Profesional
Busca la ayuda de un administrador de Jenkins o plataforma cuando el pipeline despliegue en producción, gestione credenciales compartidas, escriba en muchos hosts o necesite controles de auditoría. El manejo de credenciales, la verificación de clave de host y el comportamiento de reversión merecen una segunda revisión antes de la primera ejecución en producción.
Conclusión
Usa Jenkins para la orquestación y Ansible para el estado del despliegue. Mantén las credenciales en Jenkins, los playbooks en control de versiones, el inventario explícito y prueba los mismos comandos de Ansible fuera del pipeline antes de automatizar el despliegue en producción.