Integrazione di Ansible con Jenkins: Automatizzare la pipeline CI/CD

Integra Ansible con Jenkins per eseguire playbook da pipeline CI/CD, gestire credenziali SSH e distribuire in modo coerente.

Integrazione di Ansible con Jenkins: Automatizzare la tua pipeline CI/CD

Integrare Ansible con Jenkins permette alla tua pipeline CI/CD di creare un artefatto, eseguire test e distribuirlo con gli stessi playbook che usi al di fuori di Jenkins. La sfida principale è collegare credenziali, inventario ed esecuzione dei playbook senza trasformare la pipeline in un ammasso di comandi shell.

Questa guida mostra una configurazione pratica: Jenkins orchestra la pipeline, mentre Ansible gestisce la distribuzione e la configurazione sugli host di destinazione. Gli esempi presuppongono distribuzioni Linux basate su SSH, ma lo stesso schema funziona con inventari e ruoli specifici per ambiente.

Come si integrano Jenkins e Ansible

Prima di scrivere la pipeline, separa le responsabilità:

  • Jenkins: Fa il checkout del codice, crea artefatti, esegue test, raccoglie log e decide quando eseguire le fasi di distribuzione.
  • Ansible: Si connette agli host di destinazione, copia artefatti, scrive configurazioni, gestisce servizi e mantiene idempotenti i passaggi di distribuzione.

Perché integrare Jenkins con Ansible?

Questa suddivisione offre al tuo team un confine netto:

  1. Jenkins memorizza il flusso di rilascio in un Jenkinsfile.
  2. Ansible memorizza le azioni infrastrutturali in playbook e ruoli.
  3. I file di inventario decidono quali host appartengono a staging, produzione o altri ambienti.
  4. Le credenziali di Jenkins proteggono chiavi SSH, password Vault e token.

Prerequisiti per l'integrazione

Prima di iniziare, assicurati di avere:

  • Un controller Jenkins funzionante e almeno un agente in grado di eseguire lavori di distribuzione.
  • Ansible installato sull'agente Jenkins che esegue ansible-playbook.
  • Macchine di destinazione raggiungibili da quell'agente tramite SSH.
  • Una coppia di chiavi SSH dedicata per l'automazione.
  • Codice applicativo, inventario, playbook e ruoli in un sistema di controllo versione.

Configurazione di Jenkins per Ansible

Jenkins necessita di due cose prima di poter eseguire Ansible in sicurezza: il runtime Ansible su un agente e le credenziali per gli host di destinazione.

1. Installare il plugin Ansible

Puoi chiamare ansible-playbook direttamente da un passaggio shell, ma il plugin Ansible di Jenkins può semplificare la gestione della configurazione e dell'output.

  1. Vai su Gestisci Jenkins > Gestisci Plugin.
  2. Vai alla scheda Disponibili e cerca "Ansible".
  3. Seleziona il plugin "Ansible" e clicca Installa senza riavvio o Scarica ora e installa dopo il riavvio.

2. Configurare le credenziali SSH

Ansible utilizzerà SSH per connettersi ai server di destinazione. Memorizza la chiave privata nelle credenziali di Jenkins anziché nel repository.

  1. Vai su Gestisci Jenkins > Gestisci Credenziali.
  2. Seleziona il repository di credenziali utilizzato dai tuoi lavori.
  3. Clicca Aggiungi Credenziali.
  4. Scegli Nome utente SSH con chiave privata.
  5. Imposta un ID stabile, ad esempio ansible-ssh-key.
  6. Inserisci il nome utente SSH utilizzato sulle macchine di destinazione.
  7. Aggiungi la chiave privata e salva la credenziale.

Suggerimento: Buone pratiche per la gestione delle chiavi

  • Non codificare mai le chiavi private SSH in un Jenkinsfile, inventario o playbook.
  • Utilizza chiavi di automazione dedicate, non chiavi personali.
  • Ruota le chiavi secondo una pianificazione che corrisponda alla politica di sicurezza del tuo team.

Progettare la pipeline Jenkins con Ansible

Utilizza una Pipeline Dichiarativa in un Jenkinsfile in modo che la logica di distribuzione venga revisionata insieme al codice dell'applicazione.

Struttura base della pipeline

Ecco un esempio compatto per una build, distribuzione su staging e distribuzione su produzione con approvazione:

