Integration von Ansible mit Jenkins: Automatisierung Ihrer CI/CD-Pipeline

Integrieren Sie Ansible mit Jenkins, um Playbooks aus CI/CD-Pipelines auszuführen, SSH-Anmeldeinformationen zu verwalten und konsistent bereitzustellen.

Integration von Ansible mit Jenkins: Automatisierung Ihrer CI/CD-Pipeline

Die Integration von Ansible mit Jenkins ermöglicht Ihrer CI/CD-Pipeline, ein Artefakt zu erstellen, Tests durchzuführen und mit denselben Playbooks bereitzustellen, die Sie auch außerhalb von Jenkins verwenden. Die größte Herausforderung besteht darin, Anmeldeinformationen, Inventar und Playbook-Ausführung zu verknüpfen, ohne die Pipeline in einen Haufen von Shell-Befehlen zu verwandeln.

Diese Anleitung zeigt einen praktischen Aufbau: Jenkins orchestriert die Pipeline, während Ansible die Bereitstellung und Konfiguration auf den Zielhosts übernimmt. Die Beispiele gehen von SSH-basierten Linux-Bereitstellungen aus, aber das gleiche Muster funktioniert mit umgebungsspezifischen Inventaren und Rollen.

Wie Jenkins und Ansible zusammenpassen

Bevor Sie die Pipeline schreiben, trennen Sie die Verantwortlichkeiten:

  • Jenkins: Checkt Code aus, erstellt Artefakte, führt Tests durch, sammelt Logs und entscheidet, wann Bereitstellungsphasen ausgeführt werden.
  • Ansible: Verbindet sich mit Zielhosts, kopiert Artefakte, schreibt Konfigurationen, verwaltet Dienste und hält Bereitstellungsschritte idempotent.

Warum Jenkins mit Ansible integrieren?

Diese Aufteilung gibt Ihrem Team eine klare Grenze:

  1. Jenkins speichert den Release-Ablauf in einer Jenkinsfile.
  2. Ansible speichert Infrastrukturaktionen in Playbooks und Rollen.
  3. Inventardateien entscheiden, welche Hosts zu Staging, Produktion oder anderen Umgebungen gehören.
  4. Jenkins-Anmeldeinformationen schützen SSH-Schlüssel, Vault-Passwörter und Tokens.

Voraussetzungen für die Integration

Bevor Sie beginnen, stellen Sie sicher, dass Sie Folgendes haben:

  • Einen funktionierenden Jenkins-Controller und mindestens einen Agenten, der Bereitstellungsjobs ausführen kann.
  • Ansible, installiert auf dem Jenkins-Agenten, der ansible-playbook ausführt.
  • Zielmaschinen, die von diesem Agenten über SSH erreichbar sind.
  • Ein dediziertes SSH-Schlüsselpaar für die Automatisierung.
  • Anwendungscode, Inventar, Playbooks und Rollen in der Versionskontrolle.

Einrichten von Jenkins für Ansible

Jenkins benötigt zwei Dinge, bevor es Ansible sicher ausführen kann: die Ansible-Laufzeit auf einem Agenten und Anmeldeinformationen für die Zielhosts.

1. Installieren des Ansible-Plugins

Sie können ansible-playbook direkt über einen Shell-Schritt aufrufen, aber das Jenkins-Ansible-Plugin kann die Konfiguration und Ausgabe einfacher handhabbar machen.

  1. Navigieren Sie zu Manage Jenkins > Manage Plugins.
  2. Gehen Sie zum Tab Available und suchen Sie nach "Ansible".
  3. Wählen Sie das "Ansible"-Plugin aus und klicken Sie auf Install without restart oder Download now and install after restart.

2. Konfigurieren von SSH-Anmeldeinformationen

