Ansible im Homelab

1. Einführung

Ansible ist ein agentloses Automatisierungswerkzeug für die Konfigurationsverwaltung, Anwendungsbereitstellung und Orchestrierung. Im Gegensatz zu Puppet oder Chef benötigt Ansible keinen Agenten auf den verwalteten Systemen – die Kommunikation erfolgt ausschließlich über SSH (bei Linux/macOS) oder WinRM (bei Windows). Das einzige, was auf den Zielmaschinen vorhanden sein muss, ist Python.

Im Homelab löst Ansible das Problem der manuellen Konfigurationsdrift: Wer mehrere Server, VMs oder Raspberry Pis betreibt, kennt den Zustand, dass sich Systeme nach vielen manuellen Anpassungen voneinander unterscheiden. Ansible beschreibt den gewünschten Zustand deklarativ und stellt sicher, dass alle Systeme konsistent konfiguriert sind – reproduzierbar und dokumentiert.

Typische Anwendungsfälle im Homelab:

  • Paket-Updates auf allen Servern gleichzeitig ausführen
  • SSH-Keys und Benutzer auf neuen Maschinen einrichten
  • Docker installieren und Dienste deployen
  • Konfigurationsdateien auf mehreren Hosts synchron halten
  • Proxmox-VMs nach der Erstellung initial konfigurieren
  • Netzwerkgeräte (Cisco, OPNsense) konfigurieren

Ansible-Konfigurationen sind einfache YAML-Dateien, die sich hervorragend in Git versionieren lassen. Das Homelab wird so selbstdokumentierend: Jede Konfigurationsänderung ist als Git-Commit nachvollziehbar.

2. Installation & Inventory

2.1 Ansible installieren

Ansible wird auf dem Control Node installiert – dem Rechner, von dem aus du Playbooks ausführst. Das können dein Laptop, ein dedizierter Management-Server oder eine VM in Proxmox sein. Auf den verwalteten Hosts (Managed Nodes) wird nichts installiert.

# Empfohlen: Installation über pip (aktuellste Version)
pip3 install ansible

# Alternativ: Paketmanager (ggf. ältere Version)
apt-get install ansible          # Debian/Ubuntu
dnf install ansible              # Fedora/RHEL

# Version prüfen
ansible --version

Für SSH-basierte Verbindungen empfiehlt sich die Verwendung von SSH-Keys statt Passwörtern. Richte SSH-Keys auf allen verwalteten Hosts ein, bevor du mit Ansible arbeitest.

2.2 Inventory-Dateien

Das Inventory definiert, welche Hosts Ansible verwalten soll. Es kann als INI-Datei oder YAML geschrieben werden. Eine typische Homelab-Inventordatei (inventory/hosts.yml):

all:
  children:
    servers:
      hosts:
        proxmox01:
          ansible_host: 192.168.20.10
        proxmox02:
          ansible_host: 192.168.20.11
        nas:
          ansible_host: 192.168.20.20

    services:
      hosts:
        nextcloud:
          ansible_host: 192.168.20.30
        monitoring:
          ansible_host: 192.168.20.31

    network:
      hosts:
        opnsense:
          ansible_host: 192.168.50.1
          ansible_user: root

  vars:
    ansible_user: sven
    ansible_python_interpreter: /usr/bin/python3

Mit ansible all -m ping lässt sich testen, ob alle Hosts erreichbar sind.

2.3 Variablen und host_vars / group_vars

Variablen können auf verschiedenen Ebenen definiert werden. Die empfohlene Struktur:

inventory/
├── hosts.yml
├── group_vars/
│   ├── all.yml          # Gilt für alle Hosts
│   ├── servers.yml      # Gilt für die Gruppe "servers"
│   └── services.yml
└── host_vars/
    ├── proxmox01.yml    # Gilt nur für proxmox01
    └── nas.yml

Beispiel group_vars/all.yml:

ntp_servers:
  - 192.168.10.1
  - ptbtime1.ptb.de

timezone: Europe/Berlin
admin_email: admin@homelab.local

3. Ad-hoc-Befehle

Ad-hoc-Befehle sind einmalige Aktionen, die ohne Playbook direkt auf der Kommandozeile ausgeführt werden. Sie sind ideal für schnelle Checks und einmalige Operationen:

# Verbindung zu allen Hosts prüfen
ansible all -m ping

# Freien Speicher auf allen Servern anzeigen
ansible servers -m command -a "df -h"

# Paket installieren (als root)
ansible servers -m apt -a "name=htop state=present" --become

# Dienst neu starten
ansible monitoring -m service -a "name=prometheus state=restarted" --become

# Datei auf alle Hosts kopieren
ansible all -m copy -a "src=./sshd_config dest=/etc/ssh/sshd_config" --become

# Alle Hosts rebooten
ansible servers -m reboot --become

# Systeminformationen sammeln (Facts)
ansible proxmox01 -m setup | grep ansible_distribution

