Wenn eine CI-Steuerinstanz auf einem Cloud-Mac fortlaufend Versionsprüfungen ausführt, Code abruft, Builds startet und Artefakte einsammelt, würde eine neue SSH-Verbindung für jeden Befehl den Schlüsselaustausch, die Authentifizierung und die Sitzungsinitialisierung jedes Mal wiederholen. Die Verzögerung eines einzelnen Verbindungsaufbaus fällt möglicherweise kaum auf. Bei Dutzenden kurzen Befehlen kann die Handshake-Zeit jedoch einen erheblichen Anteil ausmachen. Mit dem integrierten SSH-Mechanismus ControlMaster können nachfolgende Befehle eine bereits authentifizierte TCP-Verbindung wiederverwenden. Dafür müssen allerdings auch die Socket-Isolierung, die Erkennung ungültiger Verbindungen und die Bereinigung beim Beenden korrekt umgesetzt werden.
Optimierungsziel bestimmen
Die Wiederverwendung von Verbindungen eignet sich für Szenarien, in denen derselbe CI-Job innerhalb weniger Minuten wiederholt auf denselben Mac zugreift. Führt der Job lediglich einen einzigen langen Build aus, bringt die Optimierung des Handshakes nur wenig. Wenn die Steuerinstanz dagegen nacheinander ein Dutzend oder mehr kurze Befehle ausführt, ist die Wiederverwendung meist deutlich sinnvoller.
Zeichnen Sie zunächst den Verbindungsablauf ohne Wiederverwendung auf:
time ssh -o BatchMode=yes build-mac 'sw_vers -productVersion'
ssh -vv -o BatchMode=yes build-mac 'true' 2>ssh-debug.log
Prüfen Sie im Debug-Protokoll, ob Schlüsselaustausch und Authentifizierung wiederholt stattfinden. Vergleichen Sie nicht nur die zufällige Laufzeit eines einzelnen Befehls. Führen Sie den Test mindestens mehrmals hintereinander aus und unterscheiden Sie dabei zwischen dem Verbindungsaufbau und der Ausführung des entfernten Befehls.
ControlMaster optimiert ausschließlich die Kosten des Verbindungsaufbaus. Xcode-Kompilierung, Abhängigkeitsauflösung und Dateiübertragungen werden dadurch nicht schneller.
Minimale Konfiguration für die Wiederverwendung einrichten
Richten Sie in ~/.ssh/config auf der CI-Steuerinstanz einen eigenen Alias für den Build-Host ein. %C bildet die Verbindungsparameter als Hash ab und verhindert so, dass ein zu langer Unix-Socket-Pfad aufgrund langer Benutzer- oder Hostnamen fehlschlägt.
Host build-mac
HostName mac.example.internal
User ci-runner
BatchMode yes
ControlMaster auto
ControlPersist 10m
ControlPath ~/.ssh/control/%C
ConnectTimeout 15
ServerAliveInterval 30
ServerAliveCountMax 3
StrictHostKeyChecking yes
UserKnownHostsFile ~/.ssh/known_hosts_ci
Beim Anlegen des Verzeichnisses müssen die Zugriffsrechte eingeschränkt werden:
install -d -m 700 "$HOME/.ssh/control"
chmod 600 "$HOME/.ssh/known_hosts_ci"
ssh build-mac 'printf "%s\n" ready'
ssh -O check build-mac
Der erste Befehl erstellt die Master-Verbindung. ssh -O check sollte anschließend den Status des Master-Prozesses zurückgeben. ControlPersist 10m bedeutet, dass die Master-Verbindung nach dem Ende der letzten Sitzung noch höchstens zehn Minuten bestehen bleibt. Es bedeutet nicht, dass sich ein Job unbegrenzt auf diese Verbindung verlassen kann.
Hostidentität fest verankern
Setzen Sie nicht der Bequemlichkeit halber StrictHostKeyChecking=no. Beziehen Sie den öffentlichen Hostschlüssel über einen kontrollierten Kanal, tragen Sie ihn in die CI-spezifische Datei known_hosts ein und verwenden Sie beim Neuaufbau eines Knotens oder bei einer Schlüsselrotation einen klar definierten Aktualisierungsprozess. Die Wiederverwendung einer Verbindung reduziert lediglich die Zahl der nachfolgenden Handshakes. Die Vertrauensgrenze der ersten Verbindung bleibt unverändert.
Sockets für parallele Jobs isolieren
Eine der häufigsten Fehlerquellen auf gemeinsam genutzten Runnern besteht darin, dass alle Jobs ~/.ssh/control/%C verwenden. Wenn zwei Pipelines gleichzeitig denselben Host ansprechen, könnten sie die jeweils vom anderen Job erstellte Master-Verbindung wiederverwenden. Beendet einer der Jobs diese Verbindung, wird dadurch auch der andere Job unterbrochen.
Robuster ist es, für jeden Job ein eigenes Verzeichnis anzulegen und ControlPath über die Befehlszeile zu überschreiben:
set -euo pipefail
job_id="${CI_JOB_ID:-local-$$}"
control_dir="${TMPDIR:-/tmp}/ssh-control-${job_id}"
install -d -m 700 "$control_dir"
ssh_opts=(
-o ControlMaster=auto
-o ControlPersist=10m
-o "ControlPath=${control_dir}/%C"
-o BatchMode=yes
)
ssh "${ssh_opts[@]}" build-mac 'xcodebuild -version'
ssh "${ssh_opts[@]}" build-mac 'git --version'
Die Job-ID muss aus einem vertrauenswürdigen Pipeline-Kontext stammen und auf eine kurze Zeichenfolge beschränkt sein. Verwenden Sie nicht direkt den Branch-Namen als Teil des Pfads. Schrägstriche, Leerzeichen und übermäßig lange Namen können das Erstellen des Sockets verhindern.
Ungültige Verbindungen erkennen und sicher neu aufbauen
Nach einem Netzwerkwechsel, einem Neustart des entfernten Systems oder einem unerwarteten Abbruch des Steuerprozesses kann die Socket-Datei noch vorhanden sein, obwohl die zugehörige Verbindung nicht mehr funktioniert. Prüfen Sie die Verbindung, bevor Sie den eigentlichen Befehl ausführen. Schlägt die Prüfung fehl, löschen Sie das Socket-Verzeichnis des aktuellen Jobs und bauen Sie anschließend eine neue Verbindung auf.
if ! ssh "${ssh_opts[@]}" -O check build-mac >/dev/null 2>&1; then
rm -rf "$control_dir"
install -d -m 700 "$control_dir"
ssh "${ssh_opts[@]}" build-mac 'true'
fi
Dabei darf ausschließlich das vom aktuellen Job erstellte Verzeichnis gelöscht werden. Ein gemeinsam genutztes Verzeichnis darf nicht mit rm -rf ~/.ssh/control/* bereinigt werden. Build-Befehle sollten außerdem nicht bedingungslos wiederholt werden: Bei einer unterbrochenen Verbindung wurde der entfernte Befehl möglicherweise bereits gestartet. Sicher ist es, nur idempotente Prüfungen automatisch zu wiederholen und anschließend anhand von Prozessen, Sperrdateien oder Build-Nummern den Status des ursprünglichen Jobs festzustellen.
Serverseitige Sitzungslimits beachten
Eine Master-Verbindung kann mehrere logische Sitzungen transportieren, jedoch nicht unbegrenzt viele. Bei zu vielen parallelen Befehlen könnte der Server neue Channels ablehnen. Begrenzen Sie zunächst die SSH-Parallelität eines einzelnen Jobs und prüfen Sie erst danach, ob die serverseitigen Richtlinien angepasst werden müssen. Eine Erhöhung des Sitzungslimits sollte nicht als Standardlösung betrachtet werden.
Beim Beenden deterministisch bereinigen
Beenden Sie die Master-Verbindung bei einem regulären Abschluss mit dem entsprechenden Steuerbefehl. Auch nach einem unerwarteten Abbruch muss das Job-Verzeichnis gelöscht werden. In einem Shell-Skript lässt sich dies zentral mit trap erledigen:
cleanup() {
ssh "${ssh_opts[@]}" -O exit build-mac >/dev/null 2>&1 || true
rm -rf "$control_dir"
}
trap cleanup EXIT INT TERM
Die abschließende Prüfung sollte vier Punkte abdecken: Aufeinanderfolgende Befehle verwenden tatsächlich dieselbe Master-Verbindung; zwei parallele Jobs nutzen unterschiedliche Verzeichnisse; nach einem Neustart des entfernten Systems kann die Verbindung neu aufgebaut werden; und nach dem Abbruch eines Jobs bleiben keine Sockets zurück. Für Automatisierungsaufgaben auf SetMini gelten dieselben Grundsätze: Prüfen Sie zunächst in der Konsole die aktuell verfügbaren Konfigurationen und behandeln Sie die Verbindungsschicht anschließend als überprüfbare, wiederherstellbare und bereinigbare Infrastruktur – nicht als verborgenen Zustand, der dauerhaft gültig bleibt.
Häufig gestellte Fragen
Sollte ControlPersist möglichst lange eingestellt werden?
Nein. Für CI reichen meist 5 bis 15 Minuten, um aufeinanderfolgende Befehle eines Jobs abzudecken. Längere Laufzeiten erhöhen das Risiko veralteter Sockets und jobübergreifender Nutzung.
Dürfen parallele Jobs denselben ControlPath verwenden?
Das ist nicht empfehlenswert. Jeder Job sollte ein eigenes Socket-Verzeichnis mit Modus 700 erhalten und beim Beenden erst ssh -O exit ausführen, bevor das Verzeichnis gelöscht wird.
Umgeht ControlMaster die Prüfung des SSH-Hostschlüssels?
Nein. Auch die erste Master-Verbindung muss StrictHostKeyChecking=yes verwenden und den Zielhost mit einer kontrollierten known_hosts-Datei prüfen.
Entwicklungsschritte in eine jederzeit erreichbare Cloud-Mac-Umgebung verlagern
Wählen Sie zwischen zwei Apple-Silicon-Konfigurationen und vier Rechenzentren. Die Ressourcen werden nicht mit anderen Mietern geteilt; maßgeblich ist der aktuelle Status in der Konsole.