Nuestra comprobación de estado reinició ocho veces un servidor de modelos sano en un día
Operaciones
El 22 de julio un watchdog reinició nuestro servidor de modelos local ocho veces. Ninguno de esos reinicios tuvo un fallo detrás. La comprobación que debía mantener el modelo activo fue lo que lo tumbó.
Qué ocurrió
El servidor de modelos ejecutaba vLLM en una máquina NVIDIA GB10 y producía unos 8 tokens por segundo para una sola petición. Cada cinco minutos un watchdog le enviaba una petición real de chat y esperaba hasta 45 segundos la respuesta. Cinco comprobaciones falladas seguidas significaban un servidor colgado, y el watchdog lo reiniciaba. Un reinicio dejaba el modelo fuera de servicio durante varios minutos mientras se volvían a cargar unos 19 GB de pesos.
Ese día un trabajo por lotes mantenía ocupadas entre dos y cuatro peticiones en el servidor durante minutos seguidos. La petición del watchdog esperaba en la cola detrás de ellas, se quedaba sin tiempo y contaba como fallo. Cinco comprobaciones así después, un servidor sano fue reiniciado. Teníamos un archivo de marca "ocupado" que el watchdog respetaba, pero el trabajo por lotes nunca lo activaba.
Cómo un reinicio falso llevó al siguiente
Cada reinicio fallaba las peticiones en curso con un error 500 ("cannot schedule new futures after shutdown"). Un segundo watchdog contaba errores 500 y reiniciaba el servidor cuando había demasiados, y cuantos más errores veía, antes actuaba. Así que cada reinicio falso hacía que el siguiente llegara antes. En total, cuatro watchdogs distintos podían reiniciar ese servidor, en timers de 5, 10, 15 y 30 minutos.
La regla que sacamos de ello
Una comprobación de estado no debe usar lo que protege. El recurso escaso de un servidor de modelos son sus slots para peticiones en curso. Una comprobación que ocupa uno de esos slots, o espera por uno, es parte de la carga que debe juzgar. Comprueba barato, y reinicia solo cuando puedas demostrar que el servidor está colgado.
La comprobación que lo sustituyó
vLLM publica sus propios contadores en /metrics. La nueva comprobación los lee primero:
- Peticiones en ejecución y el motor avanzando: sano y ocupado. No se envía petición de prueba.
- Peticiones en ejecución y sin progreso entre dos lecturas: colgado. Este es el caso que aún reinicia.
- Nada en ejecución: inactivo. Solo entonces la comprobación pide una respuesta corta, que un motor inactivo da al instante.
La primera versión contaba los tokens generados como progreso. Pasó ocho pruebas, y en su primera ejecución en vivo encontró el servidor ocupado y sano: 2 peticiones en ejecución, 107 tokens nuevos entre las dos lecturas.
Qué hemos cambiado desde entonces
- Pasos del motor, no tokens de salida. Mientras vLLM lee un prompt largo, no produce tokens de salida durante un tiempo, pero sigue dando pasos. Contar los pasos (el recuento de vllm:iteration_tokens_total) evita que un prompt largo parezca un servidor bloqueado.
- Dos esperas antes de considerar "bloqueado", para que una pausa breve no cuente como fallo.
- Un reinicio entre las dos lecturas se detecta mediante la hora de inicio del proceso en las mismas métricas; la comprobación entonces pide una respuesta en lugar de comparar contadores de dos procesos distintos.
- Un proxy se comprueba solo a través de /models. Detrás de un proxy de menor conexión, cada lectura de /metrics puede provenir de un backend distinto, como descubrimos cuando ejecutamos una copia del modelo en cada una de dos máquinas. Los backends se comprueban directamente.
La diferencia es fácil de ver. Mientras escribíamos esta nota, ambos servidores de modelos ejecutaban 12 solicitudes cada uno, su límite. Una solicitud de prueba habría esperado en la cola. La nueva comprobación informó de ambos como ocupados y sanos, a 126 y 101 tokens por segundo, sin enviar ninguna.
Factory Floor 1.0.2 incluye esta comprobación como models/llm-health.py, con pruebas para servidores inactivos, ocupados, bloqueados y reiniciados, un servidor sin métricas, un proxy y un backend muerto.