Das Flag --become entspricht sudo – es erhöht die Privilegien auf dem Zielhost. Mit -i inventory/hosts.yml wird das Inventory explizit angegeben, wenn es nicht dem Standardpfad entspricht.

4. Playbooks

Playbooks sind das Herzstück von Ansible. Sie beschreiben eine Abfolge von Tasks, die auf einer Gruppe von Hosts ausgeführt werden sollen. Ein Playbook ist eine YAML-Datei mit einer Liste von Plays. Jedes Play richtet sich an eine Host-Gruppe und enthält eine Liste von Tasks.

4.1 Struktur

---
- name: Grundkonfiguration für alle Server
  hosts: servers
  become: true

  tasks:
    - name: Paketliste aktualisieren
      apt:
        update_cache: true
        cache_valid_time: 3600

    - name: Grundpakete installieren
      apt:
        name:
          - vim
          - htop
          - curl
          - git
          - ufw
        state: present

    - name: SSH-Passwortauthentifizierung deaktivieren
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^PasswordAuthentication'
        line: 'PasswordAuthentication no'
        state: present
      notify: SSH neu starten

    - name: Zeitzone setzen
      timezone:
        name: "{{ timezone }}"

  handlers:
    - name: SSH neu starten
      service:
        name: sshd
        state: restarted

Playbooks werden ausgeführt mit: ansible-playbook -i inventory/hosts.yml playbooks/basis.yml

4.2 Handler

Handler sind spezielle Tasks, die nur ausgeführt werden, wenn sie von einem anderen Task benachrichtigt werden (über notify). Sie werden am Ende eines Plays ausgeführt, und zwar nur einmal – egal wie viele Tasks sie benachrichtigt haben. Typischer Anwendungsfall: SSH-Dienst nur neu starten, wenn die Konfigurationsdatei tatsächlich geändert wurde.

4.3 Idempotenz

Idempotenz ist eines der wichtigsten Konzepte in Ansible: Ein Task, der mehrfach ausgeführt wird, produziert dasselbe Ergebnis wie ein einmalig ausgeführter Task. Ansible-Module sind so geschrieben, dass sie prüfen, ob eine Änderung nötig ist, bevor sie diese durchführen. Das apt-Modul installiert ein Paket nur dann, wenn es noch nicht installiert ist. Das erlaubt es, Playbooks beliebig oft auszuführen, ohne unerwünschte Nebeneffekte.

5. Rollen (Roles)

Rollen sind eine strukturierte Methode, Ansible-Aufgaben zu kapseln und wiederverwendbar zu machen. Statt alles in einem Playbook zu haben, wird die Logik in eigenständige Rollen aufgeteilt. Eine Rolle hat eine festgelegte Verzeichnisstruktur:

roles/
└── docker/
    ├── tasks/
    │   └── main.yml       # Haupt-Taskliste
    ├── handlers/
    │   └── main.yml       # Handler für diese Rolle
    ├── templates/
    │   └── daemon.json.j2 # Jinja2-Templates
    ├── files/
    │   └── docker.conf    # Statische Dateien
    ├── vars/
    │   └── main.yml       # Nicht-überschreibbare Variablen
    ├── defaults/
    │   └── main.yml       # Überschreibbare Standardwerte
    └── meta/
        └── main.yml       # Metadaten und Abhängigkeiten

Eine Rolle wird im Playbook einfach referenziert:

---
- name: Docker auf Service-Hosts einrichten
  hosts: services
  become: true
  roles:
    - common
    - docker
    - monitoring_agent

Vorgefertigte Rollen aus der Community sind über Ansible Galaxy verfügbar: ansible-galaxy install geerlingguy.docker installiert eine weit verbreitete Docker-Rolle.

6. Typische Homelab-Playbooks

System-Update auf allen Hosts

---
- name: Alle Server aktualisieren
  hosts: all
  become: true
  tasks:
    - name: Paketliste aktualisieren und alle Pakete upgraden
      apt:
        update_cache: true
        upgrade: dist

    - name: Nicht mehr benötigte Pakete entfernen
      apt:
        autoremove: true
        purge: true

    - name: Prüfen ob Neustart nötig ist
      stat:
        path: /var/run/reboot-required
      register: reboot_required

    - name: Neustart falls nötig
      reboot:
        reboot_timeout: 300
      when: reboot_required.stat.exists

Neuen Benutzer auf allen Hosts anlegen

---
- name: Admin-Benutzer anlegen
  hosts: all
  become: true
  vars:
    admin_user: sven
    admin_ssh_key: "ssh-ed25519 AAAA... sven@laptop"
  tasks:
    - name: Benutzer anlegen
      user:
        name: "{{ admin_user }}"
        groups: sudo
        shell: /bin/bash
        create_home: true

    - name: SSH-Key hinterlegen
      authorized_key:
        user: "{{ admin_user }}"
        key: "{{ admin_ssh_key }}"
        state: present

    - name: Passwortlosen sudo erlauben
      lineinfile:
        path: /etc/sudoers.d/{{ admin_user }}
        line: "{{ admin_user }} ALL=(ALL) NOPASSWD:ALL"
        create: true
        validate: visudo -cf %s

