Zum Hauptinhalt springen

Ansible

Definition

Ansible ist ein Open-Source-Automatisierungswerkzeug von Red Hat, das Konfigurationsmanagement, Anwendungsdeployment und Aufgabenautomatisierung über eine Flotte von Servern mithilfe einfacher, menschenlesbarer YAML-Dateien namens Playbooks übernimmt. Seine definierende architektonische Entscheidung ist agentlos: Ansible verbindet sich über SSH (Linux) oder WinRM (Windows) mit verwalteten Knoten und führt Aufgaben direkt aus, ohne dass auf den Zielmaschinen Daemon- oder Agent-Software erforderlich ist. Dies macht die Einführung erheblich einfacher als bei agentenbasierten Werkzeugen — man kann bestehende Server verwalten, ohne dass über Python und einen SSH-Server hinaus Software vorinstalliert sein muss.

Ansible arbeitet nach einem Push-Modell: Ein Operator führt ein Playbook von einem Steuerknoten aus, Ansible verbindet sich mit dem Ziel-Inventar der Hosts und führt Aufgaben der Reihe nach aus. Aufgaben rufen Module auf — idempotente Arbeitseinheiten, die wissen, wie Pakete installiert, Dateien verwaltet, Dienste gestartet, Befehle ausgeführt und mit Cloud-APIs interagiert werden. Community- und offizielle Module decken praktisch jeden Linux-Paketmanager, Dienst, Cloud-Anbieter, Netzwerkgerät und jede Anwendung ab. Rollen bündeln verwandte Aufgaben, Dateien, Vorlagen und Variablen in wiederverwendbare, teilbare Einheiten, die auf Ansible Galaxy veröffentlicht oder in internen Git-Repositories gepflegt werden können.

Im ML- und Data-Engineering-Kontext füllt Ansible die Lücke, die Terraform hinterlässt. Terraform stellt Infrastruktur bereit (erstellt die GPU-Instanz, die VPC, den S3-Bucket); Ansible konfiguriert, was auf dieser Infrastruktur läuft (installiert die korrekte CUDA-Version, konfiguriert die Python-Umgebung, richtet verteilte Trainingsabhängigkeiten ein und stellt sicher, dass GPU-Monitoring-Tools laufen). Die beiden Werkzeuge ergänzen sich eher, als dass sie konkurrieren: Ein typischer MLOps-Workflow nutzt Terraform zur Bereitstellung von Cloud-Ressourcen und Ansible, um diese Ressourcen in einen trainierungsbereiten Zustand zu bringen.

Funktionsweise

Inventar

Das Inventar definiert, welche Hosts Ansible verwaltet. Ein statisches Inventar ist eine INI- oder YAML-Datei, die Hostnamen oder IP-Adressen nach Rolle gruppiert (z. B. [gpu_training_nodes], [model_serving]). Dynamische Inventare fragen Cloud-APIs (AWS EC2, GCP Compute, Azure VMs) zur Laufzeit ab, um die Host-Liste aus lebender Infrastruktur aufzubauen — unerlässlich für Auto-Scaling-Umgebungen. Host- und Gruppen-Variablen definieren pro-Host- oder pro-Gruppen-Konfigurationswerte, die in Playbooks referenziert werden.

Playbooks und Aufgaben

Ein Playbook ist eine YAML-Datei mit einem oder mehreren Plays. Jedes Play richtet sich an eine Gruppe von Hosts und eine Liste von Aufgaben. Jede Aufgabe ruft ein Modul mit Argumenten auf und definiert optional Bedingungen (when), Schleifen (loop) und bei Änderung ausgelöste Handler. Aufgaben werden innerhalb eines Plays sequenziell ausgeführt; Plays können parallel über Hosts ausgeführt werden. Das Ergebnis jeder Aufgabe ist eines von: ok (keine Änderung erforderlich), changed (Änderung vorgenommen), failed oder skipped. Ansible gibt nach jedem Playbook-Lauf eine Zusammenfassung dieser Ergebnisse aus.

Rollen

Rollen bieten eine standardisierte Verzeichnisstruktur für die Organisation verwandter Automatisierung: tasks/, handlers/, templates/, files/, vars/, defaults/ und meta/. Eine Rolle kann auf mehrere Plays in mehreren Playbooks angewendet werden, und Rollen können von anderen Rollen abhängen. Ansible Galaxy hostet Tausende von Community-Rollen (z. B. geerlingguy.docker, nvidia.nvidia_driver), die mit ansible-galaxy install installiert und direkt in Playbooks verwendet werden können.

