Adrian RomoAdrian Romo
Alle Texte
Projekt-Log 3 Min. Lesezeit

Sekunden der Arbeit, Stunden des Aufenthalts

Mein Morgenbriefing begann zu scheitern. Ollama war zwar erreichbar, lieferte aber HTTP 500 zurück, weil ein 21 Sekunden langer Bild-Render auch Stunden später noch 6,6 GB VRAM belegte.

Zwei Morgen hintereinander hat mein lokaler Observer ein unvollständiges Briefing erzeugt und das Sprachmodell als nicht verfügbar gemeldet. Ollama lief. Der Service war gesund. Alle Checks zeigten, dass der Stack in Ordnung war.

Er gab HTTP 500 zurück, weil das Modell 14.191 MiB brauchte und die Karte nur 124 MiB frei hatte.

Eine GPU, mehrere Mieter

Eine einzelne 3090 hostet hier alles: das lokale LLM, das das Briefing schreibt, einen Cross-Encoder-Reranker, einen resident Worker für die Content-Pipeline und die Bildgenerierung für Social-Posts. Meistens ist das kein Problem, weil die meisten klein oder bursty sind.

Zwei Dinge waren nicht in Ordnung.

Das erste war ein Nachtjob, der in den Arbeitstag hineinlief — ein Job, der von einem Scheduler in die Warteschlange gestellt wird, erbt ein frisches Zeitbudget, nicht das verbleibende Zeitfenster, sodass eine Aufgabe, die vor Morgengrauen fertig sein sollte, vier Stunden in den Morgen hinein lief. Das hatte ich schon behoben und verifiziert, dass es hält.

Das zweite war das Interessante.

Einundzwanzig Sekunden Arbeit, ganztägiger Aufenthalt

Der Content-Scheduler rendert Cards und Carousels über einen lokalen Diffusionsserver. Das Rendering dauert etwa 21 Sekunden und verbraucht etwa 6,6 GB. Danach ist es fertig, loggt ein fröhliches „0 models unloaded“ und behält den VRAM.

Sekunden der Arbeit. Stunden des Aufenthalts. Und weil der Job erfolgreich war, meldete nirgendwo ein Service ein Problem — das einzige Symptom war ein anderer Service, der Stunden später keinen Platz mehr fand.

Hier mag ich ein Detail in der Benennung, weil es derselbe Fehler ist, den ich immer wieder finde: Der Timer wird beschrieben als Planung des Tagesinhalts, der Service als Veröffentlichung geplanter Inhalte, und was er tatsächlich tut, ist auf der GPU generieren. Nichts im Namen deutete darauf hin, dass es ein GPU-Mieter ist. Hättest du mich gefragt, welche Einheiten um die Karte konkurrieren, hätte ich ihn nicht genannt.

Die zweite Sache beheben: zwei Änderungen, beide zum Zurückgeben von Speicher

Keine davon ist der Nachtjob-Fix, der bereits erledigt war. Beide betreffen den residenten Render-Server.

Gib frei, was du nicht mehr brauchst. Der Scheduler ruft jetzt beim Beenden den free-Endpunkt des Diffusionsservers auf, geschützt durch die Bedingung, dass die Queue leer ist, sodass niemals Modelle entladen werden, während ein Job noch läuft. Das ist nicht fatal und bewahrt den ursprünglichen Exit-Code — eine Veröffentlichung darf niemals wegen einer Speicherfreigabe fehlschlagen.

Reserviere nicht, was du nie nutzt. Der Observer hatte ein 32k-Kontextfenster für einen Prompt angefragt, der etwa 7,6k Tokens misst, und reservierte dadurch einen 5.440 MiB großen Key-Value-Cache, den er nie anrührte. Das Halbieren des Kontexts reduzierte den Cache auf 2.720 MiB und den Gesamtbedarf von 14.191 MiB auf 11.327 MiB — und verschob das Modell von 37 von 41 Layern auf der GPU auf alle 41.

Der zweite Fix ist der, den es sich zu verinnerlichen lohnt. Ich habe keinen Speicher hinzugefügt oder einen Mieter entfernt. Ich habe aufgehört, Speicher auf Basis eines Default-Werts zu reservieren, den ich nie hinterfragt hatte. Der schlimmste realistische Tagesfall passt jetzt mit Luft nach oben, selbst wenn der erste Fix nie greift.

Die Fehlermeldung hat höflich gelogen

Vor all dem hat das Briefing das Modell als „nicht erreichbar“ gemeldet. Es war nicht nicht erreichbar. Es antwortete sofort, aber mit einer Ablehnung.

Das sind unterschiedliche Probleme mit unterschiedlichen Lösungen, und sie in einem Wort zusammenzufassen hat mir den ersten Morgen komplett gekostet. „Nicht erreichbar“ schickt dich dazu, Netzwerk, Ports und Container-Status zu prüfen. „Abgelehnt“ schickt dich dazu, zu prüfen, was angefragt wurde. Ich habe das Label in derselben Woche geändert, in der ich den Speicher angepasst habe.

Eine Fehlermeldung ist eine Diagnose, und eine falsche Diagnose im Log ist schlimmer als keine Diagnose, weil du ihr glaubst.

Geschrieben von

Adrian Romo

Senior Backend Engineer für skalierbare Python-APIs, AWS-Lambda-Architekturen, Voice-Systeme und Enterprise-Integrationen.

Weiter geht's

Wohin als Nächstes?

Stöbere durch weitere technische Texte, sieh dir die Engineering Case Studies an oder melde dich direkt.