7. Ansible Vault

Ansible Vault verschlüsselt sensible Daten wie Passwörter, API-Keys und Zertifikate, die in Playbooks und Variablendateien verwendet werden. Verschlüsselte Dateien können bedenkenlos in Git eingecheckt werden.

Vault-Datei erstellen

# Neue verschlüsselte Datei anlegen
ansible-vault create inventory/group_vars/all/vault.yml

# Bestehende Datei verschlüsseln
ansible-vault encrypt inventory/group_vars/all/secrets.yml

# Vault-Datei bearbeiten
ansible-vault edit inventory/group_vars/all/vault.yml

# Datei entschlüsseln (temporär anzeigen)
ansible-vault view inventory/group_vars/all/vault.yml

Vault in Playbooks nutzen

Eine typische vault.yml enthält verschlüsselte Variablen:

# Inhalt nach dem Entschlüsseln:
vault_db_password: "meinGeheimesPasswort123"
vault_nextcloud_admin_password: "anderesGeheimnisXYZ"
vault_wireguard_private_key: "abcdef..."

In group_vars/all.yml werden Vault-Variablen auf reguläre Namen gemappt:

db_password: "{{ vault_db_password }}"
nextcloud_admin_password: "{{ vault_nextcloud_admin_password }}"

Playbook mit Vault-Passwort ausführen:

# Passwort interaktiv eingeben
ansible-playbook site.yml --ask-vault-pass

# Passwort aus Datei lesen (für Automatisierung)
ansible-playbook site.yml --vault-password-file ~/.vault_pass

Das Vault-Passwort selbst sollte in einem Passwort-Manager wie Vaultwarden oder Bitwarden aufbewahrt werden.

8. Best Practices

Dry-Run vor dem echten Lauf

Mit --check simuliert Ansible einen Playbook-Lauf, ohne tatsächlich Änderungen vorzunehmen. Ideal um zu prüfen, was ein Playbook tun würde:

ansible-playbook site.yml --check --diff

--diff zeigt zusätzlich an, wie Dateien geändert würden.

Tags für selektive Ausführung

Tasks können mit Tags versehen werden, um nur bestimmte Teile eines Playbooks auszuführen:

- name: Docker installieren
  apt:
    name: docker-ce
  tags:
    - docker
    - install
# Nur Tasks mit Tag "docker" ausführen
ansible-playbook site.yml --tags docker

# Alle Tasks außer "install" ausführen
ansible-playbook site.yml --skip-tags install

Projektstruktur

Eine bewährte Verzeichnisstruktur für ein Homelab-Ansible-Projekt:

ansible/
├── inventory/
│   ├── hosts.yml
│   ├── group_vars/
│   │   ├── all.yml
│   │   └── all/
│   │       └── vault.yml   (verschlüsselt)
│   └── host_vars/
├── roles/
│   ├── common/
│   ├── docker/
│   └── monitoring/
├── playbooks/
│   ├── site.yml            (alle Hosts)
│   ├── update.yml          (System-Updates)
│   └── services.yml        (Dienste deployen)
├── ansible.cfg
└── requirements.yml        (Galaxy-Rollen)

ansible.cfg

Eine projektlokale ansible.cfg spart Tipporwand und standardisiert das Verhalten:

[defaults]
inventory = inventory/hosts.yml
roles_path = roles
host_key_checking = False
stdout_callback = yaml
interpreter_python = auto_silent

[privilege_escalation]
become = False
become_method = sudo

9. Zusammenfassung

Ansible ist im Homelab ein mächtiges Werkzeug, um Konfigurationsdrift zu bekämpfen und Setups reproduzierbar zu machen. Das agentlose Modell über SSH macht den Einstieg einfach – kein Agent, keine Datenbank, keine komplexe Infrastruktur.

Die wichtigsten Punkte im Überblick:

  • Ansible benötigt nur Python auf den verwalteten Hosts und kommuniziert über SSH – kein Agent nötig.
  • Das Inventory definiert Hosts und Gruppen; Variablen werden in group_vars/ und host_vars/ gepflegt.
  • Ad-hoc-Befehle für schnelle Einmalaktionen; Playbooks für wiederholbare, dokumentierte Konfiguration.
  • Handler werden nur bei Änderungen ausgelöst und genau einmal am Ende des Plays ausgeführt.
  • Idempotenz ist ein Kernprinzip: Playbooks können beliebig oft ausgeführt werden ohne unerwünschte Seiteneffekte.
  • Rollen strukturieren größere Projekte und machen Konfigurationslogik wiederverwendbar.
  • Ansible Vault verschlüsselt Geheimnisse, sodass sie sicher in Git versioniert werden können.
  • Mit --check --diff Änderungen immer erst simulieren, bevor sie auf produktive Systeme angewendet werden.