Xcode-Speicher auf einem Cloud-Mac überwachen und sicher bereinigen

DevOps & CI/CD ·ca. 6 Min. Lesezeit

Xcode-Speicher auf einem Cloud-Mac überwachen und sicher bereinigen

Bei einem Cloud-Mac, der dauerhaft für Archivierungen und automatisierte Tests eingesetzt wird, liegt die häufigste Fehlerursache nicht unbedingt im Code. Xcode schreibt fortlaufend Daten in DerivedData, Archive, Geräte-Supportdateien und Simulatorverzeichnisse. Ist das Laufwerk nahezu voll, kann der Compiler irreführende Schreibfehler melden; auch der Signierungsschritt hinterlässt unter Umständen nur unvollständige Artefakte. Eine zuverlässige Strategie besteht deshalb nicht darin, regelmäßig sämtliche Verzeichnisse zu leeren. Stattdessen sollten zunächst die tatsächliche Belegung ermittelt, eine Kapazitätsschranke für Builds eingerichtet und die Daten anschließend nach ihrer Wiederherstellbarkeit gestaffelt bereinigt werden.

Ausgangswerte für die Speicherbelegung erfassen

Erfassen Sie zuerst den freien Systemspeicher und prüfen Sie anschließend die Entwicklungsverzeichnisse. df zeigt, wie viel Speicher im Dateisystem noch beschreibbar ist. Mit du lässt sich ermitteln, welche Verzeichnisse den Platz belegen. Die beiden Werkzeuge erfüllen unterschiedliche Aufgaben und können einander nicht ersetzen.

df -Pk /

for path in \
  "$HOME/Library/Developer/Xcode/DerivedData" \
  "$HOME/Library/Developer/Xcode/Archives" \
  "$HOME/Library/Developer/CoreSimulator" \
  "$HOME/Library/Developer/Xcode/iOS DeviceSupport"
do
  if [ -e "$path" ]; then
    du -sk "$path"
  fi
done

Sinnvoll sind jeweils eine Messung vor einem sauberen Build, nach Abschluss der Archivierung und nach dem Ende der Tests. Diese drei Werte zeigen den Speicherzuwachs eines einzelnen Jobs und verhindern, dass normale Lastspitzen fälschlich als Speicherleck eingestuft werden. Um besonders große Verzeichnisse innerhalb von DerivedData zu finden, führen Sie Folgendes aus:

