SureVM Engineering-Notizen

Prozessprioritäten in Cloud-Mac-CI gezielt steuern

Prozessprioritäten in Cloud-Mac-CI gezielt steuern

Wenn derselbe Cloud-Mac gleichzeitig Xcode-Builds, Tests, die Komprimierung von Protokollen und die Prüfung von Artefakten ausführt, liegt das häufigste Problem nicht in einem einzelnen außer Kontrolle geratenen Prozess. Vielmehr konkurrieren mehrere unterstützende Aufgaben, die auch später abgeschlossen werden könnten, mit dem kritischen Build um CPU-, Festplatten- und Arbeitsspeicherbandbreite. Die Folgen sind meist schwankende Kompilierungszeiten, Test-Timeouts oder ein verzögert reagierender Remote-Desktop. Der erste Schritt zur Lösung besteht nicht darin, pauschal die Priorität aller Prozesse zu senken, sondern zunächst zwischen kritischem Pfad und Hintergrundpfad zu unterscheiden.

Zunächst eine vergleichbare Prozess-Baseline erstellen

Führen Sie einen repräsentativen Build einmal ohne weitere Aufgaben aus. Wiederholen Sie ihn anschließend, während Komprimierung, Prüfungen und Indizierung gleichzeitig laufen. Erfassen Sie bei beiden Durchläufen die Build-Dauer, die Fehlerphase sowie CPU-Auslastung, Arbeitsspeicherverbrauch, Status und Nice-Wert der wichtigsten Prozesse.

mkdir -p "$HOME/ci-observe"

ps -axo pid,ppid,user,%cpu,%mem,state,nice,etime,command \
  | sort -k4 -nr \
  | head -n 40 \
  > "$HOME/ci-observe/processes.txt"

iostat -w 2 -c 15 > "$HOME/ci-observe/iostat.txt"
vm_stat 2 15 > "$HOME/ci-observe/vm-stat.txt"

Die Messung muss den tatsächlich verwendeten Workspace und einen realistischen Umfang der Abhängigkeiten abdecken. Der erstmalige Download von Abhängigkeiten darf jedoch nicht mit stabilen Builds in derselben Baseline vermischt werden. Steigt die Build-Dauer, während iostat eine anhaltend hohe Aktivität zeigt und der Komprimierungsprozess deutlich CPU belegt, spricht dies für eine Anpassung der unterstützenden Aufgaben. Liegt das Hauptproblem dagegen bei hohem Speicherdruck oder zu vielen parallel ausgeführten Tests, reicht eine reine Prioritätsänderung meist nicht aus.

Eine Priorität ist ein Hinweis für den Scheduler, keine Ressourcenobergrenze. Sie hilft bei der Entscheidung, wer zuerst ausgeführt wird, ersetzt aber keine Begrenzung der Parallelität und garantiert keinem Prozess einen festen CPU-Anteil.

Aufgaben nach ihrem kritischen Pfad klassifizieren

CI-Prozesse lassen sich in drei Kategorien einteilen. Zur ersten gehören Kompilierung, Linken, Tests und der Export von Artefakten. Sie entscheiden darüber, ob die Pipeline abgeschlossen wird, und sollten mit normaler Priorität ausgeführt werden. Die zweite umfasst die Komprimierung von Protokollen, das Erzeugen von Prüfsummen und das Bereinigen alter Artefakte. Diese Aufgaben müssen abgeschlossen werden, brauchen aber normalerweise nicht vor dem Build ausgeführt zu werden. Zur dritten Kategorie gehören Editor-Indizierung, interaktive Analysen und temporäre Diagnosen, die nur bei Bedarf gestartet werden sollten.

Regeln nicht pauschal an Werkzeugnamen knüpfen

Dasselbe Werkzeug kann auf unterschiedlichen Pfaden eingesetzt werden. So gehört tar beim Entpacken einer vor dem Build benötigten Abhängigkeit zum kritischen Pfad, bei der Komprimierung von Protokollen nach dem Build dagegen zu den Hintergrundaufgaben. Entscheidend ist, ob der Vorgang den nächsten Schritt blockiert, ob ein Fehler die Pipeline zwingend abbrechen muss und ob seine Ausgabe von der aktuellen Aufgabe sofort benötigt wird.

Die Klassifizierung sollte in der Pipeline-Konfiguration festgehalten werden, statt sich auf spontane Eingriffe durch Bediener zu verlassen. Dadurch ist bei der Prüfung eines Skripts direkt erkennbar, welche Befehle herabgestuft werden. Zugleich wird verhindert, dass neue Aufgaben standardmäßig eine ungeeignete Strategie übernehmen.

Hintergrundaufgaben mit nice und taskpolicy kapseln

nice passt den Scheduling-Prioritätswert eines Prozesses an. Ein positiver Wert bedeutet, dass der Prozess anderen eher die Ausführung überlässt. Normale Benutzer können die Priorität selbst gestarteter Prozesse senken, sie aber nicht beliebig auf eine höhere Prioritätsstufe anheben. Unter macOS lässt sich ein Befehl mit taskpolicy -b einer Hintergrundrichtlinie zuordnen. Da sich dies nicht nur auf die CPU auswirkt, sollte die Option ausschließlich für Aufgaben verwendet werden, die nachweislich nicht den kritischen Pfad blockieren.