Variablen und Templating

Ansible verwendet die Jinja2-Template-Engine in Playbooks und Template-Dateien. Variablen können auf mehreren Ebenen definiert werden (Rollen-Defaults, Gruppen-Vars, Host-Vars, Playbook-Vars, Extra-Vars mit -e) mit einer klaren Prioritätsreihenfolge. Templates (.j2-Dateien) generieren Konfigurationsdateien dynamisch — zum Beispiel eine verteilte Trainingskonfigurationsdatei mit der korrekten Master-Knoten-IP, Anzahl der GPUs und Batch-Größe für jede Umgebung.

Idempotenz und Handler

Ansible-Module sind so konzipiert, dass sie idempotent sind: Das mehrfache Ausführen eines Playbooks erzeugt denselben Endzustand ohne unbeabsichtigte Nebeneffekte. Wenn ein Paket bereits in der korrekten Version installiert ist, meldet die Aufgabe ok und tut nichts. Handler sind spezielle Aufgaben, die am Ende eines Plays nur dann ausgeführt werden, wenn sie von einer Aufgabe benachrichtigt wurden, die changed ergab — verwendet, um Dienste (wie einen CUDA-beschleunigten Training-Daemon) nur dann neu zu starten, wenn sich ihre Konfiguration tatsächlich geändert hat.

Wann verwenden / Wann NICHT verwenden

Verwenden wennVermeiden wenn
Software auf bestehenden Servern konfiguriert werden soll: CUDA, Python, Pip-Pakete, Systemdienste installierenNeue Cloud-Infrastruktur von Grund auf bereitgestellt werden soll (dafür Terraform verwenden)
GPU-Trainingsknoten nach der Erstellung durch Terraform eingerichtet werden sollenFeinkörnige Zustandsverfolgung über Hunderte von Ressourcen benötigt wird (Ansible hat keine State-Datei)
Konsistente ML-Umgebungen über Entwicklungs-, Staging- und Produktionsmaschinen eingerichtet werden sollenKomplexe Abhängigkeitsgraphen zwischen Cloud-Ressourcen mit automatischer Sortierung benötigt werden
Ad-hoc-Befehle über eine Flotte von Servern ausgeführt werden sollen (z. B. eine Konfigurationsdatei überall aktualisieren)Zielmaschinen nicht über SSH oder WinRM vom Steuerknoten erreichbar sind
Anwendungsupdates deployt oder Konfigurationsänderungen auf vielen Knoten ausgerollt werden sollenCloud-native Ressourcen (VPCs, IAM-Rollen, S3-Buckets) bereitgestellt werden sollen — Terraform verwenden
Teams IaC-Tooling mit flacher YAML-Lernkurve benötigenSehr schnelle parallele Ausführung erforderlich ist; Ansible's SSH-Overhead begrenzt die Skalierbarkeit bei Tausenden von Knoten

Vergleiche

KriteriumAnsibleTerraform
ParadigmaProzedural mit idempotenten Modulen — Aufgaben laufen der Reihe nachDeklarativ — gewünschten Zustand beschreiben, Terraform berechnet das Diff
ZustandsverwaltungZustandslos — keine integrierte Verfolgung des zuvor angewendeten ZustandsExplizite State-Datei bildet Konfiguration auf echte Ressourcen-IDs ab
Primärer AnwendungsfallKonfigurationsmanagement und Software-Deployment auf bestehenden HostsCloud-Infrastruktur-Bereitstellung (Instanzen, Netzwerke, Speicher)
Cloud-Anbieter-UnterstützungCloud-Module vorhanden, aber weniger umfassend als Terraform-Provider1.000+ Provider mit tiefer, versionierter API-Abdeckung
IdempotenzAufgabenebene — jedes Modul muss idempotent geschrieben seinNativ — Plan/Apply konvergiert immer zum deklarierten Zustand
LernkurveNiedrig — YAML-Aufgaben sind lesbar; keine neue Sprache erforderlichModerat — HCL-Syntax + State/Plan-Konzept zu erlernen
Agent erforderlichNein — agentlos, verbindet sich über SSHNein — Terraform läuft auf dem Steuerrechner, ruft Cloud-APIs auf
Gemeinsamer EinsatzAnsible konfiguriert Software auf Infrastruktur, die Terraform bereitgestellt hatTerraform stellt Ressourcen bereit; Ansible übernimmt OS- und App-Konfiguration