du -sk "$HOME/Library/Developer/Xcode/DerivedData"/* 2>/dev/null \
  | sort -nr \
  | head -20

Der von APFS ausgewiesene freie Speicher kann durch löschbare Daten beeinflusst werden. Eine Kapazitätsschranke für Builds sollte die von df gemeldeten verfügbaren Blöcke auswerten. Die nominelle Größe einzelner Verzeichnisse reicht nicht aus, um den erfolgreichen Abschluss eines Jobs sicher vorherzusagen.

Eine klare Kapazitätsschranke für Builds definieren

Der Schwellenwert sollte aus den tatsächlich gemessenen Spitzenwerten abgeleitet und nicht allein nach Erfahrungswerten fest eingetragen werden. Verwenden Sie zunächst 30GB als Ausgangspunkt und erfassen Sie den niedrigsten freien Speicher während einer vollständigen Archivierung. Nach mehreren stabilen Durchläufen kann der Schwellenwert auf das 1,5-Fache des Spitzenzuwachses angepasst werden. Der zusätzliche Platz für exportierte Artefakte muss ebenfalls berücksichtigt werden.

Das folgende Skript kann ganz am Anfang der Pipeline ausgeführt werden. Reicht der Speicher nicht aus, beendet es sich mit dem Statuscode 75. Der Scheduler kann den Job dadurch als vorübergehend nicht ausführbar markieren, statt einen Build zu starten, der mit hoher Wahrscheinlichkeit fehlschlägt.

#!/bin/zsh
set -euo pipefail

minimum_kb=$((30 * 1024 * 1024))
available_kb=$(df -Pk / | awk 'NR == 2 {print $4}')

if (( available_kb < minimum_kb )); then
  printf 'Insufficient disk capacity: %s KB available
' "$available_kb"
  exit 75
fi

printf 'Disk capacity check passed: %s KB available
' "$available_kb"

Der Schwellenwert sollte für jeden Knoten separat gespeichert werden. Unterschiedliche Xcode-Versionen, Simulatorkombinationen und Projektgrößen erzeugen jeweils eigene Lastspitzen. Die Messwerte eines kleinen Projekts dürfen daher nicht als Kapazitätsnachweis für einen großen Workspace dienen.

Jeden Build auf ein eigenes Verzeichnis begrenzen

Ein gemeinsam genutztes standardmäßiges DerivedData-Verzeichnis verursacht nicht nur Kapazitätsprobleme. Es lässt sich außerdem kaum zuverlässig feststellen, welches Verzeichnis zu welchem Job gehört. Wird dem Workspace ein eigener Pfad zugewiesen, können seine Daten nach Abschluss des Jobs gezielt gelöscht werden, ohne parallel laufende Builds zu beeinträchtigen.

job_root="$HOME/build-jobs/$BUILD_ID"
derived_data="$job_root/DerivedData"
archive_path="$job_root/artifacts/App.xcarchive"

mkdir -p "$job_root/artifacts"

xcodebuild \
  -workspace App.xcworkspace \
  -scheme App \
  -configuration Release \
  -derivedDataPath "$derived_data" \
  -archivePath "$archive_path" \
  archive

BUILD_ID sollte vom Jobsystem bereitgestellt werden und ausschließlich Buchstaben, Ziffern, Punkte, Unterstriche oder Bindestriche enthalten. Vor dem Löschen muss außerdem geprüft werden, ob sich der Zielpfad tatsächlich unterhalb von build-jobs befindet. So wird verhindert, dass eine leere Variable den Löschbereich versehentlich auf das Home-Verzeichnis ausweitet.

Daten nach Wiederherstellbarkeit gestaffelt bereinigen

Die Bereinigung sollte mit Daten beginnen, die sich neu erzeugen lassen. Archive, die eine manuelle Prüfung erfordern, kommen erst zuletzt an die Reihe.

Stufe Daten Empfohlene Maßnahme Wichtigste Grenze
1 DerivedData abgeschlossener Jobs Nach Jobverzeichnis löschen Sicherstellen, dass kein Build-Prozess darauf zugreift
2 Nicht mehr verfügbare Simulatoreinträge Mit simctl bereinigen Das Verzeichnis Devices nicht direkt löschen
3 Alte Geräte-Supportdateien Anhand der tatsächlich getesteten Versionen prüfen Noch für das Debugging benötigte Systemversionen behalten
4 xcarchive und dSYM Nach manueller Prüfung auslagern Veröffentlichte Versionen müssen nachvollziehbar bleiben

Nicht mehr gültige Simulatoren sollten mit dem dafür vorgesehenen Systemwerkzeug entfernt werden:

xcrun simctl delete unavailable

Leeren Sie CoreSimulator/Devices nicht direkt. Wenn der Verzeichnisinhalt nicht mehr mit dem Zustand des Simulatordienstes übereinstimmt, entstehen beim späteren Erstellen und Starten von Geräten Probleme, die deutlich schwerer zu diagnostizieren sind. Auch Archive sollten nicht pauschal gelöscht werden, nur weil sie älter als eine bestimmte Anzahl von Tagen sind. Ein xcarchive kann die dSYM-Dateien einer veröffentlichten Version enthalten. Erfassen Sie deshalb zuerst Version, Build-Nummer und Auslagerungsort, bevor Sie die lokale Kopie entfernen.

Drei typische Fehler bei der automatischen Bereinigung vermeiden

Erstens dürfen gemeinsam genutzte Verzeichnisse nicht während eines laufenden Builds bereinigt werden. Selbst eine offensichtlich alte Datei kann vom Compiler noch über einen Index oder ein Zwischenprodukt referenziert werden. Der Bereinigungsjob sollte daher eine knotenweite Sperre anfordern oder ausschließlich eigenständige Arbeitsverzeichnisse bearbeiten, deren Jobs bereits als abgeschlossen markiert wurden.

Zweitens ist eine erfolgreich ausgeführte Löschoperation nicht automatisch mit tatsächlich freigegebenem Speicher gleichzusetzen. Führen Sie nach dem Löschen erneut df -Pk / aus und prüfen Sie, ob die Zahl der verfügbaren Blöcke wirklich gestiegen ist. Bleibt sie unverändert, suchen Sie nach Dateien, die noch von Prozessen geöffnet sind, statt denselben Löschbefehl wiederholt auszuführen.

Drittens sollte ein Skript nicht selbst entscheiden, ob ein Archiv wichtig ist. Unbeaufsichtigte Jobs dürfen lediglich Löschkandidaten auflisten und beispielsweise Verzeichnisgröße, letzten Änderungszeitpunkt und zugehörige Build-Nummer erfassen. Ob ein Eintrag ausgelagert oder gelöscht wird, muss anhand der Release-Dokumentation entschieden werden.

Die Bereinigung mit einem reproduzierbaren Build abnehmen

Führen Sie nach der Bereinigung mindestens eine Archivierung aus, die dem regulären Produktionsjob entspricht. Erfassen Sie für die Abnahme den Speicher zu Beginn, den niedrigsten freien Speicher, den Speicher am Ende, den Archivpfad und den Exit-Status. Prüfen Sie außerdem, ob das .xcarchive vollständig erzeugt wurde. Wenn der Knoten auch Simulatortests ausführt, starten Sie jede beibehaltene Version aus der Testmatrix mindestens einmal. Vergewissern Sie sich dabei, dass sich die Geräte ordnungsgemäß erstellen, starten und herunterfahren lassen.

Die abschließende Checkliste kann kurz bleiben: Die Kapazitätsschranke vor dem Build wurde passiert; der Job verwendet ein eigenes DerivedData-Verzeichnis; nach seinem Ende sind keine Compilerprozesse zurückgeblieben; Archiv und dSYM wurden erfasst; simctl list enthält keine fehlerhaften Geräte; der verbleibende Speicher liegt über dem Schwellenwert für den nächsten Job. Erst wenn alle diese Bedingungen erfüllt sind, ist die Bereinigung vollständig abgeschlossen.

Häufig gestellte Fragen

Wie viel freien Speicher sollte ein Xcode-Build-Knoten mindestens haben?

Als Startwert sind 30GB vor dem Build sinnvoll. Danach sollte der Grenzwert anhand des höchsten gemessenen Verbrauchs eines vollständigen Archivs angepasst werden; große Projekte benötigen mindestens das 1,5-Fache dieses Spitzenwerts.

Darf das gesamte DerivedData-Verzeichnis gelöscht werden?

Ja, sofern garantiert kein Build darauf zugreift. Sicherer ist ein eigener DerivedDataPath pro Arbeitsbereich, damit ausschließlich abgeschlossene Auftragsverzeichnisse entfernt werden.

Welche Xcode-Daten gehören nicht in eine automatische Bereinigung?

Laufende Archive, noch nicht gesicherte xcarchive-Pakete, dSYM-Dateien veröffentlichter Versionen und aktiv verwendete Simulatorgeräte müssen von automatischen Löschregeln ausgeschlossen bleiben.

Dedizierter physischer Knoten

Eine reproduzierbare Umgebung auf demselben Cloud-Mac behalten

Wählen Sie Modell, Region und Mietdauer fest aus und erledigen Sie Entwicklungs- oder Automatisierungsaufgaben mit vollständiger grafischer macOS-Oberfläche und Kommandozeile.

Cloud-Mac jetzt mieten