Nasza kontrola stanu uruchomiła ponownie zdrowy serwer modeli osiem razy w ciągu jednego dnia
Operacje
22 lipca watchdog uruchomił ponownie nasz lokalny serwer modeli osiem razy. Żaden z tych restartów nie był spowodowany awarią. Kontrola, która miała utrzymywać model w działaniu, była tym, co go wyłączało.
Co się stało
Serwer modeli uruchamiał vLLM na jednej maszynie NVIDIA GB10 i generował około 8 tokenów na sekundę dla pojedynczego żądania. Co pięć minut watchdog wysyłał do niego rzeczywiste żądanie czatu i czekał na odpowiedź do 45 sekund. Pięć nieudanych kontroli z rzędu oznaczało zawieszony serwer, a watchdog go restartował. Restart zabierał model na kilka minut, podczas gdy ładowało się ponownie około 19 GB wag.
Tego dnia zadanie wsadowe utrzymywało dwa do czterech żądań zajętych na serwerze przez kilka minut naraz. Żądanie watchdoga czekało w kolejce za nimi, wyczerpywało limit czasu i było liczone jako błąd. Po pięciu takich kontrolach zdrowy serwer został zrestartowany. Mieliśmy plik flagi „busy”, który watchdog respektował, ale zadanie wsadowe nigdy go nie ustawiało.
Jak jeden fałszywy restart doprowadził do następnego
Każdy restart kończył się błędem 500 dla trwających żądań („cannot schedule new futures after shutdown”). Drugi watchdog liczył błędy 500 i restartował serwer, gdy było ich zbyt wiele, a im więcej błędów widział, tym szybciej reagował. Tak więc każdy fałszywy restart przyspieszał następny. Łącznie cztery niezależne watchdogi mogły restartować ten serwer, na timerach co 5, 10, 15 i 30 minut.
Zasada, którą z tego wyciągnęliśmy
Kontrola stanu nie może korzystać z zasobów, które chroni. Deficytowym zasobem serwera modeli są sloty dla trwających żądań. Kontrola, która zajmuje jeden z tych slotów lub na niego czeka, jest częścią obciążenia, które ma oceniać. Sprawdzaj tanio i restartuj tylko wtedy, gdy możesz wykazać, że serwer jest zawieszony.
Kontrola, która go zastąpiła
vLLM publikuje własne liczniki pod adresem /metrics. Nowa kontrola najpierw je odczytuje:
- Żądania w trakcie realizacji i silnik robiący postępy: zdrowy i zajęty. Nie wysyłane jest żadne żądanie testowe.
- Żądania w trakcie realizacji i brak postępu między dwoma odczytami: zawieszony. To przypadek, który nadal powoduje restart.
- Nic nie jest w trakcie realizacji: bezczynny. Tylko wtedy kontrola prosi o krótką odpowiedź, którą bezczynny silnik udziela natychmiast.
Pierwsza wersja liczyła wygenerowane tokeny jako postęp. Przeszła osiem testów, a przy pierwszym uruchomieniu na żywo wykryła serwer jako zajęty i zdrowy: 2 żądania w trakcie realizacji, 107 nowych tokenów między dwoma odczytami.
Co zmieniliśmy od tego czasu
- Kroki silnika, nie tokeny wyjściowe. Gdy vLLM czyta długi prompt, przez pewien czas nie generuje tokenów wyjściowych, ale nadal wykonuje kroki. Liczenie kroków (liczba vllm:iteration_tokens_total) sprawia, że długi prompt nie wygląda jak zawieszony serwer.
- Dwa odczekania przed uznaniem za „zawieszony”, aby krótka przerwa nie była traktowana jako awaria.
- Restart między dwoma odczytami jest wykrywany na podstawie czasu uruchomienia procesu w tych samych metrykach; sprawdzenie prosi wtedy o odpowiedź zamiast porównywać liczniki z dwóch różnych procesów.
- Proxy jest sprawdzane tylko przez /models. Za proxy z równoważeniem obciążenia metodą least-connection każdy odczyt /metrics może pochodzić z innego backendu, co odkryliśmy, gdy uruchomiliśmy kopię modelu na dwóch maszynach. Backendy są sprawdzane bezpośrednio.
Różnica jest łatwa do zauważenia. Gdy pisaliśmy tę notatkę, oba nasze serwery modeli obsługiwały po 12 żądań, czyli na swoim limicie. Żądanie testowe czekałoby w kolejce. Nowe sprawdzenie zgłosiło oba jako zajęte i sprawne, przy 126 i 101 tokenach na sekundę, bez wysyłania żadnego żądania.
Factory Floor 1.0.2 zawiera to sprawdzenie jako models/llm-health.py, wraz z testami dla serwerów bezczynnych, zajętych, zawieszonych i zrestartowanych, serwera bez metryk, proxy oraz martwego backendu.