// Jenkinsfile
pipeline {
    agent any

    environment {
        // Definisci le variabili d'ambiente se necessario
        ANSIBLE_HOST_KEY_CHECKING = 'False' // Attenzione in produzione, preferisci known_hosts
    }

    stages {
        stage('Checkout Sorgente') {
            steps {
                git 'https://tuo-scm-url/tuo-repo.git'
            }
        }

        stage('Build Applicazione') {
            // Questa fase potrebbe creare un JAR, WAR, immagine Docker, ecc.
            // Esempio: crea un JAR Spring Boot
            steps {
                sh 'mvn clean package'
            }
        }

        stage('Esegui Test Unitari') {
            steps {
                sh 'mvn test'
            }
        }

        stage('Distribuisci su Staging') {
            steps {
                script {
                    // Utilizza l'agente SSH per rendere disponibile la chiave privata ad Ansible
                    sshagent(credentials: ['ansible-ssh-key']) {
                        // Esegui il playbook Ansible
                        sh 'ansible-playbook -i inventory/staging.ini playbooks/deploy_app.yml \n                            -e "app_version=$(cat target/VERSION)"'
                    }
                }
            }
        }

        stage('Distribuisci su Produzione') {
            // Questa fase potrebbe richiedere approvazione manuale
            input {
                message "Procedere con la distribuzione in Produzione?"
                ok "Distribuisci in Produzione"
            }
            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 terminata.'
        }
        success {
            echo 'Pipeline completata con successo!'
            // slackSend channel: '#deployments', message: "Distribuzione riuscita: ${env.BUILD_URL}"
        }
        failure {
            echo 'Pipeline fallita!'
            // slackSend channel: '#deployments', message: "Distribuzione fallita: ${env.BUILD_URL}"
        }
    }
}

Dettagli chiave della pipeline

  • agent any funziona per una demo. In produzione, utilizza un'etichetta agente che garantisca l'installazione di Ansible.
  • sshagent(credentials: ['ansible-ssh-key']) espone la chiave privata solo all'interno di quel blocco.
  • ansible-playbook -i inventory/staging.ini playbooks/deploy_app.yml mantiene esplicito l'ambiente di destinazione.
  • Il blocco input per la produzione aggiunge un gate manuale prima di una distribuzione sensibile.
  • Evita di disabilitare il controllo della chiave host in produzione. Gestisci invece known_hosts sull'agente Jenkins.

Esempio di playbook Ansible: Distribuzione di un'applicazione web

Ecco un inventory/staging.ini e playbooks/deploy_app.yml semplificati per un'applicazione web Java. Il nome dell'artefatto, il gestore dei servizi e i percorsi potrebbero differire.

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: Distribuisci Applicazione 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: Assicura che la directory dell'applicazione esista
      ansible.builtin.file:
        path: "{{ app_path }}"
        state: directory
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: '0755'

    - name: Copia il JAR dell'applicazione sulla destinazione
      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: riavvia servizio app

    - name: Assicura che il file del servizio systemd esista
      ansible.builtin.template:
        src: templates/my-webapp.service.j2
        dest: /etc/systemd/system/{{ app_name }}.service
        owner: root
        group: root
        mode: '0644'
      notify: riavvia servizio app

    - name: Assicura che il servizio app sia avviato e abilitato
      ansible.builtin.systemd:
        name: "{{ app_name }}"
        state: started
        enabled: yes

  handlers:
    - name: riavvia servizio app
      ansible.builtin.systemd:
        name: "{{ app_name }}"
        state: restarted
        daemon_reload: yes

templates/my-webapp.service.j2 (Template del servizio Systemd)

[Unit]
Description={{ app_name }} Applicazione
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

Questo playbook crea la directory dell'app, copia il JAR creato, scrive un'unità systemd e avvia il servizio. Il handler riavvia il servizio solo quando l'artefatto o il file dell'unità cambiano.

Buone pratiche e suggerimenti

  • Mantieni i playbook idempotenti in modo che rieseguire una pipeline fallita non crei danni aggiuntivi.
  • Memorizza le password Vault nelle credenziali di Jenkins e passale tramite un file password temporaneo o un binding di credenziali approvato.
  • Organizza la logica di distribuzione in ruoli, come webserver, database e app_deploy.
  • Utilizza inventari separati o group_vars per staging e produzione.
  • Esegui Ansible su agenti Jenkins dedicati, non sul controller.
  • Usa -v o -vv solo quando hai bisogno di maggiori dettagli; evita di far trapelare segreti nei log.
  • Testa i playbook prima che Jenkins li esegua, specialmente per i ruoli di produzione.

Quando consultare un professionista

Coinvolgi un amministratore Jenkins o di piattaforma quando la pipeline distribuisce in produzione, gestisce credenziali condivise, scrive su molti host o necessita di controlli di audit. La gestione delle credenziali, la verifica della chiave host e il comportamento di rollback meritano una seconda revisione prima della prima esecuzione in produzione.

Conclusione

Usa Jenkins per l'orchestrazione e Ansible per lo stato della distribuzione. Mantieni le credenziali in Jenkins, i playbook nel controllo versione, rendi l'inventario esplicito e testa gli stessi comandi Ansible al di fuori della pipeline prima di automatizzare la distribuzione in produzione.