Bewährte Methoden zur Optimierung großer Ansible-Bereitstellungen
Praktische Wege, um große Ansible-Ausführungen mit Forks, Strategie-Plugins, Fakten-Caching, SSH-Wiederverwendung und besserem Playbook-Design zu beschleunigen.
Bewährte Methoden zur Optimierung großer Ansible-Bereitstellungen
Große Ansible-Bereitstellungen werden aus einfachen Gründen langsam: zu viele SSH-Handshakes, zu viel Fakten-Sammlung, ein Controller mit zu wenig CPU oder ein Playbook, das jeden Host auf den langsamsten warten lässt. Die Lösung ist selten eine einzelne Einstellung. Das beste Ergebnis erzielen Sie, indem Sie den Verbindungs-Overhead reduzieren, die Parallelität anpassen und Plays schreiben, die weniger Arbeit pro Host verrichten.
Ich würde "großer Maßstab" nicht durch eine strikte Host-Anzahl definieren. Ein Inventar mit 300 Hosts kann sich groß anfühlen, wenn jede Aufgabe Pakete über langsame Verbindungen installiert. Ein Inventar mit 3.000 Hosts kann handhabbar sein, wenn der Controller gut dimensioniert und die Playbooks schlank sind. Behandeln Sie die untenstehenden Zahlen als Ausgangspunkte und messen Sie dann mit Ihrem eigenen Inventar und Ihren Modulen.
Parallelität optimieren, bevor Sie Ansible die Schuld geben
Parallelität ist normalerweise der erste Hebel, den es zu testen gilt, da Ansible viel Zeit mit Warten auf entfernte Hosts verbringt. Das Ziel ist nicht "höchste Forks gewinnen". Das Ziel ist genügend Parallelität, um den Controller auszulasten, ohne SSH, Privilegieneskalation, Paket-Repositories oder die Ziele selbst zu überlasten.
Steuerung der Parallelität mit forks
Der Parameter forks definiert die Anzahl der parallelen Prozess-Worker, die der Ansible-Controller starten kann. Die optimale Anzahl zu finden erfordert ein Gleichgewicht zwischen Controller-Ressourcen (CPU und Speicher) und den Verbindungslimits der Zielumgebung.
Setzen Sie forks in Ihrer ansible.cfg oder über die Befehlszeile (-f oder --forks).
[defaults]
forks = 100
Starten Sie niedriger, als Sie denken. Führen Sie dasselbe Playbook gegen dieselbe Host-Gruppe mit 25, 50, 100 und 200 Forks aus, während Sie CPU, Speicher, SSH-Fehler und Laufzeit beobachten. Wenn die CPU größtenteils im Leerlauf ist und Hosts Zeit mit Warten verbringen, erhöhen Sie die Forks. Wenn der Controller zu swappen beginnt, sich Python-Prozesse stapeln oder Ziele Verbindungen ablehnen, reduzieren Sie.
Auswahl der richtigen Strategie-Plugin
Die Standard-Ausführungsstrategie von Ansible ist linear, was bedeutet, dass Aufgaben auf allen Ziel-Hosts abgeschlossen sein müssen, bevor zur nächsten Aufgabe im Playbook übergegangen wird. Bei Tausenden von Knoten kann ein einzelner langsamer Host den gesamten Durchlauf zum Engpass machen.
Für einige große Bereitstellungen verwenden Sie die free-Strategie.
Free-Strategie (strategy = free):
free erlaubt es Hosts, unabhängig voneinander durch das Playbook zu gehen, sobald sie eine Aufgabe abgeschlossen haben, ohne auf langsamere Hosts zu warten. Es kann den Durchsatz verbessern, wenn Aufgaben unabhängig sind. Verwenden Sie es nicht blind für Rollout-Bereitstellungen, gemeinsame Migrationen oder Plays, bei denen die Aufgabenreihenfolge über die gesamte Flotte hinweg wichtig ist.
# Beispiel Playbook-Definition
---
- hosts: all
strategy: free
tasks:
- name: Sicherstellen, dass der Dienst läuft
ansible.builtin.service:
name: httpd
state: started
Fakten cachen, wenn Sie sie wiederverwenden
Fakten-Sammlung ist nützlich, aber es ist leicht, wiederholt dafür zu bezahlen. Wenn Ihre Playbooks Fakten über mehrere Durchläufe hinweg verwenden, cachen Sie sie. Wenn ein Play keine Host-Fakten benötigt, deaktivieren Sie die Sammlung für dieses Play.
Verwendung externer Caches (Redis oder Memcached)
Für einen einzelnen Controller kann das Cachen von JSON-Dateien ausreichen. Für mehrere Controller oder Automatisierungs-Worker verwenden Sie einen externen Cache wie Redis oder Memcached, damit jeder Worker denselben Fakten-Cache sieht.
Umsetzbare Konfiguration in ansible.cfg:
[defaults]
gathering = smart
fact_caching = redis
fact_caching_timeout = 7200 ; Fakten für 2 Stunden cachen (in Sekunden)
fact_caching_prefix = ansible_facts
; Bei Verwendung von Redis
fact_caching_connection = localhost:6379:0
Setzen Sie gathering = smart, wenn gecachte Fakten Teil Ihres Workflows sind. Wenn Sie nur einen kleinen Teil der Host-Daten benötigen, verwenden Sie gather_subset anstatt alles zu sammeln.
3. Optimierung von Verbindung und Transport
Die Reduzierung des Overheads, der mit dem Aufbau von Verbindungen verbunden ist, ist bei Tausenden von gleichzeitigen SSH-Sitzungen von größter Bedeutung.
SSH-Pipelining
Pipelining reduziert die Anzahl der SSH-Roundtrips, die Ansible für viele Modulausführungen verwendet. Es ist oft eine Aktivierung wert, aber testen Sie es mit Ihren Privilegieneskalationsregeln.
SSH-Verbindungswiederverwendung (ControlPersist)
Für Unix-ähnliche Ziele verhindern die Einstellungen ControlMaster und ControlPersist, dass Ansible für jede einzelne Aufgabe eine brandneue SSH-Sitzung initiiert. Es hält einen Control-Socket für eine bestimmte Dauer offen, sodass nachfolgende Aufgaben die bestehende Verbindung nutzen können.
Umsetzbare Konfiguration in ansible.cfg:
[ssh_connection]
pipelining = True
; Aggressive Verbindungswiederverwendung verwenden (z.B. 30 Minuten)
ssh_args = -C -o ControlMaster=auto -o ControlPersist=30m -o ServerAliveInterval=15
Pipelining kann mit sudo-Konfigurationen kollidieren, die ein TTY erfordern. Wenn Sie immer noch Defaults requiretty in sudoers haben, entfernen Sie es für den Automatisierungsbenutzer oder lassen Sie Pipelining für diese Hosts deaktiviert.
Windows-Optimierung (WinRM)
Wenn Sie Windows-Knoten anvisieren, stimmen Sie WinRM separat ab. Kerberos ist normalerweise die bessere Produktionswahl als Basic-Authentifizierung, und die WinRM-Dienstlimits müssen möglicherweise überprüft werden, wenn viele Jobs gleichzeitig verbinden.
4. Inventarverwaltung für den Maßstab
Statische Inventardateien werden mühsam, wenn Hosts häufig erstellt und zerstört werden. Dynamisches Inventar ist nicht für jede große Umgebung zwingend erforderlich, aber es ist die richtige Standardeinstellung für Cloud-Flotten, Autoscaling-Gruppen und CMDB-gestützte Infrastruktur.
Dynamische Inventarquellen
Nutzen Sie Inventar-Plugins für Ihren Cloud-Anbieter (AWS EC2, Azure, Google Cloud) oder Ihr CMDB-System. Dynamisches Inventar stellt sicher, dass Ansible nur aktive Hosts mit aktuellen Daten anvisiert.
# Beispiel: Ausführung gegen ein dynamisch gefiltertes AWS-Inventar
ansible-playbook -i aws_ec2.yml site.yml --limit 'tag_Environment_production'
Intelligentes Targeting und Filtern
Vermeiden Sie es, Playbooks gegen das gesamte Inventar (hosts: all) auszuführen, es sei denn, es ist unbedingt erforderlich. Verwenden Sie granulare Gruppen, Limits (--limit) und Tags (--tags), um den Ausführungszielsatz zu minimieren.
5. Architektonische Überlegungen und Controller-Dimensionierung
Für große Bereitstellungen muss die Umgebung, in der Ansible läuft, angemessen bereitgestellt werden.
Controller-Dimensionierung
Ansible ist stark ressourcenabhängig vom Controller, hauptsächlich CPU und RAM, aufgrund der Notwendigkeit, Prozesse für die parallele Ausführung zu forken.
- CPU: Mehr Forks bedeuten normalerweise mehr Python-Arbeit auf dem Controller. Beobachten Sie die durchschnittliche Auslastung und die Auslastung pro Kern während echter Playbook-Durchläufe.
- RAM: Jeder Fork verbraucht Speicher. Große Templates, große Variablen und gesprächige Callback-Plugins können den Speicherverbrauch schnell erhöhen.
- Speicher-I/O: Schneller lokaler Speicher hilft, wenn der Controller viele temporäre Dateien, Logs, Artefakte oder dateibasierte Fakten-Cache-Einträge schreibt.
Nutzung von Automatisierungsplattformen
Für Teams, die Planung, RBAC, Audit-Trails und mehrere Ausführungs-Worker benötigen, verwenden Sie Ansible Automation Platform oder AWX anstelle einer einzelnen langlebigen Shell-Sitzung auf einem Kontrollknoten.
AAP bietet:
- Job-Planung und -Verlauf: Zentrale Protokollierung und Prüfung.
- Ausführungsumgebungen: Konsistente, reproduzierbare Laufzeitumgebungen.
- Clustering und Skalierung: Verteilen Sie die Ausführung auf mehrere Worker-Knoten, um massive Parallelitätsanforderungen zu bewältigen, ohne einen einzelnen Controller zu überlasten.
- Credential-Management: Sichere Handhabung von Geheimnissen im großen Maßstab.
6. Playbook-Design für Effizienz
Selbst mit optimierter Infrastruktur können schlecht geschriebene Playbooks Leistungssteigerungen zunichtemachen.
Fakten-Sammlung minimieren
Wenn Sie gecachte Fakten verwenden (Abschnitt 2), deaktivieren Sie aktiv redundante Fakten-Sammlung, wo möglich:
- hosts: web_servers
gather_facts: no # Fakten-Sammlung für dieses Play deaktivieren
tasks:
# ... nur Aufgaben ausführen, die nicht auf gesammelten Systemfakten basieren
Verwenden Sie run_once und delegate_to sparsam
Aufgaben, die sequentiell oder zentral ausgeführt werden müssen (z.B. Initiierung eines Rollouts, Aktualisierung eines Load Balancers), sollten über run_once: true und delegate_to: management_node abgewickelt werden. Dies vermeidet verschwenderische Parallelität, wenn nur ein Host die Aktion ausführen soll.
Batch-Operationen bevorzugen
Verwenden Sie, wann immer möglich, Module, die Batch-Operationen nativ handhaben (z.B. Paketmanager wie apt oder yum, die eine Liste von Paketen akzeptieren), anstatt eine große Liste mit einer loop oder with_items über separate package-Aufgaben zu iterieren.
# Besser: eine Paketaufgabe mit einer Liste
- name: Notwendige Abhängigkeiten installieren
ansible.builtin.package:
name:
- nginx
- python3-pip
- firewall
state: present
7. Messen Sie das Playbook, nicht nur die Host-Anzahl
Wenn ein Ansible-Durchlauf langsam ist, fügen Sie Timing hinzu, bevor Sie weitere Knöpfe drehen. Der eingebaute profile_tasks-Callback ist ein guter erster Durchgang:
[defaults]
callbacks_enabled = profile_tasks, timer
Führen Sie das Playbook einmal gegen eine repräsentative Host-Gruppe aus und schauen Sie sich die langsamsten Aufgaben an. Sie könnten feststellen, dass die meiste Zeit in einer Paketinstallation, einem Template-Rendering-Schritt oder einem Befehl verbracht wird, der auf einen externen Dienst wartet. In diesem Fall erzeugt eine Erhöhung der forks nur mehr Druck auf denselben Engpass.
Für einen wiederholbaren Test halten Sie den Inventarausschnitt stabil:
ansible-playbook -i inventory site.yml --limit 'web:&production' -f 50
ansible-playbook -i inventory site.yml --limit 'web:&production' -f 100
ansible-playbook -i inventory site.yml --limit 'web:&production' -f 200
Notieren Sie die Gesamtlaufzeit, fehlgeschlagene Hosts, Controller-CPU, Controller-Speicher und etwaige SSH- oder Sudo-Fehler auf der Zielseite. Beobachten Sie auch den Paket-Repository- oder Artefakt-Server während des Tests. Ein Playbook kann wie ein Ansible-Problem aussehen, wenn das eigentliche Problem darin besteht, dass jeder Host gleichzeitig dasselbe Paket von einem überlasteten internen Mirror herunterlädt.
8. Reduzieren Sie die Arbeit, bevor Sie die Parallelität erhöhen
Große Ansible-Durchläufe verbessern sich oft mehr dadurch, dass sie weniger tun, als dadurch, dass sie es schneller tun. Einige Beispiele tauchen wiederholt auf:
- Eine Template-Aufgabe rendert eine große Konfigurationsdatei bei jedem Durchlauf, obwohl sich nur ein kleiner Include geändert hat.
- Eine Shell-Aufgabe führt auf jedem Host einen Discovery-Befehl aus, obwohl der Wert bereits im Inventar steht.
- Eine Rolle installiert Pakete einzeln in einer Schleife.
- Ein Handler startet einen Dienst nach mehreren nicht zusammenhängenden Template-Änderungen neu, obwohl ein Reload ausreichen würde.
Verwenden Sie Modul-Idempotenz anstelle von shell, wo möglich. Ein Shell-Befehl, der immer "changed" meldet, kann Handler auf Hunderten von Hosts auslösen und eine harmlose Überprüfung in einen rollierenden Neustart verwandeln. Wenn Sie command oder shell verwenden müssen, setzen Sie changed_when und creates oder removes sorgfältig.
- name: Anwendungsverzeichnis einmal initialisieren
ansible.builtin.command: /usr/local/bin/app-init /srv/app
args:
creates: /srv/app/.initialized
Diese kleine Absicherung verhindert wiederholte Arbeit und vermeidet falsche Change-Meldungen.
9. Verwenden Sie Batches zur Risikokontrolle
Leistung ist nicht das einzige Anliegen im großen Maßstab. Manchmal ist das schnellste Playbook betrieblich gefährlich. Für Service-Flotten verwenden Sie serial, um die Schadensreichweite zu kontrollieren:
- hosts: app_servers
serial: 10%
max_fail_percentage: 5
tasks:
- name: Anwendungspaket bereitstellen
ansible.builtin.package:
name: myapp
state: latest
serial macht den Durchlauf länger, als wenn alle Hosts gleichzeitig angegriffen würden, gibt aber Load Balancern, Monitoring und Menschen Zeit zu reagieren. Es schützt auch gemeinsame Abhängigkeiten. Ein Paket-Mirror, ein Datenbank-Migrationsendpunkt oder ein Secret-Manager überleben möglicherweise keine Tausende gleichzeitiger Anfragen.
Große Ansible-Bereitstellungen belasten Systeme, die leicht vergessen werden: DNS-Resolver, Paket-Repositories, Secret-Stores, Logging-Pipelines und Monitoring-Endpunkte. Wenn ein Playbook nur bei höheren Fork-Anzahlen langsamer wird, überprüfen Sie diese gemeinsamen Dienste, bevor Sie Ansible die Schuld geben.
Halten Sie auch die Callback-Ausgabe unter Kontrolle. Sehr ausführliche Logs sind beim Debuggen nützlich, können aber große Durchläufe verlangsamen und den eigentlichen Fehler begraben. Verwenden Sie hohe Ausführlichkeit für einen schmalen Host-Ausschnitt und kehren Sie dann für die Ausführung auf der gesamten Flotte zur normalen Ausgabe zurück.
10. Teilen Sie Plays nach Fehlerdomänen auf
Ein übersehener Skalierungstrick ist, das gesamte Anwesen nicht mehr als eine Bereitstellungseinheit zu behandeln. Wenn Datenbank-Hosts, Web-Hosts, Queues und Cache-Knoten alle im selben riesigen Play leben, kann eine langsame oder defekte Gruppe nicht zusammenhängende Arbeiten verzögern. Trennen Sie Plays nach Fehlerdomäne und Abhängigkeitsreihenfolge.
Führen Sie beispielsweise die grundlegende OS-Konfiguration breit aus, aber stellen Sie Anwendungscode nach Service-Tier bereit. Aktualisieren Sie Cache-Knoten in ihrem eigenen Play. Entladen und starten Sie Web-Knoten in Batches neu. Wenden Sie die Datenbankkonfiguration mit zusätzlichen Prüfungen und geringerer Parallelität an. Dies macht Wiederholungen sicherer, da Sie den fehlgeschlagenen Teil erneut ausführen können, ohne die Arbeit auf jedem Host zu wiederholen.
Es macht auch die Zuständigkeiten klarer. Das für einen Service verantwortliche Team kann seine Batch-Größe, Health Checks und Rollback-Verhalten abstimmen, ohne die globalen Automatisierungsstandards zu ändern. Große Ansible-Bereitstellungen bleiben wartbar, wenn die Playbook-Struktur der Art und Weise entspricht, wie die Infrastruktur während echter Incidents und Wartungsfenster tatsächlich ausfällt.
Die wirkungsvollste Arbeit zur Ansible-Leistungsoptimierung ist normalerweise einfach: SSH-Verbindungen wiederverwenden, unnötige Fakten-Sammlung vermeiden, forks richtig dimensionieren und Playbooks davon abhalten, wiederholt winzige Operationen durchzuführen. Danach schauen Sie sich die Architektur an. Wenn ein Controller nicht sauber mithalten kann, verteilen Sie die Arbeit auf Ausführungsknoten und machen Sie Inventar, Anmeldeinformationen und Protokollierung wiederholbar.