Vor- und Nachteile

AspektVorteileNachteile
Agentlose ArchitekturKeine Software auf Zielknoten zu installieren; funktioniert mit bestehendem SSHSSH-Overhead begrenzt die Leistung bei sehr großem Maßstab (10.000+ Knoten)
YAML-PlaybooksMenschenlesbare, selbstdokumentierende AutomatisierungKomplexe Logik (Schleifen, Bedingungen) wird in YAML ausführlich
Idempotente ModuleSicher auszuführen; Drift-Korrektur ohne NebeneffekteIdempotenz hängt von Modulqualität ab; Shell/Command-Module sind nicht inhärent idempotent
Ansible GalaxyGroßes Ökosystem an Community-Rollen für gängige SoftwareQualität der Community-Rollen variiert; Pinning von Rollenversionen ist für Reproduzierbarkeit entscheidend
Keine State-DateiEinfach, kein State-Management-OverheadKeine integrierte Drift-Erkennung zwischen Ausführungen; manuelles oder Drittanbieter-Tooling erforderlich
Jinja2-TemplatingLeistungsstarke dynamische KonfigurationsgenerierungTemplate-Debugging ist schwieriger als nativer Code; Fehler treten zur Laufzeit auf

Code-Beispiele

# ml_environment_setup.yml
# Ansible playbook to configure a GPU training node for ML workloads.
# Installs CUDA toolkit, cuDNN, Python 3.11, pip packages, and sets up
# a systemd service for the Prometheus node exporter.
#
# Usage:
# ansible-playbook -i inventory.ini ml_environment_setup.yml
#
# inventory.ini example:
# [gpu_training_nodes]
# 10.0.1.10 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/ml-key.pem
# 10.0.1.11 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/ml-key.pem

---
- name: Configure GPU training nodes for ML workloads
hosts: gpu_training_nodes
become: true # Run tasks as root via sudo
vars:
cuda_version: "12.1"
python_version: "3.11"
pip_packages:
- torch==2.3.0
- torchvision==0.18.0
- torchaudio==2.3.0
- numpy==1.26.4
- pandas==2.2.2
- scikit-learn==1.4.2
- mlflow==2.13.0
- evidently==0.4.30
- prometheus-client==0.20.0
node_exporter_version: "1.8.1"
ml_user: "mlops"
ml_workdir: "/opt/ml"

handlers:
- name: restart node_exporter
ansible.builtin.systemd:
name: node_exporter
state: restarted
daemon_reload: true

tasks:
# --- System prerequisites ---

- name: Update apt package cache
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600 # Skip update if cache is less than 1 hour old

- name: Install system dependencies
ansible.builtin.apt:
name:
- build-essential
- git
- wget
- curl
- htop
- nvtop # GPU monitoring in terminal
- python{{ python_version }}
- python{{ python_version }}-dev
- python{{ python_version }}-venv
- python3-pip
state: present

# --- CUDA installation ---

- name: Check if CUDA {{ cuda_version }} is already installed
ansible.builtin.command: nvcc --version
register: nvcc_check
changed_when: false
failed_when: false

- name: Add CUDA repository keyring
ansible.builtin.shell: |
wget -qO /tmp/cuda-keyring.deb \
https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb
dpkg -i /tmp/cuda-keyring.deb
when: cuda_version not in (nvcc_check.stdout | default(''))
args:
creates: /usr/share/keyrings/cuda-archive-keyring.gpg

- name: Install CUDA toolkit {{ cuda_version }}
ansible.builtin.apt:
name: cuda-toolkit-{{ cuda_version | replace('.', '-') }}
state: present
update_cache: true
when: cuda_version not in (nvcc_check.stdout | default(''))

- name: Set CUDA environment variables in /etc/environment
ansible.builtin.lineinfile:
path: /etc/environment
line: "{{ item }}"
state: present
loop:
- 'CUDA_HOME=/usr/local/cuda'
- 'PATH=/usr/local/cuda/bin:$PATH'
- 'LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH'

# --- ML user and workspace ---

- name: Create dedicated ML user
ansible.builtin.user:
name: "{{ ml_user }}"
shell: /bin/bash
home: "/home/{{ ml_user }}"
create_home: true
state: present

- name: Create ML working directory
ansible.builtin.file:
path: "{{ ml_workdir }}"
state: directory
owner: "{{ ml_user }}"
group: "{{ ml_user }}"
mode: "0755"

