DEV Community

Uhltak Therestismysecret
Uhltak Therestismysecret

Posted on

Ansible im Alltag: Idempotente Rollen schreiben, die 2 Jahre halten

Hook – Warum Ansible‑Rollen sonst schneller verrotten als ein Obstkorb

Stellen Sie sich vor, Sie legen einen Vorratskeller an und verlassen ihn jedes Jahr für ein Picknick. Beim ersten Mal riecht alles frisch, beim zweiten riecht es nach Feuchtigkeit, beim dritten klingt das Geräusch der Ratten schon aus den Regalen. Genau das passiert mit Ansible‑Rollern, die nicht idempotent geschrieben sind. Sie funktionieren beim ersten Durchlauf, aber bei jeder Änderung – sei es ein neues Paket, ein geändertes Kernel‑Modul oder ein Update Ihrer Cloud‑CLI – brechen sie zusammen. Der Grund liegt nicht an Ansible selbst, sondern an unserer eigenen Nachlässigkeit beim Rollen‑Design. In diesem Artikel zeige ich Ihnen, wie Sie Rollen bauen, die selbst nach zwei Jahren noch „frisch“ sind – und das ohne stundenlange Debug‑Sessions.


Warum Idempotenz das Herzstück von Ansible ist

Erklärung

Idempotenz bedeutet in der IT‑Automatisierung: Ein Vorgang kann beliebig oft ausgeführt werden, ohne das Ergebnis zu ändern, sobald der gewünschte Zustand erreicht ist. Ansible implementiert das per default über das changed‑Flag. Ein Task, der bereits den gewünschten Zustand hat, wird als ok gemeldet und ändert nichts am System.

Beispiel 1 – Paketinstallation mit apt

- name: Installieren von nginx (idempotent)
  apt:
    name: nginx
    state: present
    update_cache: yes
  become: true
Enter fullscreen mode Exit fullscreen mode

Falls nginx bereits installiert ist, gibt das Modul apt changed: false zurück. Das ist das Fundament jeder zuverlässigen Rolle: Jeder Schritt muss prüfen, ob er etwas ändern muss, bevor er etwas unternimmt.

Persönliche Einschätzung

In meinen ersten zehn Jahren als Administrator habe ich unzählige Playbooks gesehen, die ein Paket einfach mit shell: apt-get install -y nginx installieren. Das ist ein Anti‑Pattern: Die Shell‑Kommandos ignorieren den Zustand und produzieren bei jedem Run unnötige Änderungen. Das kostet nicht nur CPU‑Zeit, sondern führt zu unvorhersehbaren Neustarts – und das ist das, worüber Sie in Ihrem Team diskutieren sollten.


Grundlegende Bausteine einer zeitlosen Rolle

Erklärung

Eine robuste Rolle besteht aus vier klar getrennten Bereichen:

  1. Defaults – sichere, überschreibbare Werte.
  2. Variables – projekt‑spezifische Overrides.
  3. Tasks – reine idempotente Aktionen.
  4. Handlers – nur bei Bedarf ausgelöste Neustarts oder Reloads.

Durch diese Trennung bleibt die Rolle flexibel und gleichzeitig stabil.

Beispiel 2 – defaults/main.yml

# defaults/main.yml
nginx_user: www-data
nginx_worker_processes: "auto"
nginx_log_dir: /var/log/nginx
Enter fullscreen mode Exit fullscreen mode

Ein Kollege kann die Werte in group_vars oder host_vars überschreiben, ohne die Rolle selbst zu verändern. Das verhindert „Hard‑Coded‑Schrott“ und erlaubt Ihnen, dieselbe Rolle für 1000 Server zu nutzen.

Persönliche Einschätzung

Ich habe einmal versucht, eine Rolle zu schreiben, die nginx_user fest in root kodierte. Das lief erstmal, bis ein Kunde ein Debian‑System mit www-data verlangte – dann war das erst ein kompletter Ausfall. Nie wieder ein Hard‑Code! Nutzen Sie immer defaults.


Praktische Beispiele – Drei Rollen, die jetzt und in zwei Jahren funktionieren

Beispiel 1 – nginx‑install

tasks/main.yml

- name: Installieren des nginx Pakets
  apt:
    name: nginx
    state: present
  become: true

- name: Sicherstellen, dass der Service läuft und bei Bedarf neu laden
  service:
    name: nginx
    state: started
    enabled: true
  notify: Restart nginx
Enter fullscreen mode Exit fullscreen mode

handlers/main.yml

- name: Restart nginx
  service:
    name: nginx
    state: restarted
Enter fullscreen mode Exit fullscreen mode

Durch das getrennte notify wird nginx nur neu gestartet, wenn vorher etwas geändert wurde – zum Beispiel ein Konfigurations‑File.

Einschätzung: Der größte Zeitgewinn entsteht, wenn Sie immer notify verwenden statt command: systemctl restart nginx im Task selbst. So bleibt die Idempotenz erhalten.


Beispiel 2 – systemd‑service‑template

