Cloud-Mac-CI-Runner vor der Jobannahme zuverlässig prüfen

Cloud-Mac-CI-Runner vor der Jobannahme zuverlässig prüfen

Ein Cloud-Mac kann gestern Abend einen Build erfolgreich abgeschlossen haben und heute erst nach der Annahme des nächsten Jobs ausfallen – etwa wegen zu wenig freiem Speicherplatz, eines geänderten Xcode-Auswahlpfads, zurückgebliebener Simulatorzustände oder eines festgefahrenen Kindprozesses. Zu diesem Zeitpunkt belegt die Pipeline bereits einen Ausführungsplatz, während der eigentliche Fehler zwischen Abhängigkeitsinstallation, Build-Protokollen und Wiederholungsmeldungen untergeht. Zuverlässiger ist eine Aufnahmeprüfung, bevor der Scheduler einen Job zuweist: Nur ein erfolgreicher Check erlaubt die Jobannahme; andernfalls verlässt der Runner die Warteschlange und speichert einen Snapshot seines Zustands.

Gesundheitsprüfung vor der Jobannahme ausführen

Eine Aufnahmeprüfung sollte kurz, deterministisch und frei von Seiteneffekten sein. Sie ist weder eine vollständige Systemprüfung noch dafür zuständig, die Maschine „nebenbei zu reparieren“. Sie sollte lediglich fünf Fragen beantworten:

  1. Ist der Pfad zu den Entwicklungswerkzeugen vorhanden und lässt sich xcodebuild starten?
  2. Verfügt das Arbeitsvolume über ausreichend freien Speicherplatz?
  3. Beeinträchtigt der Speicherdruck bereits neue Jobs?
  4. Kann der Simulatordienst eine Liste der Geräte und Laufzeitumgebungen zurückgeben?
  5. Hat der vorherige Job verwaltete Prozesse oder eine Job-Sperre hinterlassen?

Runner auf VMCache können durch einen selbst gehosteten Agenten, einen zeitgesteuerten Scheduler oder den eigenen Warteschlangenmanager des Teams gesteuert werden. Unabhängig von der gewählten Lösung muss die Prüfung vor der „Jobannahme“ stattfinden und nicht als erster Schritt des Build-Skripts. Im zweiten Fall zählt die Ausführung bereits als fehlgeschlagener Job und verfälscht Erfolgsquoten sowie Wiederholungsstatistiken.

Schlägt die Gesundheitsprüfung fehl, sollte der Runner vorübergehend keine neuen Jobs annehmen und Diagnoseinformationen sichern. Caches sofort zu löschen, sämtliche Prozesse zu beenden oder die ganze Maschine neu zu starten, ist dagegen nicht die richtige Reaktion.

Zuerst ein überprüfbares Prüfsystem erstellen

Das folgende Skript liest ausschließlich den Zustand aus und lehnt die Jobannahme mit einem von null verschiedenen Exitcode ab. Die Grenzwerte müssen an den Umfang des jeweiligen Projekts angepasst werden und sollten nicht unverändert als einheitliche Konstanten für alle Runner dienen.

#!/bin/zsh
set -u

WORK_VOLUME="${WORK_VOLUME:-/}"
MIN_FREE_GB="${MIN_FREE_GB:-40}"
LOCK_FILE="${RUNNER_LOCK_FILE:-/tmp/vmcache-ci-job.lock}"
failures=0

fail() {
  print -u2 "FAIL: $1"
  failures=$((failures + 1))
}

developer_dir="$(xcode-select -p 2>/dev/null || true)"
[[ -n "$developer_dir" && -d "$developer_dir" ]] \
  || fail "developer directory is unavailable"

if ! xcodebuild -version >/dev/null 2>&1; then
  fail "xcodebuild cannot start"
fi

free_kb="$(df -Pk "$WORK_VOLUME" | awk 'NR==2 {print $4}')"
if [[ -z "$free_kb" ]]; then
  fail "free disk space cannot be read"
else
  free_gb=$((free_kb / 1024 / 1024))
  (( free_gb >= MIN_FREE_GB )) \
    || fail "free disk space is below ${MIN_FREE_GB} GB"
fi

if [[ -e "$LOCK_FILE" ]]; then
  fail "a previous job lock still exists"
fi

if ! xcrun simctl list devices available >/dev/null 2>&1; then
  fail "simulator service is not responding"
fi

exit $((failures > 0 ? 20 : 0))

