TL;DR — Kurzzusammenfassung

Meistern Sie Ansible-Playbooks und Roles fuer die Infrastrukturautomatisierung. Inventar, Module, Jinja2, ansible-vault, Fehlerbehandlung und LAMP-Deployment.

ANSIBLE — PLAYBOOKS, ROLES UND VAULT Inventar INI / YAML group_vars/ host_vars/ Playbook plays / tasks handlers / vars Ansible-Engine Roles / Module Vault / Jinja2 SSH webservers nginx / php dbservers mysql / backup loadbalancers haproxy / certs ansible-vault Verschluesselte Secrets AES-256 Roles tasks / handlers templates / files defaults / meta Agentenloser Push-Modus — das Inventar steuert alles

Infrastruktur in grossem Massstab ohne Automatisierung zu verwalten fuehrt unweigerlich zu Konfigurationsdrift, vergessenen Schritten und Vorfaellen mitten in der Nacht. Ansible-Playbooks und -Roles bieten eine strukturierte, wiederholbare Moeglichkeit, genau festzulegen, wie jeder Server aussehen soll, und diesen Zustand idempotent ueber die gesamte Flotte durchzusetzen. Dieser Leitfaden deckt den vollstaendigen Ansible-Arbeitsablauf ab — von der Inventarverwaltung und Playbook-Architektur ueber Roles, Jinja2-Templates, ansible-vault und Fehlerbehandlung bis hin zu einem LAMP-Deployment als abschliessendes Praxisbeispiel.

Voraussetzungen

  • Ansible auf einem Steuerknoten installiert (Ubuntu 22.04+ oder jede Linux/macOS-Maschine)
  • SSH-Schluessel-basierter Zugriff auf einen oder mehrere verwaltete Hosts
  • Python 3 auf allen Ziel-Hosts (auf den meisten Distributionen vorinstalliert)
  • Grundlegende Kenntnisse der YAML-Syntax und der Linux-Kommandozeile

Ansible-Architektur: Das Push-Modell

Ansible verwendet eine agentenlose Push-Architektur. Ihr Steuerknoten sendet Konfigurationen an verwaltete Knoten ueber SSH. Es laeuft kein Daemon, es gibt keine Zustandsdatenbank und es muss nichts auf den Zielen installiert werden ausser einem funktionierenden Python-Interpreter.

Die Ausfuehrungskette lautet: Inventar → Playbook → Module.

  1. Ansible liest das Inventar, um festzustellen, welche Hosts anvisiert werden sollen
  2. Es analysiert das Playbook, um eine geordnete Liste von Tasks zu erstellen
  3. Fuer jeden Task kopiert Ansible ein kleines Python-Modul via SSH auf den Remote-Host, fuehrt es aus, erfasst das Ergebnis und entfernt das Modul
  4. Die Ergebnisse (ok / changed / failed / skipped) werden aggregiert und im Terminal angezeigt

Inventarverwaltung

INI- vs. YAML-Format

# inventory.ini
[webservers]
web1.example.com
web2.example.com ansible_port=2222

[dbservers]
db1.example.com
db2.example.com

[production:children]
webservers
dbservers

[all:vars]
ansible_user=deployer
ansible_ssh_private_key_file=~/.ssh/id_ed25519
# inventory.yml
all:
  vars:
    ansible_user: deployer
    ansible_ssh_private_key_file: ~/.ssh/id_ed25519
  children:
    webservers:
      hosts:
        web1.example.com:
        web2.example.com:
          ansible_port: 2222
    dbservers:
      hosts:
        db1.example.com:
        db2.example.com:

group_vars und host_vars

projekt/
  inventory.ini
  group_vars/
    all.yml
    webservers.yml
    dbservers.yml
  host_vars/
    web1.example.com.yml
  site.yml
# group_vars/webservers.yml
http_port: 80
https_port: 443
document_root: /var/www/html
worker_processes: 4

Dynamisches Inventar mit dem aws_ec2-Plugin

# aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
  - us-east-1
filters:
  instance-state-name: running
  "tag:Environment": production
