Onze health check heeft een gezonde modelserver acht keer per dag herstart
Ops
Op 22 juli heeft een watchdog onze lokale modelserver acht keer herstart. Geen van die herstarts had een crash als oorzaak. De check die de modelserver online moest houden, was juist de oorzaak van de uitval.
Wat er gebeurde
De modelserver draaide vLLM op één NVIDIA GB10-machine en leverde ongeveer 8 tokens per seconde voor één verzoek. Elke vijf minuten stuurde een watchdog een echt chatverzoek en wachtte maximaal 45 seconden op het antwoord. Vijf mislukte checks op rij betekende een vastgelopen server, en de watchdog startte die opnieuw op. Een herstart nam de modelserver enkele minuten uit bedrijf terwijl ongeveer 19 GB aan gewichten opnieuw werd geladen.
Die dag hield een batchtaak twee tot vier verzoeken minutenlang bezig op de server. Het verzoek van de watchdog stond in de wachtrij achter deze verzoeken, liep op tijd uit en werd als mislukt geteld. Na vijf van zulke checks werd een gezonde server herstart. We hadden een "busy"-vlagbestand dat de watchdog respecteerde, maar de batchtaak zette deze vlag nooit.
Hoe één onterechte herstart leidde tot de volgende
Elke herstart liet de lopende verzoeken falen met een 500-fout ("cannot schedule new futures after shutdown"). Een tweede watchdog telde 500-fouten en startte de server opnieuw op bij te veel fouten; hoe meer fouten hij zag, hoe sneller hij ingreep. Zo zorgde elke onterechte herstart dat de volgende eerder kwam. In totaal konden vier aparte watchdogs die server herstarten, op timers van 5, 10, 15 en 30 minuten.
De regel die we eruit hebben gehaald
Een health check mag geen gebruikmaken van wat hij bewaakt. De schaarse bron van een modelserver zijn de slots voor lopende verzoeken. Een check die een van die slots inneemt, of erop wacht, maakt deel uit van de belasting die hij juist moet beoordelen. Check goedkoop, en start alleen opnieuw op als je kunt aantonen dat de server vastloopt.
De check die de oude verving
vLLM publiceert zijn eigen tellers op /metrics. De nieuwe check leest die eerst:
- Lopende verzoeken en de engine die voortgang boekt: gezond en bezet. Er wordt geen testverzoek verzonden.
- Lopende verzoeken en geen voortgang tussen twee lezingen: vastgelopen. Dit is het geval dat nog steeds een herstart veroorzaakt.
- Niets lopend: inactief. Alleen dan vraagt de check om een kort antwoord, dat een inactieve engine direct geeft.
De eerste versie telde gegenereerde tokens als voortgang. Deze versie doorstond acht tests, en bij de eerste live-run vond de check de server bezet en gezond: 2 lopende verzoeken, 107 nieuwe tokens tussen de twee lezingen.
Wat we sindsdien hebben gewijzigd
- Engine-stappen, geen output-tokens. Terwijl vLLM een lange prompt leest, produceert het een tijdlang geen output-tokens, maar wel stappen. Het tellen van stappen (de telling van vllm:iteration_tokens_total) voorkomt dat een lange prompt eruitziet als een vastgelopen server.
- Twee wachtmomenten voordat je van 'vastgelopen' spreekt, zodat een korte pauze niet als fout telt.
- Een herstart tussen de twee uitlezingen wordt opgepikt door de processtarttijd in dezelfde metrics; de controle vraagt dan om een antwoord in plaats van tellers van twee verschillende processen met elkaar te vergelijken.
- Een proxy wordt alleen via /models gecontroleerd. Achter een least-connection-proxy kan elke uitlezing van /metrics van een andere backend komen, zoals we ontdekten toen we een kopie van het model op elk van de twee machines draaiden. De backends worden rechtstreeks gecontroleerd.
Het verschil is makkelijk te zien. Toen we deze notitie schreven, draaiden beide modelserver elk 12 requests, hun limiet. Een testrequest zou in de wachtrij hebben gestaan. De nieuwe controle meldde beide als bezet en gezond, met 126 en 101 tokens per seconde, zonder er een te versturen.
Factory Floor 1.0.2 bevat deze controle als models/llm-health.py, met tests voor idle, bezette, vastgelopen en herstarte servers, een server zonder metrics, een proxy en een dode backend.