# --- Python virtual environment and packages ---

- name: Create Python virtual environment
ansible.builtin.command:
cmd: python{{ python_version }} -m venv {{ ml_workdir }}/venv
creates: "{{ ml_workdir }}/venv/bin/python"
become_user: "{{ ml_user }}"

- name: Upgrade pip in virtual environment
ansible.builtin.pip:
name: pip
state: latest
virtualenv: "{{ ml_workdir }}/venv"
become_user: "{{ ml_user }}"

- name: Install ML Python packages
ansible.builtin.pip:
name: "{{ pip_packages }}"
virtualenv: "{{ ml_workdir }}/venv"
state: present
become_user: "{{ ml_user }}"

- name: Write requirements.txt for reproducibility
ansible.builtin.copy:
dest: "{{ ml_workdir }}/requirements.txt"
content: "{{ pip_packages | join('\n') }}\n"
owner: "{{ ml_user }}"
group: "{{ ml_user }}"
mode: "0644"

# --- Prometheus Node Exporter for infrastructure monitoring ---

- name: Check if node_exporter is already installed
ansible.builtin.stat:
path: /usr/local/bin/node_exporter
register: node_exporter_stat

- name: Download Prometheus node_exporter {{ node_exporter_version }}
ansible.builtin.get_url:
url: "https://github.com/prometheus/node_exporter/releases/download/v{{ node_exporter_version }}/node_exporter-{{ node_exporter_version }}.linux-amd64.tar.gz"
dest: /tmp/node_exporter.tar.gz
mode: "0644"
when: not node_exporter_stat.stat.exists

- name: Extract and install node_exporter
ansible.builtin.unarchive:
src: /tmp/node_exporter.tar.gz
dest: /tmp
remote_src: true
when: not node_exporter_stat.stat.exists

- name: Copy node_exporter binary to /usr/local/bin
ansible.builtin.copy:
src: "/tmp/node_exporter-{{ node_exporter_version }}.linux-amd64/node_exporter"
dest: /usr/local/bin/node_exporter
mode: "0755"
remote_src: true
when: not node_exporter_stat.stat.exists
notify: restart node_exporter

- name: Create node_exporter systemd service
ansible.builtin.copy:
dest: /etc/systemd/system/node_exporter.service
content: |
[Unit]
Description=Prometheus Node Exporter
After=network.target

[Service]
User=nobody
ExecStart=/usr/local/bin/node_exporter \
--collector.systemd \
--collector.processes
Restart=on-failure

[Install]
WantedBy=multi-user.target
mode: "0644"
notify: restart node_exporter

- name: Enable and start node_exporter
ansible.builtin.systemd:
name: node_exporter
enabled: true
state: started
daemon_reload: true

# --- Verification ---

- name: Verify GPU is visible to CUDA
ansible.builtin.command: nvidia-smi
register: nvidia_smi_output
changed_when: false
failed_when: nvidia_smi_output.rc != 0

- name: Print GPU info
ansible.builtin.debug:
var: nvidia_smi_output.stdout_lines

- name: Verify PyTorch can see the GPU
ansible.builtin.command:
cmd: "{{ ml_workdir }}/venv/bin/python -c \"import torch; print('CUDA available:', torch.cuda.is_available()); print('GPU count:', torch.cuda.device_count())\""
register: torch_check
changed_when: false
become_user: "{{ ml_user }}"

- name: Print PyTorch GPU availability
ansible.builtin.debug:
var: torch_check.stdout_lines

Praktische Ressourcen

  • Ansible-Dokumentation — Offizielle Dokumentation zu Playbooks, Modulen, Rollen, Inventar und Best Practices.
  • Ansible Galaxy — Community-Hub für wiederverwendbare Ansible-Rollen und -Collections, einschließlich NVIDIA-GPU-Treiber, Docker- und Kubernetes-Rollen.
  • Jeff Geerling — Ansible für DevOps — Umfassendes Buch und begleitendes GitHub-Repository zu Ansible von Grundlagen bis zu Produktionsmustern.
  • NVIDIA-Ansible-Collection — Offizielle NVIDIA-Ansible-Collection für die Verwaltung von GPU-Treibern, CUDA- und NCCL-Installationen.
  • Ansible-Best-Practices-Leitfaden — Offizielle Tipps und Tricks zu Verzeichnisstruktur, Variablenverwaltung und Leistungsoptimierung.

Siehe auch