Aus Linux-Magazin 04/2021

IT-Automatisierung mit Saltstack

© _Aleksandr Khakimullin / 123RF.com

Der in Python geschriebene modulare Werkzeugkasten Saltstack enthält vorgefertigte Module für viele Einsatzzwecke und erweist sich als schnell und zuverlässig.

2013 stand für die Autoren ein Wechsel in ein neues Datacenter an, verbunden mit einer Umstellung der verwendeten Infrastruktur weg vom klassischen Bare-Metal-Server hin zu wohldimensionierten virtuellen Maschinen. Bis dahin diente Puppet zum Konfigurationsmanagement, was aber viele architekturbedingte Probleme und Unzulänglichkeiten mit sich brachte. Andererseits ließen sich auch nicht alle Puppet-Manifeste sofort ersetzen, weshalb nur eine Lösung infrage kam, die man ohne Probleme parallel einsetzen konnte.

Nach einigen Vergleichen fiel die Entscheidung für Saltstack, das schon damals ein eigenes Puppet-Modul mitbrachte. Damit konnte man Puppet aus Saltstack heraus steuern. Zudem überzeugte Saltstack (kurz: Salt) durch seine modulare Architektur und hohe Geschwindigkeit. Wo die Laufzeit von Ansible und Chef häufig unvorhersehbar war, da lieferte Saltstack nach wenigen Sekunden bereits zuverlässig Resultate – unabhängig davon, ob es sich um 10 oder 100 Hosts handelte. Die Lasttests der damals neuen Plattform erfolgten beispielsweise mit 300 AWS-VMs, die via Salt-Cloud in wenigen Minuten ausgerollt wurden.

Es gibt viele Möglichkeiten, Saltstack zu betreiben: von der klassischen Server-Client-Architektur – im Salt-Jargon Master-Minion – bis zu einem Serverless-Modus (Masterless). Flexibilität beweist Saltstack auch dann, wenn es um die Frage der Verbindung geht: Es unterstützt zum Beispiel ZeroMQ, SSH oder einen reinen TCP-Modus inklusive TLS. Mit Proxy-Modulen lassen sich auch Systeme anbinden, die weder SSH noch Python unterstützen. Das Proxy-Modul übersetzt dann die Salt-Syntax in für das Zielsystem kompatible Befehle.

Saltstack ermöglicht diese Vielseitigkeit, indem es von Anfang an auf eine modulare Bauweise setzt. Egal, ob es darum geht, gewünschte Zustände zu beschreiben, Verbindungen aufzubauen, Aktionen vorzunehmen oder Ausgaben darzustellen: Alles ist modular, austausch- und erweiterbar. Dabei sind viele sinnvolle Standardeinstellungen gesetzt, und der Anwender kann ohne große Konfigurationsorgien einfach loslegen.

So mancher Admin bekommt die Tiefen des modularen Baukastens nie zu sehen. Steigen die Ansprüche und sucht man nach zeitbasierten Schedulern oder REST-APIs, findet man sie bei Saltstack im Lieferumfang. Während andere Automatisierer hier auf Lösungen von Drittanbietern verweisen müssen (oder andere Tools herumbauen), erklärt die Salt-Dokumentation lapidar, wie sich diese Aufgaben mit wenigen Zeilen lösen lassen. Das hat den Vorteil, dass stets die gleiche Terminologie zum Einsatz kommt und Konfigurationen im gewohnten Format erfolgen – einfach als weiteres Feature aus dem Baukasten, das man kombiniert und anwendet.

Das Setup

Da die übliche Saltstack-Installation [1] aus der Master-Minion-ZeroMQ-Variante besteht, betrachten wir hier hauptsächlich diese Architektur. Ein Salt-Master ist dabei die Instanz, die alles steuert. Der Master sendet die Befehle und empfängt die Resultate. Er verteilt über einen eingebauten Fileserver (»saltfs«) die State- und Execution-Module sowie andere Dateien an die Minions. Der Salt-Minion agiert als Client und empfängt vom Master die Anweisungen. Die arbeitet er ab, indem er State- und Execution-Module ausführt sowie Informationen und Resultate an den Master liefert.