keyed_groups:
  - key: tags.Role
    prefix: role

Playbook-Struktur

- name: Webserver konfigurieren
  hosts: webservers
  become: true
  vars:
    app_name: myapp
    app_port: 8080

  tasks:
    - name: nginx installieren
      apt:
        name: nginx
        state: present
        update_cache: true

    - name: nginx-Konfiguration aus Template deployen
      template:
        src: templates/nginx.conf.j2
        dest: /etc/nginx/nginx.conf
        owner: root
        group: root
        mode: "0644"
      notify: nginx neu starten

    - name: Sicherstellen, dass nginx laeuft und aktiviert ist
      service:
        name: nginx
        state: started
        enabled: true

  handlers:
    - name: nginx neu starten
      service:
        name: nginx
        state: restarted

Bedingungen mit when

- name: apache2 installieren (nur Debian)
  apt:
    name: apache2
    state: present
  when: ansible_os_family == "Debian"

- name: httpd installieren (nur Red Hat)
  yum:
    name: httpd
    state: present
  when: ansible_os_family == "RedHat"

Schleifen mit loop

- name: Benoetierte Pakete installieren
  apt:
    name: "{{ item }}"
    state: present
  loop:
    - nginx
    - curl
    - ufw
    - fail2ban

- name: Anwendungsverzeichnisse erstellen
  file:
    path: "{{ item.path }}"
    state: directory
    owner: "{{ item.owner }}"
    mode: "{{ item.mode }}"
  loop:
    - { path: /var/www/myapp, owner: www-data, mode: "0755" }
    - { path: /var/log/myapp, owner: www-data, mode: "0750" }

Jinja2-Templates