Ansible verwendet SSH, um eine Verbindung zu Zielservern herzustellen. Speichern Sie den privaten Schlüssel in Jenkins-Anmeldeinformationen und nicht im Repository.

  1. Gehen Sie zu Manage Jenkins > Manage Credentials.
  2. Wählen Sie den von Ihren Jobs verwendeten Anmeldeinformationsspeicher aus.
  3. Klicken Sie auf Add Credentials.
  4. Wählen Sie SSH Username with private key.
  5. Legen Sie eine stabile ID fest, z. B. ansible-ssh-key.
  6. Geben Sie den SSH-Benutzernamen ein, der auf den Zielmaschinen verwendet wird.
  7. Fügen Sie den privaten Schlüssel hinzu und speichern Sie die Anmeldeinformationen.

Tipp: Best Practices für die Schlüsselverwaltung

  • Hartcodieren Sie niemals private SSH-Schlüssel in einer Jenkinsfile, einem Inventar oder einem Playbook.
  • Verwenden Sie dedizierte Automatisierungsschlüssel, keine persönlichen Schlüssel.
  • Rotieren Sie Schlüssel nach einem Zeitplan, der der Sicherheitsrichtlinie Ihres Teams entspricht.

Entwerfen Ihrer Jenkins-Pipeline mit Ansible

Verwenden Sie eine deklarative Pipeline in einer Jenkinsfile, damit die Bereitstellungslogik zusammen mit dem Anwendungscode überprüft wird.

Grundlegende Pipelinestruktur

Hier ist ein kompaktes Beispiel für einen Build, ein Staging-Deployment und ein gated Production-Deployment:

// Jenkinsfile
pipeline {
    agent any

    environment {
        // Definieren Sie bei Bedarf Umgebungsvariablen
        ANSIBLE_HOST_KEY_CHECKING = 'False' // Seien Sie vorsichtig in der Produktion, bevorzugen Sie known_hosts
    }

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

        stage('Build Application') {
            // Diese Phase könnte ein JAR, WAR, Docker-Image usw. erstellen.
            // Beispiel: Erstellen eines Spring Boot JAR
            steps {
                sh 'mvn clean package'
            }
        }

        stage('Run Unit Tests') {
            steps {
                sh 'mvn test'
            }
        }

        stage('Deploy to Staging') {
            steps {
                script {
                    // Verwenden Sie den SSH-Agenten, um den privaten Schlüssel für Ansible verfügbar zu machen
                    sshagent(credentials: ['ansible-ssh-key']) {
                        // Ansible-Playbook ausführen
                        sh 'ansible-playbook -i inventory/staging.ini playbooks/deploy_app.yml \
                            -e "app_version=$(cat target/VERSION)"'
                    }
                }
            }
        }

        stage('Deploy to Production') {
            // Diese Phase erfordert möglicherweise eine manuelle Genehmigung
            input {
                message "Bereitstellung für die Produktion fortsetzen?"
                ok "In Produktion bereitstellen"
            }
            steps {
                script {
                    sshagent(credentials: ['ansible-ssh-key']) {
                        sh 'ansible-playbook -i inventory/production.ini playbooks/deploy_app.yml \
                            -e "app_version=$(cat target/VERSION)"'
                    }
                }
            }
        }
    }

    post {
        always {
            echo 'Pipeline beendet.'
        }
        success {
            echo 'Pipeline erfolgreich!'
            // slackSend channel: '#deployments', message: "Bereitstellung erfolgreich: ${env.BUILD_URL}"
        }
        failure {
            echo 'Pipeline fehlgeschlagen!'
            // slackSend channel: '#deployments', message: "Bereitstellung fehlgeschlagen: ${env.BUILD_URL}"
        }
    }
}

