Esta semana hemos tenido un problema al desplegar nuestra aplicación NextJS y este post es para contar nuestro aprendizaje. Cuando la infraestructura estaba lista y desplegamos nuestro frontend, nos dimos cuenta que había un raro cuándo se servía nuestra aplicación al navegador, devolviendo algunos 404 aleatorios bajo la ruta /.next/static/*
. Enseguida nos dimos cuentas que al haber desplegado la aplicación detrás de un balanceador de carga la aplicación no respondía como esperábamos. Enseguida nos dimos cuenta que el problema estaba al haber puesto un balanceador de por medio y la forma en la que estábamos desplegando nuestra aplicación.
De forma predeterminada, un “Balanceador de Carga” dirige cada petición de manera independiente a la instancia registrada con menor carga, aunque cada tipo de balanceador tiene sus propias estrategias dependiendo del software y el proveedor con el que estemos trabajando. Entre estas estrategias de distribución de la carga nos podemos encontrar con las siguientes:
- Round-Robin: las peticiones son distribuidas entre los servidores de forma cíclica, independientemente de la carga del servidor. Esto consigue distribuir las peticiones de forma ecuánime pero sin tener en cuenta la carga.
- Weighted Round-Robin: La misma estrategia que la anterior pero las peticiones se entregan dependiendo del peso ponderado que le proporcionemos a cada servidor.
- LeastConnection: Cada petición es atendida por el servidor con menos conexiones activas en ese momento.
- Weighted LeastConnection: Igual que el enaterior pro la eleción se hace en base del peso ponderado que le proporcionemos a cada servidor.
- Session Sticky: se selecciona el servidor que atenderá la petición con base en algún dato identificativo del usuario como la dirección IP o una cookie. Esta estrategia también se conoce como afinidad de sesión y garantiza que todas las solicitudes de ese usuario durante la sesión se envían a la misma instancia.
Enseguida nos pudimos dar cuenta que la petición que se hacía para recuperar unos ficheros javascript contenía un hash, de tal manera que cada servidor al haber construido el build con un identificador distinto y el balanceador elegido no permitía session sticky, siendo totalmente stateless. Existían distintas maneras de solventar este problema, generar el build en una máquina distinta y replicar los ficheros o como decidimos en nuestro caso intentar generar ese build con un hash común.
Tras revisar la documentación de Next.js, descubrimos que a partir de la versión 6 del framework proporciona un mecanismo para poder definir ese identificador de build. Next.js usa una constante generada en el momento de la compilación para identificar qué versión de la aplicación se está sirviendo. Para mantener una ID de compilación estática entre compilaciones, el framework proporciona la función generateBuildId
.
En nuestro caso pensamos en usar el identificador del commit de git para identificar cada nueva release. Googleando nos encontramos enseguida con que alguien ya había generado una librería que solventaba este problema por lo que nos decidimos a utilizarla ( Viva Internet 🙂 ). Esta librería era next-build-id y concretamente solucionaba el problema configurando fácilmente el script de build `next build`
con el último hash de confirmación de git. De tal manera que nuestro fichero next.config.js
incluimos lo siguiente:
const nextBuildId = require('next-build-id') module.exports = { generateBuildId: async () => { const fromGit = await nextBuildId({ dir: __dirname }) return fromGit.id } }
Una vez implementado este cambio comenzamos a ver cómo nuestra aplicación frontend funcionaba correctamente. Recuerda, de los errores se aprende y hay que construir tus aplicaciones pensando en la infraestructura donde se vaya a desplegar 🙂