{# templates/nginx.conf.j2 #}
user www-data;
worker_processes {{ worker_processes | default('auto') }};

http {
    server {
        listen {{ http_port }};
        server_name {{ server_name }};
        root {{ document_root }};

        {% if enable_ssl | default(false) %}
        listen {{ https_port }} ssl;
        ssl_certificate {{ ssl_cert_path }};
        {% endif %}
    }
}

Tags fuer selektive Ausfuehrung

# Nur Tasks mit dem Tag 'config' ausfuehren
ansible-playbook site.yml --tags config

# Tasks mit dem Tag 'install' ueberspringen
ansible-playbook site.yml --skip-tags install

Wesentliche Module

ModulZweckHauptparameter
apt / yumPaketverwaltungname, state, update_cache
copyStatische Dateien kopierensrc, dest, owner, mode
templateJinja2-Templates deployensrc, dest, owner, mode
serviceSystemdienste verwaltenname, state, enabled
userBenutzerkonten verwaltenname, groups, shell, state
fileVerz., Symlinks erstellenpath, state, owner, mode
lineinfileZeilen in Dateien bearbeitenpath, regexp, line, state
commandBefehl ausfuehrenargv, creates
shellShell-Befehle mit Pipescmd, chdir
gitRepository klonen/aktualisierenrepo, dest, version
docker_containerDocker-Container verwaltenname, image, state, ports
debugVariablenwerte ausgebenmsg, var

Role-Struktur

ansible-galaxy init roles/webserver
roles/webserver/
  tasks/
    main.yml
  handlers/
    main.yml
  templates/
    nginx.conf.j2
  files/
    index.html
  vars/
    main.yml
  defaults/
    main.yml
  meta/
    main.yml

defaults vs. vars

  • defaults/main.yml: Niedrige Prioritaet. Aufrufer koennen diese Werte leicht ueberschreiben.
  • vars/main.yml: Hohe Prioritaet. Diese ueberschreiben die meisten anderen Variablenquellen.
# roles/webserver/defaults/main.yml
http_port: 80
https_port: 443
worker_processes: auto
document_root: /var/www/html
enable_ssl: false
# roles/webserver/tasks/main.yml
- name: nginx installieren
  apt:
    name: nginx
    state: present
    update_cache: true

- name: Dokumentstammverzeichnis erstellen
  file:
    path: "{{ document_root }}"
    state: directory
    owner: www-data
    group: www-data
    mode: "0755"

- name: nginx-Konfiguration deployen
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
  notify: nginx neu starten

Roles in einem Playbook verwenden

- name: Web-Tier konfigurieren
  hosts: webservers
  become: true
  roles:
    - role: common
    - role: security
    - role: webserver
      vars:
        http_port: 80
        enable_ssl: true
        document_root: /var/www/myapp

Ansible Galaxy fuer Community-Roles

ansible-galaxy install geerlingguy.mysql
ansible-galaxy install -r requirements.yml
# requirements.yml
roles:
  - name: geerlingguy.nginx
    version: "3.2.0"
collections:
  - name: community.docker
    version: ">=3.4.0"

Ansible Vault fuer Geheimnisverwaltung

ansible-vault create group_vars/all/vault.yml
ansible-vault edit group_vars/all/vault.yml
ansible-vault encrypt secrets.yml
ansible-vault encrypt_string 'MeinGeheimnis123' --name 'db_password'
# group_vars/all/vars.yml (Klartext, im Git)
db_host: db1.example.com
db_password: "{{ vault_db_password }}"
# group_vars/all/vault.yml (verschluesselt, im Git)
vault_db_password: "SuperGeheim123!"
vault_api_key: "abcdef1234567890"
ansible-playbook site.yml --vault-password-file ~/.vault_pass

Fehlerbehandlung

ignore_errors und failed_when

- name: Pruefen ob Anwendung installiert ist
  command: which myapp
  register: myapp_check
  ignore_errors: true
  changed_when: false

- name: Anwendung nur installieren wenn fehlend
  apt:
    name: myapp
    state: present
  when: myapp_check.rc != 0

- name: Migrationsskript ausfuehren
  command: /opt/myapp/migrate.sh
  register: migration_result
  failed_when: "'ERROR' in migration_result.stdout"
  changed_when: "'Changes applied' in migration_result.stdout"

block / rescue / always

- name: Anwendung mit Rollback deployen
  block:
    - name: Anwendungsdienst stoppen
      service:
        name: myapp
        state: stopped

    - name: Neue Version deployen
      git:
        repo: https://github.com/org/myapp.git
        dest: /opt/myapp
        version: "{{ app_version }}"

    - name: Anwendungsdienst starten
      service:
        name: myapp
        state: started

  rescue:
    - name: Auf vorherige Version zuruecksetzen
      git:
        repo: https://github.com/org/myapp.git
        dest: /opt/myapp
        version: "{{ previous_version }}"

    - name: Team ueber Fehler benachrichtigen
      debug:
        msg: "Deployment von {{ app_version }} fehlgeschlagen. Zurueckgesetzt auf {{ previous_version }}."

  always:
    - name: Deployment-Versuch protokollieren
      shell: echo "{{ app_version }} versucht am $(date)" >> /var/log/deployments.log
      changed_when: false

Best Practices fuer Idempotenz

  • Module bevorzugen statt shell/command: Module pruefen den Zustand vor dem Handeln
  • creates mit command verwenden: ueberspringt den Befehl, wenn die Datei bereits existiert
  • changed_when: false setzen bei Befehlen, die nur Zustand lesen
  • lineinfile statt shell echo >> zum Aendern von Konfigurationsdateien verwenden
  • apt: update_cache: true nicht bei jedem Task: cache_valid_time: 3600 nutzen

ansible.cfg-Konfiguration

[defaults]
inventory = ./inventory.ini
remote_user = deployer
private_key_file = ~/.ssh/id_ed25519
host_key_checking = False
retry_files_enabled = False
stdout_callback = yaml
roles_path = ./roles:~/.ansible/roles
forks = 10

[privilege_escalation]
become = True
become_method = sudo
become_user = root

Debugging und Tests

ansible-playbook site.yml --check --diff
ansible-playbook site.yml -vvv
ansible-playbook site.yml --syntax-check
- name: Aufgeloeste Variablen anzeigen
  debug:
    msg: "App: {{ app_name }}, Port: {{ app_port }}"

- name: Befehlsausgabe erfassen und pruefen
  command: systemctl status nginx
  register: nginx_status
  ignore_errors: true
  changed_when: false

- name: nginx-Status ausgeben
  debug:
    var: nginx_status.stdout_lines

Werkzeugvergleich

MerkmalAnsibleTerraformPuppetChefSaltStack
HauptanwendungKonfig-Mgmt + OrchestrierungInfrastruktur-ProvisionierungKonfig-MgmtKonfig-MgmtKonfig + Remote-Exec
ArchitekturAgentenlos (SSH)Agentenlos (API)Mit AgentMit AgentMit oder ohne Agent
SpracheYAMLHCLPuppet DSLRubyYAML / Python
ZustandsverfolgungZustandslostfstate-DateiPuppetDBChef-ServerSalt mine
LernkurveNiedrigMittelHochHochMittel
IdempotentJaJaJaJaJa

Ansible und Terraform sind komplementaer: Verwenden Sie Terraform zum Erstellen von Cloud-Ressourcen und Ansible zur Konfiguration.

Praxisbeispiel: LAMP-Stack-Deployment

# lamp.yml
- name: LAMP-Stack deployen
  hosts: webservers
  become: true
  vars:
    php_version: "8.3"
    mysql_root_password: "{{ vault_mysql_root_password }}"
    mysql_db_name: myapp
    mysql_db_user: appuser
    mysql_db_password: "{{ vault_mysql_db_password }}"

  tasks:
    - name: Apache und PHP-Pakete installieren
      apt:
        name:
          - apache2
          - "libapache2-mod-php{{ php_version }}"
          - "php{{ php_version }}"
          - "php{{ php_version }}-mysql"
          - "php{{ php_version }}-curl"
          - "php{{ php_version }}-mbstring"
        state: present
        update_cache: true

    - name: MySQL-Server installieren
      apt:
        name:
          - mysql-server
          - python3-pymysql
        state: present

    - name: MySQL starten und aktivieren
      service:
        name: mysql
        state: started
        enabled: true

    - name: Anwendungsdatenbank erstellen
      mysql_db:
        login_user: root
        login_password: "{{ mysql_root_password }}"
        name: "{{ mysql_db_name }}"
        state: present

    - name: Apache mod_rewrite aktivieren
      apache2_module:
        name: rewrite
        state: present
      notify: Apache neu starten

    - name: Anwendung aus Git klonen
      git:
        repo: "{{ app_repo }}"
        dest: /var/www/html
        version: "{{ app_version }}"
        force: true

    - name: Eigentuemer des Web-Verzeichnisses setzen
      file:
        path: /var/www/html
        owner: www-data
        group: www-data
        recurse: true

    - name: Sicherstellen, dass Apache laeuft und aktiviert ist
      service:
        name: apache2
        state: started
        enabled: true

  handlers:
    - name: Apache neu starten
      service:
        name: apache2
        state: restarted
ansible-playbook -i inventory.ini lamp.yml --vault-password-file ~/.vault_pass

Zusammenfassung

  • Die agentenlose Push-Architektur von Ansible bedeutet, dass Sie nur SSH-Zugriff benoetigen, um mit der Automatisierung zu beginnen
  • Das Inventar (INI, YAML oder dynamisch) steuert das gesamte Host-Targeting; group_vars und host_vars halten Variablen organisiert
  • Playbooks verketten Plays, Tasks, Handlers und Jinja2-Templates in einem lesbaren, deklarativen Arbeitsablauf
  • Roles verpacken Tasks, Handlers, Templates und Defaults in wiederverwendbare, teilbare Einheiten
  • ansible-vault verschluesselt Geheimnisse im Ruhezustand; kombinieren Sie Vault-Dateien mit Klartextvariablen-Dateien fuer eine saubere Git-Geschichte
  • block/rescue/always bietet strukturierte Fehlerbehandlung und Rollback-Logik fuer Produktions-Deployments
  • Validieren Sie immer mit --check --diff und -vvv bevor Sie in der Produktion ausfuehren

Verwandte Artikel