Manchmal muss man eigene Systemd‑Units bereitstellen. Das geht komplett idempotent mit dem template‑Modul.

- name: Systemd‑Unit bereitstellen
  template:
    src: myservice.service.j2
    dest: /etc/systemd/system/myservice.service
    owner: root
    mode: "0644"
  notify: Reload systemd

- name: Service aktivieren und starten
  systemd:
    name: myservice
    enabled: true
    state: started
Enter fullscreen mode Exit fullscreen mode

myservice.service.j2

[Unit]
Description=Meine Beispiel‑Service
After=network.target

[Service]
ExecStart={{ myservice_exec_path }}
User={{ myservice_user }}
Restart=on-failure

[Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode

Durch das Template können Sie Parameter ändern, ohne die Unit neu zu schreiben. Nur bei einer echten Änderung wird systemd daemon-reload ausgeführt (Reload systemd‑Handler).

Einschätzung: Viele meiner Kunden haben die Unit-Datei per copy statt template bereitgestellt. Das vergaß die notify‑Aufrufe, so dass nach Änderungen ein manueller systemctl daemon-reload nötig war. Mit template ist das automatisch und idempotent.


Beispiel 3 – Ansible‑Galaxy‑Veröffentlichung

Ein oft übersehener Aspekt ist die Versionierung Ihrer Rollen. Nutzen Sie Semantic‑Versionierung (SemVer) und legen Sie ein meta/main.yml an.

# meta/main.yml
galaxy_info:
  author: Max Mustermann
  description: Idempotente nginx‑Installation für Produktionsumgebungen
  license: MIT
  min_ansible_version: "2.9"
  platforms:
    - name: Debian
      versions: ["buster", "bullseye"]
  galaxy_tags: ["nginx", "web", "security"]

version: 1.2.0
Enter fullscreen mode Exit fullscreen mode

Veröffentlichen Sie die Rolle mit:

ansible-galaxy role import mygithubuser myansiblerole
Enter fullscreen mode Exit fullscreen mode

Durch das version‑Feld können Sie später exakt bestimmen, welche Role‑Version in einem Playbook benutzt wird:

- hosts: webservers
  roles:
    - role: mygithubuser.myansiblerole
      version: "1.2.0"
Enter fullscreen mode Exit fullscreen mode

Einschätzung: Ohne Versionsangabe greift das Playbook beim nächsten ansible-galaxy install plötzlich die neuste Version, die möglicherweise Break‑Changes enthält. Ich habe schon Teams erlebt, deren Produktionsumgebungen nach einem galaxy install -r requirements.yml wegen einer einzigen Minor‑Version gescheitert sind.


Häufige Fehler – Was andere immer wieder falsch machen

  1. Shell‑Befehle statt Ansible‑Module – Sie verlieren Idempotenz und werden von Fehlermeldungen überflutet. Beispiel: shell: systemctl restart nginx.
  2. Hard‑Coded Pfad‑Strings – Verwenden Sie Variablen ({{ var_name }}) und defaults.
  3. Handlers vergessen – Wenn Sie einen Service neu starten, tun Sie das immer, auch wenn nichts geändert wurde. Das erzeugt unnötige Downtime.
  4. Keine Tests – Rollen ohne Molecule‑Tests laufen Gefahr, bei zukünftigen Änderungen zu brechen. Schreiben Sie mindestens ein molecule/default‑Scenario.
  5. Versionsmix – Nutzen Sie unterschiedliche Ansible‑Versionen in Ihrer CI‑Pipeline und im Produktivsystem. Das führt zu seltsamen Fehlermeldungen, weil Module unterschiedlich implementiert sind.

Fazit und der nächste konkrete Schritt

Idempotenz ist kein Nice‑to‑have, sondern der Grundpfeiler jeder langlebigen Ansible‑Automatisierung. Wenn Sie heute die drei vorgestellten Beispiele übernehmen, reduzieren Sie nicht nur Ihre Wartungszeit, sondern bauen ein Fundament, das auch nach einem Jahreswechsel noch funktioniert.

Ihr nächster Schritt (in 5 Minuten):

  1. Öffnen Sie ein Repository für Ihre Rollen (z. B. auf GitHub).
  2. Legen Sie eine defaults/main.yml mit mindestens drei Variablen an.
  3. Implementieren Sie die nginx‑install‑Rolle exakt wie im Beispiel – inklusive Handler.
  4. Führen Sie ansible-playbook -i inventory test.yml --check --diff aus, um die Idempotenz zu prüfen.
  5. Committen Sie die Rolle und publishen Sie sie zu Ansible‑Galaxy mit ansible-galaxy role import.

Wenn Sie diese Schritte befolgen, haben Sie innerhalb einer Stunde eine produktionsreife, idempotente Rolle, die Sie in jedem zukünftigen Projekt wiederverwenden können – und das ohne Angst vor dem nächsten „Broken‑Update“. Viel Erfolg beim Rollen‑Bau!

Top comments (0)