- Al realizar intentos repetidos de conexión para verificar el estado de un servidor web en un script de Bash, puede surgir el problema de que el servidor caiga inesperadamente en un bucle infinito
timeout, una herramienta para resolver esto, establece un límite de tiempo para la ejecución de un comando y, si se supera, envía una señal para intentar terminar el proceso
- No puede aplicarse directamente a shell built-ins como
until, por lo que puede resolverse mediante wrapping de un proceso bash o separándolo en un script
Espera de un servidor web y problema de bucle infinito en scripts de Bash
- En la práctica se usan scripts de Bash para configurar servidores web y comprobar su estado
- La estructura pospone la siguiente tarea mientras el servidor termina de levantarse, y normalmente funciona sin problemas
- Sin embargo, si el servidor se bloquea durante el arranque, termina entrando en un bucle infinito y fue necesario resolverlo
Ejemplo de uso de until y sus límites
Introducción de la utilidad timeout
- El comando
timeout termina el comando enviando una señal (como SIGTERM) si no finaliza dentro del tiempo especificado
- Ejemplo: en
timeout 1s sleep 5, después de 1 segundo se intenta terminar el proceso sleep
- Al finalizar devuelve un código de salida anormal (por ejemplo, 124)
Intento de combinar timeout y until, y el problema
Solución: wrapping de un proceso Bash o uso de un script externo
1 comentarios
Comentarios en Hacker News
Uno de mis trucos poco conocidos favoritos es usar
stracefault injection para probar fallos en distintas llamadas al sistemaEn este enlace relacionado lo explican con más detalle.
Me parece una función realmente increíble, y ojalá la hubiera conocido antes.
Como no tenía forma de probar las ramas de error, solía reemplazar temporalmente solo partes de una función con código provisional, pero este truco abre la posibilidad de un enfoque más limpio.
Esto se ve realmente útil.
Me pregunto si existe algo parecido en Windows.
Para los health checks de servicios, se propone que la mejor manera es configurar tanto un tiempo máximo de timeout como un número máximo de reintentos.
Normalmente se reintenta hasta X veces, y se considera fallo si no responde dentro de un máximo de Y tiempo.
Se enfatiza que hay que decidir el fallo lo antes posible, en vez de quedarse esperando demasiado tiempo.
En servicios estándar, el health check solo debería empezar una vez que las dependencias del contenedor estén garantizadas y todo esté listo para funcionar.
En Kubernetes, revisar Init Container; en AWS ECS,
dependsOn; y en Docker Compose,depends_on.Se comparte un ejemplo en shell POSIX.
Pero también se menciona que
curlya trae esta funcionalidad integrada, así que podría usarse directamente así, sin script aparte:En Mac, como el comando
timeoutno viene por defecto, alguien comparte que hizo varios intentos para implementar un timeout usando solo builtins de bash.Explica que el comando
sleepsí es estándar en POSIX, así que puede usarse.Comparte un ejemplo de implementación de timeout como este:
Usa una función llamada
times_uppara manejar el timeout.También muestra una prueba repitiendo un
for20 veces con un timeout de 10 segundos.Hace 12 años implementé algo parecido siguiendo un consejo de Stack Overflow.
En este enlace de referencia se puede ver el detalle.
Solo usaba shell builtins y
sleep, y se recalca que ese código tenía que ser obligatoriamente compatible con POSIX.También se menciona que la sintaxis
{1..20}de bash en el ejemplo no es POSIX, así que hay que tener cuidado.Mi mejora fue hacer que devolviera
truesi no ocurría el timeout yfalsesi sí ocurría, para simplificar el manejo de errores dentro del script.Se comparte un método muy simple como este: ejecutar el comando y
sleepen paralelo, y cuando pase el tiempo indicado, terminar el comando con una señal.Se comparte un ejemplo de script de hace 13 años que implementaba un timeout usando
read -t.Enlace
curlya tiene la bandera--retry-connrefused, así que esta funcionalidad puede aprovecharse directamente sin un loop en shell.Si necesitas pasar variables al usar
bash -c, se recomienda agregar argumentos así:También se explica por qué se usa
"--"y cuál es el rol deargv[0].Se menciona que también podría usarse
printf %q, pero se prefiere el enfoque compatible con Bourne.Se explica que
"--"tiene un significado muy claro como señal de fin de opciones en bash y en la mayoría de las CLI de Unix/Linux.Referencia relacionada
Busybox decide qué programa ejecutar basándose en el valor de
argv[0], así que puede configurarse como"ls","mv","cp"u otro comando deseado.Cuando se necesita lógica de reintentos, una forma que uso seguido es esta:
No es muy elegante, pero normalmente funciona bien; en un nivel más avanzado, se podría aplicar backoff exponencial.
También tiene ventajas en términos de escalabilidad.
shellcheckrecomienda manejar ese tipo de caso usando la variable_.Enlace de referencia
También se enfatiza que la función
eventually_succeedspodría necesitar su propio timeout o código defensivo adicional, según el caso.Es un recordatorio de que en POSIX/procesos/IO siempre conviene escribir código defensivo.
En el pasado, cuando mis hijos eran pequeños, usaba el siguiente comando como una especie de control parental para que solo pudieran ver un programa durante 30 minutos:
Se comenta que fue una idea muy útil en la práctica.
Como necesito enviar señales a subprocesos, no me gusta mucho usar comandos en línea ni archivos de script temporales.
El método que prefiero es poner la lógica compleja deseada dentro de una función, exportarla y luego envolverla con
timeout bash -c.Esto se relaciona con el método seguro para pasar argumentos que mencionó aidenn0.
"$@"sí o sí.De lo contrario, los argumentos que contienen espacios no se pasarán correctamente.
También se comparte un ejemplo de
long_fnpara comprobar ese punto.Se recuerda una publicación de blog anterior donde se mencionaba
timeout.Si alguien tiene más curiosidad sobre lenguajes de programación generales o sobre el funcionamiento interno, más allá del shell, se recomienda este blog relacionado.
Se comparte experiencia agregando timeout a comandos en una configuración de Kubernetes.
Se comenta que scripts POSIX shell como
await-cmd.sh,await-http.shyawait-tcp.shya están bastante maduros y pueden ser bastante útiles en ciertos escenarios.Enlace al proyecto relacionado