Komplexere Setups beinhalten mehrere Master Server, die in verschiedenen Zonen laufen und dort jeweils ihre eigenen Minions kontrollieren. Diese Zonen-Master lassen sich wiederum über einen übergeordneten Master steuern. Die Beschreibung einer solchen Konfiguration würde aber den Umfang dieses Artikels sprengen.

Wir gehen hier von folgender Situation aus: Es gibt in einem privaten Netz eine Instanz, auf der ein Salt-Master-Prozess läuft. Diese Instanz trägt den Host-Namen »salt«, der sich via DNS auflösen lässt. Dann gibt es noch zwei weitere Instanzen namens »minion01« und »minion02«. Auf ihnen ist bereits ein Salt-Minion installiert und gestartet. Es gibt keine vom Default abweichenden Konfigurationen.

Wenn ein Minion startet, sucht er als Erstes nach seinem Master. Gibt die Konfiguration nichts anderes vor, dann führt der Minion einen DNS-Lookup nach dem Namen »salt« aus und versucht, sich auf den Port 4505 (Publisher Port) der gefundenen Adresse zu verbinden. Ein unregistrierter Minion schickt dann seinen Public Key an den Master und bittet um Registrierung. Auf dem Salt-Master kann man sich diese Keys mit dem Befehl aus der ersten Zeile von Listing 1 auflisten lassen.

Listing 1

Keys auflisten

# salt-key list
    Accepted Keys:
    Denied Keys:
    Unaccepted Keys:
    minion01
    minion02
    Rejected Keys:
# salt 'minion0*' test.ping

Mit dem Befehl »salt-key –accept minion0*« akzeptiert der Master die Public Keys von »minion01« und »minion02«, die damit ab diesem Zeitpunkt unter seiner Kontrolle stehen. Ob sich beide Minions erreichen lassen, prüft das Kommando aus der letzten Zeile von Listing 1. Geht alles gut, quittiert die Ausgabe den Minion-Namen mit »True«, der Minion lässt sich also erreichen und steuern.

Targeting

Um eine Aktion auf einem Minion anzustoßen, muss man wissen, wie man eine bestimmte Instanz aus der Minion-Liste herausfiltert und anspricht. Im Default-Setup erfordert das weder statische noch dynamische Host-Listen: Der Salt-Master weiß, welche Minions bei ihm registriert sind, und man kann jederzeit komplexe Queries bauen, um sie zu adressieren. Um beispielsweise die Logrotate-Konfigurationsdatei aller Debian-Hosts mit zwei x86_64-CPUs und 4 GByte RAM zu sehen, die den Namen »minion« plus zwei Zahlen am Ende tragen, setzt man den Befehl aus der ersten Zeile von Listing 2 ab.

Listing 2

Salt-Query

# salt -C "G@os:Debian and G@num_cpus:2 and G@cpuarch:x86_64 and G@mem_total:4096 and E@minion\d{2,}" logrotate.show_conf
# salt -C "N@group1" logrotate.show_conf

Die von den Minions bereitgestellten Informationen heißen im Salt-Jargon Grains; im Listing tauchen sie mit dem Kürzel »G@« auf. Solche Grains liefern Informationen über Netzwerkschnittstellen, RAM, CPUs, die benutzte Virtualisierung oder Ähnliches. Im Beispiel filtert der Master die Grains nach den Keys »os«, »num_cpus«, »cpuarch« und »mem_total«. Zusätzlich gibt es einen regulären Ausdruck (eingeleitet durch »E@«) »minion\d{2,}«, der alle Hosts findet, die dem gewünschten Namensschema entsprechen – also »minion01«, »minion02« und so weiter). Auf diesen Hosts wird dann der Befehl »show_conf« aus dem Execution-Modul »logrotate« ausgeführt.

Benutzt man solche Target-Filter wiederkehrend, kann man sie auch in eine YAML-Struktur speichern und als sogenannte Nodegroup wiederverwenden. Eine solche Nodegroup ruft man mit einem Kommando wie dem in der letzten Zeile von Listing 2 auf. Nodegroups dürfen sich sogar untereinander referenzieren.