Verwenden Sie im Skript weder sudo rm -rf noch killall, und wechseln Sie Xcode nicht automatisch. Die Aufnahmephase soll eine Entscheidung treffen, nicht den vorgefundenen Zustand verändern. So bleibt der Schaden selbst dann begrenzt, wenn die Prüfung falsch konfiguriert ist.

Feste Bedeutung der Exitcodes definieren

Empfehlenswert ist folgende Festlegung: 0 bedeutet, dass der Runner Jobs annehmen darf, 20 steht für eine nicht erfüllte Umgebungsanforderung und 30 dafür, dass die Prüfung selbst nicht abgeschlossen werden konnte. Bei 20 setzt der Scheduler den Runner auf pausiert; bei 30 meldet er einen Fehler der Prüfinfrastruktur. Geben Sie nicht für jede Ausnahme pauschal 1 zurück, da sich ein Knotenausfall sonst nicht von einem Skriptfehler unterscheiden lässt.

Festplatte und Arbeitsspeicher prüfen, ohne sie gleichzeitig zu bereinigen

Der Grenzwert für den freien Festplattenspeicher muss die Spitzenbelegung eines Jobs durch Quellcode, Abhängigkeiten, DerivedData, Archive und temporäre Dateien abdecken. Er lässt sich aus den Spitzenwerten früherer erfolgreicher Jobs ableiten und sollte zusätzlich genügend Spielraum für ein Rollback enthalten. Außerdem darf die Prüfung nicht auf das Root-Volume beschränkt bleiben. Liegen Arbeitsverzeichnis, Caches oder Simulatordaten auf anderen APFS-Volumes, muss df -Pk für jedes einzelne ausgeführt werden.

Bei der Arbeitsspeicherprüfung genügt es nicht, nur den „freien Speicher“ auszulesen. macOS nutzt Arbeitsspeicher aktiv als Dateicache; aussagekräftiger sind anhaltender Speicherdruck und Swap-Aktivität. Eine schlanke Aufnahmeprüfung kann die Ausgaben von memory_pressure und vm_stat speichern, damit die Überwachung deren Entwicklung vergleichen kann:

snapshot_dir="${RUNNER_STATE_DIR:-$HOME/ci-state}"
mkdir -p "$snapshot_dir"

{
  date -u "+%Y-%m-%dT%H:%M:%SZ"
  xcode-select -p
  xcodebuild -version
  df -h /
  memory_pressure
  vm_stat
} > "$snapshot_dir/preflight-latest.txt" 2>&1

Lehnen Sie einen Job nicht allein aufgrund einer einzelnen Zählung von Speicherseiten ab. Zuverlässiger ist es, zunächst den Zustand zu protokollieren und die Jobannahme nur bei Bedingungen zu sperren, von denen bekannt ist, dass sie die aktuelle Arbeitslast scheitern lassen. Für MLX-Inferenz oder umfangreiche Linkvorgänge sollte der Bedarf an gemeinsamem Arbeitsspeicher zudem in einer eigenen Richtlinie für die jeweilige Warteschlange definiert werden, statt für alle Jobs denselben Grenzwert zu verwenden.

Zurückgebliebene Jobs erkennen, ohne Systemprozesse versehentlich zu beenden

Die Erkennung zurückgebliebener Prozesse muss deren Zugehörigkeit berücksichtigen. Wer Prozesse allein anhand der Namen xcodebuild, swift oder Simulator global beendet, kann damit eine interaktive Sitzung oder die Jobs eines anderen Executors abbrechen. Sicherer ist es, jeden Job eine Sperrdatei mit PID, Job-ID und Startzeit erstellen und diese über einen Exit-Trap wieder entfernen zu lassen.

job_lock="${RUNNER_LOCK_FILE:-/tmp/vmcache-ci-job.lock}"

cleanup() {
  rm -f "$job_lock"
}

if ! ( set -o noclobber; print "$$ ${CI_JOB_ID:-unknown}" > "$job_lock" ) 2>/dev/null; then
  print -u2 "another managed job owns the runner"
  exit 20
fi

trap cleanup EXIT INT TERM

Wird eine alte Sperre gefunden, prüfen Sie zunächst, ob die darin angegebene PID noch existiert. Vergleichen Sie anschließend die Startzeit und die Befehlszeile des Prozesses. Da PIDs wiederverwendet werden, darf die Entscheidung zum Beenden eines Prozesses nicht allein auf dieser Nummer beruhen. Existiert der Prozess nicht mehr, kann die Sperrdatei zusammen mit dem Prüfzeitpunkt in das Diagnoseverzeichnis verschoben werden. Danach lässt sich die Blockierung durch einen kontrollierten Wiederherstellungsschritt aufheben.