Wichtige Details zur Pipeline

  • agent any funktioniert für eine Demo. Verwenden Sie in der Produktion ein Agenten-Label, das garantiert, dass Ansible installiert ist.
  • sshagent(credentials: ['ansible-ssh-key']) macht den privaten Schlüssel nur innerhalb dieses Blocks verfügbar.
  • ansible-playbook -i inventory/staging.ini playbooks/deploy_app.yml hält die Zielumgebung explizit.
  • Der input-Block für die Produktion fügt ein manuelles Gate vor einer sensiblen Bereitstellung hinzu.
  • Vermeiden Sie es, die Host-Key-Überprüfung in der Produktion zu deaktivieren. Verwalten Sie stattdessen known_hosts auf dem Jenkins-Agenten.

Beispiel-Ansible-Playbook: Bereitstellen einer Webanwendung

Hier sind ein vereinfachtes inventory/staging.ini und playbooks/deploy_app.yml für eine Java-Webanwendung. Ihr Artefaktname, Dienstmanager und Pfade können abweichen.

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: Webanwendung bereitstellen
  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: Sicherstellen, dass das Anwendungsverzeichnis existiert
      ansible.builtin.file:
        path: "{{ app_path }}"
        state: directory
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: '0755'

    - name: Anwendungs-JAR auf das Ziel kopieren
      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: App-Dienst neu starten

    - name: Sicherstellen, dass die systemd-Dienstdatei existiert
      ansible.builtin.template:
        src: templates/my-webapp.service.j2
        dest: /etc/systemd/system/{{ app_name }}.service
        owner: root
        group: root
        mode: '0644'
      notify: App-Dienst neu starten

    - name: Sicherstellen, dass der App-Dienst gestartet und aktiviert ist
      ansible.builtin.systemd:
        name: "{{ app_name }}"
        state: started
        enabled: yes

  handlers:
    - name: App-Dienst neu starten
      ansible.builtin.systemd:
        name: "{{ app_name }}"
        state: restarted
        daemon_reload: yes

templates/my-webapp.service.j2 (Systemd-Dienstvorlage)

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

Dieses Playbook erstellt das App-Verzeichnis, kopiert das erstellte JAR, schreibt eine systemd-Unit und startet den Dienst. Der Handler startet den Dienst nur neu, wenn sich das Artefakt oder die Unit-Datei ändert.

Best Practices und Tipps

  • Halten Sie Playbooks idempotent, sodass ein erneutes Ausführen einer fehlgeschlagenen Pipeline keinen zusätzlichen Schaden verursacht.
  • Speichern Sie Vault-Passwörter in Jenkins-Anmeldeinformationen und übergeben Sie sie über eine temporäre Passwortdatei oder eine genehmigte Anmeldeinformationsbindung.
  • Organisieren Sie die Bereitstellungslogik in Rollen, z. B. webserver, database und app_deploy.
  • Verwenden Sie separate Inventare oder group_vars für Staging und Produktion.
  • Führen Sie Ansible auf dedizierten Jenkins-Agenten aus, nicht auf dem Controller.
  • Verwenden Sie -v oder -vv nur, wenn Sie mehr Details benötigen; vermeiden Sie es, Geheimnisse in Logs preiszugeben.
  • Testen Sie Playbooks, bevor Jenkins sie ausführt, insbesondere für Produktionsrollen.

Wann Sie einen Fachmann hinzuziehen sollten

Ziehen Sie einen Jenkins- oder Plattformadministrator hinzu, wenn die Pipeline in der Produktion bereitstellt, gemeinsame Anmeldeinformationen verwaltet, auf viele Hosts schreibt oder Auditkontrollen benötigt. Die Handhabung von Anmeldeinformationen, die Überprüfung von Host-Schlüsseln und das Rollback-Verhalten verdienen eine zweite Überprüfung vor dem ersten Produktionseinsatz.

Fazit

Verwenden Sie Jenkins für die Orchestrierung und Ansible für den Bereitstellungsstatus. Bewahren Sie Anmeldeinformationen in Jenkins auf, halten Sie Playbooks in der Versionskontrolle, machen Sie das Inventar explizit und testen Sie dieselben Ansible-Befehle außerhalb der Pipeline, bevor Sie die Produktionsbereitstellung automatisieren.