Beispielsweise könnte »nodegroup.conf« die »group2« referenzieren und die »group1« um die Bedingung erweitern, dass es sich beim Init-System um Systemd handeln muss. Die Node Group »group3« wiederum könnte »group2« referenzieren und eine Query ergänzen, die nur Hosts findet, die aus dem Netz 10.10.10.0/24 kommen. Weitere Möglichkeiten und eine genaue Erklärung der Syntax findet man in der Salt-Dokumentation unter dem Stichwort “Targeting” [2].

Zustände

Salt benutzt in der Regel Zustandsbeschreibungen (States), um beispielsweise Pakete zu installieren oder Konfigurationen zu ändern. States liefern die Beschreibung eines gewünschten Zustands. Dabei kann es sich etwa um ein installiertes Paket handeln oder um eine Datei, die mit bestimmtem Inhalt und definierten Berechtigungen auf dem Zielsystem existieren muss.

Diese States rufen im Hintergrund transparent die passenden Execution-Module (wie das bereits genannte Logrotate-Modul) auf, die dann die tatsächliche Arbeit verrichten. States definiert man üblicherweise im YAML-Format, sie lassen sich aber auch mit der Template-Engine Jinja2 variabel und dynamisch gestalten. Daneben akzeptiert Saltstack auch andere Formate für State-Beschreibungen, darunter JSON, Python oder eine eigene DSL namens PyDSL [3].

Über eine Shebang-artige Zeile lässt sich der Default-Renderer dynamisch überschreiben, und man kann sogar Renderer-Ketten bilden. So sorgt der Shebang »#! gpg | jinja | yaml« dafür, dass Saltstack den State erst mit GPG rendert, dann mit Jinja2 und zu guter Letzt als YAML. Der GPG-Renderer sucht im GPG-Keyring nach dem passenden Private Key zum Public Key, mit dem der String verschlüsselt wurde, und entschlüsselt den String im Speicher. Danach verarbeiten der Jinja2- und abschließend der YAML-Renderer das Dokument. Der Default-Shebang lautet im Grunde also »#! jinja | yaml«.

Es soll nun exemplarisch ein State beschrieben werden, der das Paket iotop installiert. Dazu speichert man unter »/srv/salt/demo_state.sls« den Inhalt aus Listing 3. Der Befehl aus Listing 4 weist Salt an, auf »minion01« den entsprechenden Zustand herzustellen [4]. Die Ausgabe sieht dann so aus wie in Abbildung 1. Um ein Ausgabeformat zu erhalten, das sich an ein Shell- oder Python-Skript verfüttern lässt, ergänzt man den Befehl zum Beispiel um »–out json«.

Listing 3

demo_state.sls

Ensure that the iotop package is installed:
  pkg.installed:
    - name: iotop

Listing 4

State anwenden

# salt 'minion01' state.apply demo_state
Abbildung 1: Die Ausgabe eines »state.apply«-Aufrufs zur Installation eines Software-Pakets.

Abbildung 1: Die Ausgabe eines »state.apply«-Aufrufs zur Installation eines Software-Pakets.

Für gewöhnlich will man eine ganze Reihe an States auf einem oder mehreren Hosts anwenden. Statt dazu mehrfach »state.apply« aufzurufen (was durchaus möglich ist), legt man besser unter »/srv/salt/« ein File namens »top.sls« an (Listing 5). Dort definiert man ein Environment (im Beispiel »base«), ein Target (»minion01«) sowie eine Liste anzuwendender States (in unserem Fall »demo_state«).

Listing 5

top.sls

base:
   'minion01':
      - demo_state

In der Salt-Welt nennt man das Ganze dann abstrakt einen Highstate. Der lässt sich wieder mit »state.apply« ausrollen, allerdings ohne explizite States anzugeben. Die Ausgabe (Abbildung 2) erinnert an die des vorherigen Befehls, allerdings mit kleinen Änderungen: Die Laufzeit des Highstate war wesentlich kürzer, es gab keine Changes, und der Kommentar besagt, dass das Paket iotop bereits installiert war.

Abbildung 2: Ausgabe eines Highstate-Jobs, der eine Liste von States abarbeitet.

