Nossa verificação de integridade reiniciou um servidor de modelos saudável oito vezes em um dia
Operações
Em 22 de julho, um watchdog reiniciou nosso servidor de modelos local oito vezes. Nenhuma dessas reinicializações teve uma falha por trás. A verificação que deveria manter o modelo ativo foi o que o derrubou.
O que aconteceu
O servidor de modelos executava vLLM em uma máquina NVIDIA GB10 e produzia cerca de 8 tokens por segundo para uma única solicitação. A cada cinco minutos, um watchdog enviava uma solicitação de chat real e aguardava até 45 segundos pela resposta. Cinco verificações falhas seguidas significavam um servidor travado, e o watchdog o reiniciava. Uma reinicialização levava o modelo embora por vários minutos enquanto cerca de 19 GB de pesos eram carregados novamente.
Naquele dia, um trabalho em lote mantinha de duas a quatro solicitações ocupadas no servidor por minutos de cada vez. A solicitação do watchdog esperava na fila atrás delas, esgotava o tempo e contava como falha. Cinco verificações assim depois, um servidor saudável era reiniciado. Tínhamos um arquivo de sinalização "ocupado" que o watchdog respeitava, mas o trabalho em lote nunca o definia.
Como uma reinicialização falsa levou à próxima
Cada reinicialização falhava as solicitações em andamento com um erro 500 ("cannot schedule new futures after shutdown"). Um segundo watchdog contava erros 500 e reiniciava o servidor quando havia muitos, e quanto mais erros via, mais cedo agia. Assim, cada reinicialização falsa fazia a próxima vir mais cedo. No total, quatro watchdogs separados podiam reiniciar esse servidor, em timers de 5, 10, 15 e 30 minutos.
A regra que tiramos disso
Uma verificação de integridade não deve usar o que ela protege. O recurso escasso de um servidor de modelos são seus slots para solicitações em andamento. Uma verificação que ocupa um desses slots, ou espera por um, faz parte da carga que deveria avaliar. Verifique de forma barata e reinicie apenas quando puder mostrar que o servidor está travado.
A verificação que a substituiu
O vLLM publica seus próprios contadores em /metrics. A nova verificação os lê primeiro:
- Solicitações em execução e o motor fazendo progresso: saudável e ocupado. Nenhuma solicitação de teste é enviada.
- Solicitações em execução e sem progresso entre duas leituras: travado. Este é o caso que ainda reinicia.
- Nada em execução: ocioso. Só então a verificação pede uma resposta curta, que um motor ocioso dá imediatamente.
A primeira versão contava tokens gerados como progresso. Passou em oito testes e, na primeira execução ao vivo, encontrou o servidor ocupado e saudável: 2 solicitações em execução, 107 novos tokens entre as duas leituras.
O que mudamos desde então
- Passos do motor, não tokens de saída. Enquanto o vLLM lê um prompt longo, ele não produz tokens de saída por um tempo, mas ainda executa passos. Contar os passos (a contagem de vllm:iteration_tokens_total) evita que um prompt longo pareça um servidor travado.
- Duas esperas antes de "travado", para que uma pausa curta não conte como falha.
- Uma reinicialização entre as duas leituras é detectada pelo tempo de início do processo nas mesmas métricas; a verificação então solicita uma resposta em vez de comparar contadores de dois processos diferentes.
- Um proxy é verificado apenas através de /models. Atrás de um proxy de menor conexão, cada leitura de /metrics pode vir de um backend diferente, como descobrimos quando executamos uma cópia do modelo em cada uma das duas máquinas. Os backends são verificados diretamente.
A diferença é fácil de ver. Enquanto escrevíamos esta nota, ambos os nossos servidores de modelo estavam processando 12 requisições cada, seu limite. Uma requisição de teste teria ficado na fila. A nova verificação reportou ambos ocupados e saudáveis, a 126 e 101 tokens por segundo, sem enviar uma.
Factory Floor 1.0.2 inclui esta verificação como models/llm-health.py, com testes para servidores ociosos, ocupados, travados e reiniciados, um servidor sem métricas, um proxy e um backend morto.