Vorbereitung: eine Hostliste und stille Anmeldungen
Alle Methoden unten lesen dasselbe: einen Servernamen pro Zeile. Verwenden Sie die Namen aus Ihrer SSH-Konfiguration, dann kommen Benutzer, Ports und Schlüssel von dort und nicht aus jedem Befehl. Falls Sie noch keine haben, sehen Sie sich die Beispiele für die SSH-Config-Datei an.
web1
web2
web3
Speichern Sie sie als hosts. Zwei Optionen sorgen dafür, dass Stapelläufe sauber laufen:
-o BatchMode=yesbricht sofort ab, statt an einer Passwort- oder Passphrase-Abfrage zu warten, die niemand beantwortet.-o ConnectTimeout=5verhindert, dass ein toter Server den Rest aufhält.
Laden Sie Ihren Schlüssel vorher mit ssh-add in den Agenten, damit kein Lauf danach fragt. Bei vielen Läufen hintereinander nutzt SSH-Multiplexing mit ControlMaster eine Verbindung pro Server weiter, und jeder Befehl startet schneller.
Ein Server nach dem anderen: eine Schleife
Eine Schleife ist der einfachste Weg, und die Ausgabe bleibt in Reihenfolge. Diese hier merkt sich außerdem die Server, auf denen der Befehl fehlschlug:
failed=()
while read -r host; do
printf '%s: ' "$host"
ssh -n -o BatchMode=yes -o ConnectTimeout=5 "$host" 'uptime -p' || failed+=("$host")
done < hosts
echo "failed: ${failed[*]}"
web1: up 4 days, 9 hours, 24 minutes
web2: up 4 days, 9 hours, 24 minutes
web3: up 4 days, 9 hours, 24 minutes
failed:
Das -n ist wichtig. Ohne es liest ssh den Rest von hosts als eigene Eingabe, und die Schleife endet nach dem ersten Server. Quoting und Exit-Codes funktionieren wie bei einem einzelnen Server, siehe wie Sie einen Befehl per SSH ausführen.
Bei vielen Servern ist eine Schleife langsam: Fünfzig Server zu je zwei Sekunden dauern fast zwei Minuten.
Parallel: Hintergrundjobs, xargs und GNU parallel
Reines bash kann jedes ssh im Hintergrund starten und auf alle warten. Jeder Server schreibt in eine eigene Datei, damit sich die Ausgaben nicht mischen:
mkdir -p out
while read -r host; do
ssh -n -o BatchMode=yes "$host" 'hostname; uname -r' > "out/$host.log" 2>&1 &
done < hosts
wait
head out/*.log
Das startet alle auf einmal. Um zu begrenzen, wie viele gleichzeitig laufen, nutzen Sie xargs -P:
xargs -P 10 -I{} ssh -n -o BatchMode=yes {} 'echo "$(hostname): $(nproc) cpu"' < hosts
Die Zeilen kommen in der Reihenfolge zurück, in der die Server fertig werden. Lassen Sie den Befehl also seinen eigenen Hostnamen ausgeben.
GNU parallel kann beides besser: --tag setzt den Servernamen vor jede Zeile, und --joblog schreibt den Exit-Code jedes Servers in eine Datei:
parallel -j 10 --tag --joblog run.log ssh -n -o BatchMode=yes -o ConnectTimeout=5 {} hostname :::: hosts
cut -f 7,9 run.log
Exitval Command
255 ssh -n -o BatchMode=yes -o ConnectTimeout=5 web3 hostname
0 ssh -n -o BatchMode=yes -o ConnectTimeout=5 web2 hostname
0 ssh -n -o BatchMode=yes -o ConnectTimeout=5 web1 hostname
Hier war web3 nicht erreichbar. parallel selbst endet mit der Zahl der fehlgeschlagenen Jobs, hier 1, sodass ein Skript darauf anhalten kann.
parallel-ssh (pssh)
parallel-ssh ist genau dafür gemacht. Unter Debian und Ubuntu installieren Sie es mit apt install pssh, der Befehl heißt dort parallel-ssh. Andere Systeme nennen ihn pssh.
parallel-ssh -h hosts -i -t 10 'test -d /srv/app'
[1] 15:20:02 [FAILURE] web3 Exited with error code 255
Stderr: ssh: Could not resolve hostname web3: Name or service not known
[2] 15:20:02 [FAILURE] web1 Exited with error code 1
[3] 15:20:02 [SUCCESS] web2
Der Befehl prüft, ob ein Ordner existiert: Auf web1 fehlt er, und web3 ist nicht erreichbar. -i gibt die Ausgabe jedes Servers unter seiner Statuszeile aus, und -t 10 gibt einen Server nach 10 Sekunden auf. Um die Ausgabe aufzubewahren, schreiben Sie stattdessen eine Datei pro Server:
parallel-ssh -h hosts -t 10 -o out -e err 'df -h /'
Der Exit-Code ist ungleich null, sobald ein Server fehlschlug. Mit -p begrenzen Sie, wie viele gleichzeitig laufen.
In viele Terminals zugleich tippen: tmux
Manchmal müssen Sie jeden Server beobachten oder auf jedem eine Abfrage beantworten. tmux kann einen Bereich pro Server öffnen und jede Taste an alle senden:
tmux new-session -d -s multi 'ssh web1'
tmux split-window -t multi 'ssh web2'
tmux split-window -t multi 'ssh web3'
tmux select-layout -t multi tiled
tmux set-window-option -t multi synchronize-panes on
tmux attach -t multi
Ausschalten können Sie es in tmux mit Ctrl-b : und setw synchronize-panes off. Solange es an ist, geht alles überallhin, auch ein Tippfehler. Das tmux-Cheat-Sheet listet die Tasten zum Wechseln zwischen Bereichen.
Vor einem Befehl, der etwas ändert
Ein Fehler auf fünfzig Servern kostet fünfzigmal so viel. Ein paar Gewohnheiten halten ihn klein:
- Erst die Vorschau. Viele Werkzeuge haben einen Probelauf.
apt-get -s upgrade | grep -c ^Instzählt die Pakete, die sich ändern würden, ohne sie zu ändern. - Ein Server, dann der Rest. Führen Sie die Änderung auf dem ersten Server aus, prüfen Sie sie, dann auf den anderen.
- Vor dem Lauf fragen. Zeigen Sie in einem Skript die Anzahl und warten Sie auf eine Antwort.
- Jeden Exit-Code lesen. Ein Lauf, der auf 49 Servern klappte und auf einem fehlschlug, ist fehlgeschlagen, bis Sie den Grund kennen.
first=$(head -n 1 hosts)
ssh -n "$first" 'apt-get -s upgrade | grep -c ^Inst'
n=$(wc -l < hosts)
read -r -p "Run on $n servers? [y/N] " ok
[ "$ok" = y ] || exit 1
Halten Sie Produktion in einer eigenen Hostdatei, damit ein Befehl für Staging sie nie aus Versehen erreicht. Weitere Gewohnheiten finden Sie unter wie Sie Befehle auf dem falschen Server vermeiden.
Wann Ansible besser passt
Wenn dieselbe Änderung jede Woche wiederkommt, ist ein Werkzeug, das das Ergebnis beschreibt, besser als ein Shell-Befehl. Der Ad-hoc-Modus von Ansible liest dieselbe Liste:
ansible all -i 'web1,web2,web3,' -m ansible.builtin.command -a 'uptime -p' --forks 10
web1 | CHANGED | rc=0 >>
up 4 days, 9 hours, 25 minutes
Es meldet rc für jeden Server, und Module wie apt oder copy zeigen mit --check, was sie ändern würden. Für einen kurzen Blick auf ein paar Server sind Schleife oder parallel-ssh weniger Aufwand. Wie Sie die Liste selbst in Ordnung halten, steht unter wie Sie viele SSH-Server verwalten.
Fragen
Warum stoppt meine ssh-Schleife nach dem ersten Server?
ssh liest die Standardeingabe und nimmt dabei den Rest der Hostliste mit. Fügen Sie dem ssh-Befehl -n hinzu, oder nutzen Sie eine for-Schleife, die nicht von der Standardeingabe liest.
Auf wie vielen Servern kann ich gleichzeitig arbeiten?
So vielen, wie Rechner und Netz schaffen. Zehn bis fünfzig gleichzeitig sind üblich. Gehen Sie tiefer, wenn der Befehl schwer ist oder die Server eine Ressource wie eine Datenbank teilen.
Was unterscheidet parallel-ssh von Ansible?
parallel-ssh führt einen Shell-Befehl auf vielen Servern aus. Ansible führt Module aus, die das gewünschte Ergebnis beschreiben, kann vor einer Änderung prüfen und hält Inventare und Playbooks für wiederkehrende Arbeit.
Kann ich in mehrere SSH-Sitzungen gleichzeitig tippen?
Ja. Legen Sie in tmux jede Sitzung in einen Bereich und schalten Sie synchronize-panes ein. Jede Taste geht an alle Bereiche, bis Sie es ausschalten.
Quellen
- ssh(1), OpenBSD-Handbuch
- Handbuch zu GNU parallel
- Projekt parallel-ssh (pssh)
- tmux(1), synchronize-panes
- Einführung in Ad-hoc-Befehle, Ansible-Dokumentation