Das folgende Skript führt die Prüfsummenerstellung mit niedriger Hintergrundpriorität aus, während der Xcode-Build seine normale Richtlinie behält. Die jeweiligen Exit-Statuswerte werden separat erfasst:

#!/bin/zsh

set -u
mkdir -p Artifacts Logs

taskpolicy -b nice -n 10 /bin/sh -c \
  'find Artifacts -type f -print0 | xargs -0 shasum -a 256 > Logs/checksums.txt' &
audit_pid=$!

xcodebuild \
  -workspace App.xcworkspace \
  -scheme App \
  -configuration Release \
  -destination 'generic/platform=iOS' \
  build > Logs/xcodebuild.log 2>&1
build_rc=$?

wait "$audit_pid"
audit_rc=$?

if (( build_rc != 0 )); then
  exit "$build_rc"
fi

exit "$audit_rc"

Wenden Sie taskpolicy -b nicht pauschal auf den gesamten CI-Runner an. Dadurch würden Compiler, Testprozesse und Unterprozesse gemeinsam herabgestuft. Die gesamte Pipeline würde langsamer, ohne dass sich der dafür verantwortliche Prozess eindeutig erkennen ließe. Die Kapselung sollte deshalb so nah wie möglich am einzelnen Hintergrundbefehl erfolgen.

QoS im Anwendungscode korrekt einsetzen

Wird unterstützende Arbeit von selbst entwickelten Swift-Werkzeugen ausgeführt, sollte die Queue-QoS direkt im Code festgelegt werden, statt nach dem Start die Funktion eines Prozesses erraten zu müssen. Für vom Benutzer angestoßene Vorgänge, auf deren Ergebnis gewartet wird, eignet sich .userInitiated. Nicht zeitkritische Aufgaben wie die Aufbereitung von Protokollen oder die Inventarisierung von Caches können .utility verwenden. .background ist nur für Vorgänge geeignet, auf die der Benutzer nicht wartet und deren verzögerter Abschluss das aktuelle Ergebnis nicht beeinflusst.

let queue = DispatchQueue(
    label: "com.surevm.ci.artifact-audit",
    qos: .utility
)

queue.async {
    auditArtifacts()
}

Setzen Sie nicht alle Queues auf .userInteractive, nur um sie vermeintlich zu beschleunigen. Eine zu hohe QoS lässt unterstützende Aufgaben mit kritischen Arbeiten um Scheduling-Ressourcen konkurrieren. Außerdem kann es zu einer Prioritätsumkehr kommen, wenn eine Aufgabe mit hoher Priorität auf eine Sperre wartet, die von einer Queue mit niedriger Priorität gehalten wird. Gemeinsam genutzte Sperren, serielle Queues und synchrone Aufrufe müssen daher ebenfalls überprüft werden.

Wirkung prüfen und Rückfallbedingungen festlegen

Wiederholen Sie nach der Anpassung mindestens drei Durchläufe mit denselben Build-Eingaben und vergleichen Sie die mediane Dauer, die Fehlerstellen und die Ressourcenmessungen. Ein einzelner schnellerer Durchlauf belegt noch nicht, dass die Strategie funktioniert. Prüfen Sie außerdem, ob Hintergrundaufgaben wie Protokollkomprimierung und Prüfsummenerstellung vollständig abgeschlossen wurden. Ein erfolgreicher kritischer Build darf Fehler in unterstützenden Aufgaben nicht verdecken.

Bei der Abnahme können Sie nacheinander Folgendes prüfen:

Wenn sich die herabgestuften unterstützenden Aufgaben bis zum nächsten Build aufstauen, hat sich das Problem von Ressourcenkonkurrenz zu unzureichendem Durchsatz verlagert. Reduzieren Sie in diesem Fall die Anzahl der Hintergrundaufgaben, schränken Sie deren Verarbeitungsumfang ein oder verschieben Sie sie in eine separate Phase nach Abschluss des Builds, statt den Nice-Wert weiter zu erhöhen. Welche Konfigurationen für SureVM-Knoten konkret verfügbar sind, sollte in der Konsole geprüft werden. Unabhängig von der gewählten Konfiguration lässt sich eine Strategie, die zuerst den kritischen Pfad identifiziert und erst danach das Scheduling verändert, in der Regel leichter überprüfen und zurücknehmen als eine globale Anpassung.

Häufig gestellte Fragen

Begrenzen nice und taskpolicy die CPU-Nutzung auf einen festen Wert?

Nein. Beide beeinflussen die Planung und Hintergrundbehandlung, setzen aber keine harte Ressourcenquote. Build-Parallelität, Test-Shards und Hintergrundjobs müssen zusätzlich begrenzt werden.

Welche CI-Aufgaben eignen sich für eine Hintergrundrichtlinie?

Typische Kandidaten sind Log-Komprimierung, Prüfsummen, das Aufräumen alter Artefakte und nicht blockierende Indizierung. Kompilierung, Linken und Tests sollten normale Priorität behalten.

SureVM Cloud-Mac

Wählen Sie exklusive physische Mac-Knoten für Builds, Entwicklung und Experimente.

Vergleichen Sie zwei Mac mini M4-Konfigurationen, vier Mietlaufzeiten und fünf buchbare Knoten, und treffen Sie Ihre Wahl passend zu Ihrem Workflow.

Mietoption auswählen