Grenzen für die Simulatorprüfung festlegen

Ein erfolgreicher Aufruf von simctl list bestätigt lediglich, dass der Dienst erreichbar ist. Er garantiert nicht, dass ein bestimmtes Testziel verfügbar ist. Die Aufnahmeprüfung validiert die Dienstebene; auf Projektebene muss der Job das Zielgerät weiterhin über eine explizite destination suchen. Geräte dürfen im Aufnahmeskript nicht automatisch erstellt, gelöscht oder zurückgesetzt werden. Diese Vorgänge benötigen Zeit und verändern den Ausgangszustand nachfolgender Tests.

Scheduler anbinden und Wiederherstellungswege festlegen

Der Scheduler sollte den Runner zunächst als „wird geprüft“ markieren, das Skript ausführen und den Zustand anschließend atomar auf „bereit zur Jobannahme“ oder „isoliert“ setzen. Unterstützt der Agent keine atomare Zustandsänderung, sollte die Jobannahme zuerst pausiert, danach die Prüfung ausgeführt und die Annahme abschließend wieder aktiviert werden. So kann während der Prüfung kein neuer Job eingeschoben werden.

Wiederherstellungsmaßnahmen sollten nach Risiko gestaffelt werden:

  1. Alte Sperre bei nicht vorhandener PID: Sperrdatei archivieren und die Blockierung anschließend aufheben.
  2. Wiederherstellbares Verzeichnis überschreitet das Speicherbudget: Nach Bestätigung, dass kein Job aktiv ist, entsprechend der Verzeichniszugehörigkeit bereinigen.
  3. Simulatordienst reagiert nicht: Diagnosedaten sichern und anschließend das vom Team freigegebene Verfahren zur Dienstwiederherstellung ausführen.
  4. Falscher Xcode-Pfad oder ungewöhnlicher Festplattenzustand: Runner isoliert lassen und manuell prüfen.
  5. Derselbe Fehler tritt wiederholt hintereinander auf: Automatische Wiederherstellung beenden und die letzten Snapshots für einen Vergleich der Unterschiede aufbewahren.

Führen Sie abschließend eine Störungsübung für die Aufnahmeprüfung selbst durch. Erhöhen Sie vorübergehend den Mindestwert für freien Festplattenspeicher und bestätigen Sie, dass der Runner die Warteschlange verlässt. Setzen Sie den Wert danach zurück und prüfen Sie, ob der Runner wieder Jobs annehmen kann. Erstellen Sie außerdem eine alte Sperrdatei, um sicherzustellen, dass das System nur den Knoten isoliert und keine Prozesse irrtümlich beendet. Der Nutzen einer Gesundheitsprüfung besteht nicht darin, jeden denkbaren Fehler abzudecken, sondern Fehler vor der Jobzuweisung sichtbar zu machen und einen ausreichend klaren nächsten Schritt zu hinterlassen.

Häufig gestellte Fragen

Muss die vollständige Prüfung vor jedem Build laufen?

Vor jedem Job genügen schnelle Prüfungen. Aufwendige Diagnosen sollten regelmäßig oder erst nach einem Fehler ausgeführt werden, damit die Warteschlange nicht unnötig blockiert.

Darf bei knappem Speicherplatz das gesamte DerivedData gelöscht werden?

Nein. Sperren Sie zuerst die Jobannahme und löschen Sie nur reproduzierbare, eindeutig zugeordnete und nicht mehr verwendete Verzeichnisse.

Soll ein fehlgeschlagener Check den Runner automatisch neu starten?

Nur bei bekannten, idempotent behebbaren Fehlern. Wiederholte Plattenprobleme oder eine falsche Xcode-Auswahl benötigen gesicherte Diagnosedaten und eine manuelle Prüfung.

Dedizierter physischer Knoten

Wählen Sie einen Cloud-Mac für Builds, Tests und MLX-Inferenz

Wählen Sie M4, Arbeitsspeicher, Speicher, Knoten und Laufzeit passend zu Ihrer Aufgabe. Jede Miete umfasst einen dedizierten physischen Rechner, keine virtuelle Maschine.

Mietoption auswählen