Provisionierung
Was die Engine im Schritt Infrastruktur installiert.
Der Schritt Infrastruktur ist der Ort, an dem alles auf dem Host aufgebaut wird. Die Engine installiert nacheinander, mit live übertragenem Fortschritt:
- k3s — leichtgewichtiges und produktionsreifes Kubernetes (skaliert später auf Multi-Node).
- Ingress (Traefik) + cert-manager — Routing und automatisches TLS via Let's Encrypt.
- PostgreSQL und Redis in-cluster — die endgültige Datenbank und die Queue/Pub-Sub für Logs.
- Registry für Artefakte und die CI-Runner.
- Serverless-Laufzeit des Deployments.
- Die endgültigen Images von api und web (Deployment + Migrationen + Seed des Super-Admins als In-Cluster-Jobs).
equantic-space-* (Namespace equantic-space, Deployments equantic-space-api / equantic-space-platform, Datenbank equantic-space-db).CI-Runner
Pipeline-Jobs laufen als isolierte Pods im Cluster — ein Pod pro Job, mit eigenen CPU-/Speicher-Limits, sodass ein schwerer Job weder die Plattform noch seine Nachbarn aushungern kann. Die Kapazität wird aus dem Cluster selbst abgeleitet (mit stets reserviertem Spielraum für die Control Plane): Jobs jenseits der Obergrenze warten in der Queue, und ein neuer Node in der Fleet erhöht die CI-Kapazität automatisch. Hat der Cluster beim Anlegen des Pods noch keinen Platz für einen Job, wartet auch dieser und meldet das im Log, statt zu scheitern. Sein timeout-minutes zählt erst, sobald der Pod existiert.
Jeder Container im Pod eines Jobs ist begrenzt — der Job selbst, seine services:-Sidecars und der Docker-Daemon pro Job tragen explizite Obergrenzen, und die Festplatte des Jobs hat eine eigene Größe (siehe unten). Ein außer Kontrolle geratener Job verdrängt so sich selbst, statt den Node unter Druck zu setzen. Das timeout-minutes des Jobs wird außerdem direkt auf den Pod gestempelt: Selbst wenn der Plattform-Prozess mitten im Job stirbt, räumt der Cluster den Pod von allein ab.
docker build (oder Testcontainers) in Jobs benötigt? Übergib EQS_RUNNER_DIND=true an den Installer — EQS_RUNNER_DIND=true bash install.sh, bei einer Erstinstallation oder einem Update. Jeder Job erhält dann seinen eigenen privaten Docker-Daemon, der nie zwischen Jobs oder Workspaces geteilt wird. Das gilt für jeden Job der Installation, und der Daemon ist privilegiert — schalte ihn bewusst ein.Kapazität einstellen
Die Ableitung ist ein guter Standard, kein Urteil: Unter Einstellungen → Plattform → CI-Kapazität entscheiden Sie, wie viel der Maschine der Runner nehmen darf — jede Änderung zeigt vorab, was sie auf diesem Cluster einbringt.
Das Feld neben den Einstellungen beantwortet die Frage, die die Zahlen allein nie beantwortet haben: warum nur so viele? Für den Knoten, den es liest, zeigt es, was der Knoten hat, was die Plattform belegt (ihre gemessene Nutzung plus den Spielraum der Reserve, nicht nur den Prozentsatz), was für CI bleibt, den Anteil jedes Jobs und die Grenze, die die Anzahl bestimmt hat: die Ziel-CPU pro Job, die Speicheruntergrenze, die feste Obergrenze, eine feste Größe oder die Zulassungsgrenze. Bei mehreren Knoten wählen Sie, welcher gelesen wird, und die Summe der Flotte steht daneben.
Zwei Regler wiegen am schwersten. Die Plattform-Reserve ist der Anteil jedes Knotens, den der CI nie anfasst: Senken gibt dem CI insgesamt mehr, doch dann teilen mehr Plätze den Speicher, und jeder Job kann kleiner ausfallen. Und „nicht starten unter" ist eine Aufnahmegrenze: Die Obergrenze eines Jobs wird beim Anlegen des Pods festgeschrieben, deshalb kann ein kurzer Einbruch des freien Speichers einem zweistündigen Build ein Limit verpassen, unter dem er nicht fertig wird. Unterhalb der Grenze bietet der Knoten keinen Platz und der Job wartet in der Warteschlange — spät ist besser als verkrüppelt.
Die Reserve hat zudem eine absolute Untergrenze — mindestens 2 Kerne und 3 GiB bleiben bei der Control Plane, egal was der Prozentsatz sagt (auf sehr kleinen Hosts senkt sie sich selbstständig ab). Ein dedizierter Runner-Node, auf dem keine Plattform läuft, kann die Untergrenze über EQS_RUNNER_MIN_PLATFORM_RESERVE_CPU / EQS_RUNNER_MIN_PLATFORM_RESERVE_MEMORY_GI auf null setzen.
EQS_RUNNER_*-Variable versorgt nur eine Installation, die hier noch nie gespeichert hat. Die Änderung greift innerhalb eines Budget-Zyklus (30 s), ohne Neustart. Ein Feld leeren gibt genau diese Entscheidung an die Plattform zurück, und die Zeile unter dem Label sagt immer, woher der Wert stammt.Speicher, der mit dem Job wächst
Ein Job bekam einen Speicheranteil, der bei der Erstellung seines Pods festgelegt wurde und sein ganzes Leben lang fest blieb. Diese Zahl ist eine Vermutung über eine Kurve: ein Build ist klein beim Installieren, groß beim Typprüfen von allem auf einmal und wieder klein beim Packen. Zu hoch geraten und der Knoten ist verschwendet, zu niedrig und ein zweistündiger Build stirbt bei 95%.
Mit elastischem Speicher (standardmäßig an) beobachtet die Plattform jeden laufenden Job und hebt seinen Speicher im Betrieb an, wenn er sich seiner Obergrenze nähert und der Knoten Platz hat. Request und Limit steigen zusammen, also behält der Pod seine QoS-Klasse und der Scheduler kennt weiterhin die Wahrheit, und das Budget sieht die neue Größe, also wird der nächste Job nicht in den Speicher gelassen, den dieser gerade genommen hat.
Es wächst nur in den Platz, den dieselbe Arithmetik der Zulassung sieht: nie in die Reserve der Plattform, nie in den Anteil eines anderen Jobs. Die Wachstumsgrenze begrenzt einen einzelnen Job auf einem großen Knoten. Beides steht in Einstellungen → Plattform → CI-Kapazität.
exit 137 nicht ab: eine einzelne Allokation, die größer ist als der verbleibende Platz, tötet den Container weiterhin zwischen zwei Messungen, und ein Knoten ohne freien Platz hat nichts zu geben. Es braucht Kubernetes 1.33 oder neuer für das Resize im Betrieb — ein älterer Cluster behält den festen Anteil wie bisher.Eine Festplatte pro Job
Ein Job hat eine einzige Festplatte, wie auf einem GitHub-Runner: sein Workspace, die Images und der Build-Cache von Docker und alles andere, was er schreibt, teilen sich denselben Platz. Es sind mindestens 14 GB, was GitHub für seinen Runner dokumentiert, und meist mehr: Die Plattform misst den freien Platz jedes Nodes, hält die Freiplatz-Untergrenze ein, die der CI-Speicher verteidigt (Einstellungen → Plattform → CI-Speicher, standardmäßig 15 %), und teilt den Rest unter den laufenden Jobs auf. Wenn die Festplatte zuerst ausgeht, entscheidet sie, wie viele Jobs laufen, und das Panel neben den Feldern sagt es.
Ein Job, der über seine Festplatte hinaus schreibt, wird vom Node verdrängt, und sein Log sagt, welche Grenze er überschritten hat und wo sie eingestellt wird. Einstellungen → Plattform → CI-Kapazität legt die Untergrenze fest, mit der ein Job gezählt wird, oder fixiert die Festplatte jedes Jobs.
Läufe, die nie enden
Die Obergrenze gilt auch von außen. Wenn der Cluster nie Platz hat — meist eine festgesetzte Job-Größe, die kein Knoten bieten kann — würden die Läufe dahinter tagelang in der Warteschlange stehen, und ein Job, dessen Ausführer verschwunden ist, bliebe für immer in Arbeit.
Also gibt die Plattform von sich aus auf. Ein Lauf, der über die Warteschlangen-Obergrenze hinaus (standardmäßig 24 h) auf einen Runner wartet, und ein Lauf, der über die Lauf-Obergrenze hinaus (12 h) in Arbeit ist, werden als abgelaufen beendet; ebenso ein einzelner Job über der Job-Obergrenze (6 h), sobald das, was ihn ausgeführt hat, nicht mehr mitzählt. Die eigene timeout-minutes eines Jobs schlägt Letztere immer. Alle drei stehen in Einstellungen → Plattform → CI-Kapazität, und 0 schaltet eine davon ab.
Und wenn die Warteschlange stillsteht, weil überhaupt nichts starten darf, sagt der Läufe-Bildschirm das, mitsamt der Größe, die ein Job bräuchte.
Eine Runner-Maschine ausschalten
Ein Windows- oder macOS-Runner ist eine VM, die diese Installation betreibt, und solange niemand etwas von ihr will, sind das 8 GiB, die nichts tun. Auf der Installation, für die das hier geschrieben wurde, hielten die beiden 14,2 GiB und rund 2,4 Kerne sechzehn Stunden lang, ohne einen einzigen Job.
Einstellungen → Plattform → Runner-Maschinen hat jetzt einen Schalter für jede Maschine, die die Plattform selbst betreibt. Beim Ausschalten wird zuerst das Gastsystem um das Herunterfahren gebeten und erst danach die VM gestoppt, was sauberer und sicherer ist als den Prozess zu töten, und der Speicher geht sofort an den Rest der Installation zurück.
Eine Maschine, die Sie über ihre Adresse registriert haben, bleibt unberührt: die Plattform verbindet sich mit ihr, sie schaltet Ihre Hardware nicht aus.
Eine Maschine, die sich selbst ausschaltet
Jede Maschine, die die Plattform betreibt, hat die Einstellung Ausschalten nach … Min Leerlauf, neben ihrem Schalter. 0 ist die Voreinstellung und heißt nie — eine VM, die Minuten zum Starten braucht, will nicht jede Installation nach einer ruhigen Mittagspause gestoppt sehen.
Mit einer gesetzten Grenze schaltet die Plattform die Maschine ab, sobald so lange nichts darauf lief, und startet sie von selbst wieder, wenn ein Job dieses Betriebssystem verlangt. Der Job wartet im eigenen Log, während sie hochfährt, statt an einer Verbindung zu etwas zu scheitern, das nicht läuft.
Logs und Aufbewahrung
Ein einzelner Step darf bis zu 50.000 Log-Zeilen speichern (EQS_RUNNER_MAX_LOG_LINES_PER_STEP; 0 hebt die Grenze auf). Jenseits der Grenze läuft der Step bis zu seinem echten Ende weiter — ein unübersehbarer Kürzungsmarker wird geschrieben und weitere Ausgabe verworfen, was Datenbank und Live-Stream vor einem außer Kontrolle geratenen set -x schützt.
Step-Logs verfallen nach 90 Tagen, einstellbar in der Ansicht CI-Speicher weiter unten (0 bewahrt sie für immer auf). Runs, Jobs, Ergebnisse und Annotationen bleiben erhalten — die Historie bleibt einsehbar, nur die rohe Mitschrift läuft ab. Auch der Blob-Garbage-Collector und der Retention-Runner der Registry sind standardmäßig aktiv (jeweils abwählbar per EQS_REGISTRY_*_ENABLED=false): Nicht mehr referenzierte Blobs werden von selbst freigegeben, und eine auf der Artefakte-Seite gespeicherte Aufbewahrungsrichtlinie läuft einfach.
CI-Speicher
Der Actions-Cache und die Artefakte der Läufe liegen im Speicher der Plattform, und auf einer Installation mit nur einem Server ist dieser Speicher die Festplatte des Servers selbst. Einstellungen → Plattform → CI-Speicher hält sie in einer Richtlinie, die stündlich durchgesetzt wird:
- Cache: die Regeln von GitHub. Ein Eintrag, den seit mehr als 7 Tagen niemand wiederhergestellt hat, wird entfernt, und ein Repository über 10 GB verliert zuerst die am längsten nicht genutzten Einträge.
- Artefakte: höchstens 90 Tage aufbewahrt (1 bis 400). Das
retention-dayseines Workflows gilt, wenn es weniger verlangt, nie mehr. Ein niedrigerer Wert verkürzt auch bereits gespeicherte Artefakte. - Gesamtlimit: Cache und Artefakte zusammen dürfen standardmäßig ein Fünftel der Festplatte belegen (oder eine Anzahl GB;
0für keins). Darüber wird zuerst entfernt, was am längsten nicht genutzt wurde, Cache wie Artefakt, und die Artefakte eines noch laufenden Laufs werden nie entfernt. Regeln nach Alter allein halten keine Festplatte: Ein Repository, das bei jedem Build ein großes Paket hochlädt, hält alles unter 90 Tagen und füllt sie trotzdem. Mit S3-Speicher gibt es keine Festplatte zu messen, also legen Sie das Limit in GB fest. - Frei halten: standardmäßig 15 % der Festplatte. Fällt die Festplatte aus irgendeinem Grund darunter, gibt die CI genau das Fehlende zurück, bevor Kubernetes beginnt, Pods zu verdrängen. Alle fünf Minuten geprüft.
Die Ansicht zeigt, was die CI im Verhältnis zum Limit belegt, wer am meisten belegt und was die letzten Durchläufe entfernt haben, Fehlschläge samt Grund; eine eingegebene Zahl wird vor dem Speichern gegen diese Festplatte vorgerechnet. Jetzt aufräumen wendet ein gerade gesenktes Limit an, ohne auf die volle Stunde zu warten. Die Variablen EQS_CI_CACHE_IDLE_DAYS, EQS_CI_CACHE_REPO_LIMIT_GB, EQS_CI_ARTIFACT_RETENTION_DAYS, EQS_CI_LOG_RETENTION_DAYS, EQS_CI_STORAGE_LIMIT_GB, EQS_CI_DISK_FLOOR_PERCENT dienen nur als Startwert für eine Installation, die diese Ansicht nie gespeichert hat.
Der eigene Anteil der Plattform
Die Plattform bemisst ihre eigenen Pods am Host — Datenbank, api und web deklarieren Resource Requests, die bei der Installation abgeleitet (und von Updates nachgetragen) werden. Unter Last entscheidet der Kernel so zugunsten der Plattform, statt einen CI-Job und die Datenbank als Gleichrangige zu behandeln. Pro Komponente übersteuerbar über Variablen im Stil von EQS_PG_CPU_REQUEST, wenn Sie Ihre Maschine besser kennen als die Formel.