Abbildung 2: Ausgabe eines Highstate-Jobs, der eine Liste von States abarbeitet.

Datenstrukturen

Neben den Grains, die die Minions an den Master senden, gibt es noch die sogenannten Pillars. Das sind Daten, die der Master an spezifische Minions sendet. Diese Datenstrukturen lassen sich benutzen, um Variablen für Templates zu definieren, GPG-verschlüsselte Secrets zu speichern oder sogenannte Simple Mappings zu definieren, die betriebssystemabhängige Unterschiede abstrahieren.

Im ersten Schritt legen wir das File »/srv/pillar/mappings.sls« mit dem Inhalt aus Listing 6 an. Nun gilt es, die Pillar-Daten dediziert bestimmten Minions zuzuordnen. Dazu legt man unter »/srv/pillar/top.sls« ein Top-File mit dem Inhalt aus Listing 7 an. Das Kommando aus der ersten Zeile von Listing 8 aktualisiert anschließend die Pillars. Danach lassen sich die Pillar-Daten vom »minion01« abrufen (zweite Zeile). Als Resultat sehen wir nun alle Pillar-Daten, die »minion01« zugeordnet sind (Abbildung 3).

Listing 6

mappings.sls

mappings:
  apache2:
    Debian:
      pkg_name: apache2
      srv_name: apache2
    Red Hat:
      pkg_name: httpd
      srv_name: httpd

Listing 7

top.sls

base:
   '*':
     - mappings

Listing 8

Pillar-Daten abrufen

# salt 'minion01' saltutil.pillar refresh
# salt "minion01" pillar.items
Abbildung 3: Ausgabe der Pillar-Daten, die »minion01« zugeordnet sind.

Abbildung 3: Ausgabe der Pillar-Daten, die »minion01« zugeordnet sind.

Bei Bedarf kann man auch nur Teilbereiche der Pillar-Daten abrufen. Das Kommando aus Listing 9 ruft nur die Daten des Namespaces »mappings:apache2:Debian« ab. Damit lassen sich nun Unterschiede in den Paketnamen für Apache2 bei Debian- und Red-Hat-basierten Distributionen ausgleichen. Dazu dient ein weiteres State-File unter »/srv/salt/apache2.sls« (Abbildung 4).

Listing 9

Teilbereich abrufen

# salt "minion01" pillar.get "mappings:apache2:Debian"
minion 01:
    ----------
    pkg_name:
      apache2
    srv_name:
      apache2
Abbildung 4: Ein weiteres State-File für Apache2.

Abbildung 4: Ein weiteres State-File für Apache2.

Anschließend ergänzen wir unser Top-File unter »/srv/salt/top.sls« mit dem Inhalt von Listing 10. Ein »state.apply«-Befehl liefert nun die Informationen aus Abbildung 5 zurück. Sie zeigen, dass das Paket iotop eingerichtet ist, einige Apache2-Pakete installiert wurden und der Service Apache2 bereits läuft.

Listing 10

apache2-State

base:
   'minion1':
      - demo state
      - apache2
Abbildung 5: Die Ausgabe des »state apply«-Befehls.

Abbildung 5: Die Ausgabe des »state apply«-Befehls.

Durch unser Mapping in den Pillar-Daten bekamen die Aufrufe »pkg.installed« und »service.running« automatisch den richtigen Namen vorgesetzt. Mit anderen Worten: Ein und dieselbe Beschreibung funktioniert sowohl unter Debian-Derivaten als auch unter Red-Hat-Ablegern.

Ein Blick in die Zukunft

Im Oktober 2020 hat VMware Saltstack übernommen, dessen Zukunft damit sicherer scheint denn je. VMware ließ dazu wissen, es wolle damit seine Fähigkeiten zur Automatisierung erweitern und es für seine Multi-Cloud-Strategie nutzen. Die Autoren können sich aber auch sehr gut ein VMware-Tool-Paket vorstellen, das einen Salt-Minion mitbringt und sich von einer zukünftigen VSphere-Version aus orchestrieren lässt. Auf diese Weise könnte man in VSphere Templates anlegen, die OS-Pakete installieren und auf einem aktuellen Stand halten. (jcb/jlu)

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 5 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben