La ejecución de código en el borde de la red (Edge Computing) presenta un desafío fundamental en la computación distribuida: cómo proporcionar capacidad de cómputo elástica y aislada con latencia de milisegundos para millones de invocaciones diarias. Este problema se agrava por la necesidad de ejecutar código de usuario arbitrario de forma segura y eficiente. La solución de Netlify aborda esto mediante la adopción de MicroVMs ligeras, específicamente Firecracker, que permiten un aislamiento robusto y tiempos de arranque extremadamente rápidos, superando las limitaciones de rendimiento y seguridad de los entornos de ejecución basados en 'isolates' o contenedores tradicionales.
Históricamente, la ejecución de funciones sin servidor ha oscilado entre la eficiencia de los procesos ligeros (como los 'isolates' de V8) y el aislamiento robusto de las máquinas virtuales completas. La aparición de MicroVMs como Firecracker, que arrancan en milisegundos con un kernel Linux mínimo, representa un punto de inflexión, ofreciendo un equilibrio óptimo entre ambos mundos. Este enfoque permite a Netlify mantener el código del cliente lo más cerca posible del usuario final, minimizando la latencia de red y el overhead de cold start, un problema crónico en las arquitecturas serverless.
Arquitectura del Sistema
La nueva arquitectura de Netlify Edge Functions se basa en una red de nodos de borde (edge nodes) y nodos de cómputo (compute nodes). Cuando una solicitud llega a un edge node, este termina la conexión TLS y evalúa las rutas de Edge Functions. Si hay una coincidencia, el edge node genera una especificación de máquina que incluye imágenes de runtime, plataforma y la función del cliente, junto con límites de CPU, memoria y conexión. Esta especificación se convierte en un Service ID único para aislamiento.
El edge node utiliza rendezvous hashing para enrutar la solicitud a un compute node específico dentro de la red. Este algoritmo asegura que el mismo Service ID siempre se dirija al mismo compute node, lo que permite mantener las MicroVMs 'calientes' y el código en caché en disco, optimizando el rendimiento para invocaciones subsiguientes. Para mitigar los 'hot spots' que podrían formarse en un solo compute node, el sistema relaja la 'stickiness' del hashing por encima de un cierto umbral, distribuyendo el servicio entre un subconjunto de nodos.
En el compute node, se verifica si ya existe un 'servicio' para el Service ID. Si no, se crea uno y se asegura que todas las imágenes necesarias (runtime, plataforma, función) estén en disco, descargándolas del edge node si es necesario. Cada función se ejecuta dentro de su propia MicroVM Firecracker, que arranca un entorno Linux mínimo en milisegundos. Los archivos de la función se montan como una imagen EROFS descomprimida y se mapean en memoria, permitiendo la lectura bajo demanda. Para optimizar los 'cold starts', se toma un snapshot de la MicroVM una vez que el servidor JavaScript está listo. Cuando la función no está activa, las MicroVMs escalan a cero. En la siguiente invocación, se restaura una nueva MicroVM desde ese snapshot mapeado en memoria, permitiendo la ejecución inmediata sin esperar la carga completa del snapshot. Un plano de control gestiona la salud y el despliegue de los compute nodes, permitiendo actualizaciones rápidas y reversiones.
Flujo de Solicitud de Edge Function (Warm Invocation)
- 1 Cliente Envía solicitud HTTP(S)
- 2 Edge Node Termina TLS, verifica rutas de Edge Function. Genera especificación de máquin...
- 3 Edge Node Enruta a Compute Node usando Rendezvous Hashing.
- 4 Compute Node Recibe solicitud, verifica Service ID. Si existe servicio, lo reenvía.
- 5 Compute Node Reenvía solicitud a MicroVM Firecracker existente.
- 6 MicroVM Ejecuta código de Edge Function.
- 7 Compute Node Recibe respuesta de MicroVM.
- 8 Edge Node Recibe respuesta del Compute Node y la envía al cliente.
Flujo de Inicialización de Edge Function (Cold Invocation)
- 1 Edge Node Enruta a Compute Node (Service ID no visto antes).
- 2 Compute Node Crea nuevo servicio para Service ID. Verifica imágenes en disco.
- 3 Compute Node Si faltan imágenes (runtime, plataforma, función), las descarga del Edge Node.
- 4 Compute Node Arranca nueva MicroVM Firecracker desde cero.
- 5 MicroVM Monta imágenes EROFS, inicia servidor JavaScript.
- 6 MicroVM Toma snapshot de la MicroVM para futuras restauraciones.
- 7 Compute Node Reenvía solicitud a la MicroVM recién iniciada.
- 8 MicroVM Ejecuta código de Edge Function.
| Capa | Tecnología | Justificación |
|---|---|---|
| compute | Firecracker MicroVMs | Proporciona aislamiento de seguridad y un entorno de ejecución ligero y de arranque rápido para cada Edge Function, reemplazando modelos basados en 'isolates' o contenedores. vs V8 Isolates, Docker Containers, Kubernetes Pods Arranca un entorno Linux mínimo, monta archivos de función como EROFS memory-mapped, utiliza snapshots para optimizar cold starts. |
| networking | Rendezvous Hashing | Algoritmo de enrutamiento para asignar solicitudes de Edge Function a compute nodes específicos, manteniendo la 'stickiness' para optimizar el caché y reducir cold starts. vs Round-robin DNS, Load Balancers basados en IP Hash, Consistent Hashing La 'stickiness' se relaja por encima de un umbral de tráfico para distribuir la carga y evitar hot spots. |
| storage | EROFS (Enhanced Read-Only File System) | Formato de sistema de archivos utilizado para montar las imágenes de las Edge Functions dentro de las MicroVMs, permitiendo lectura bajo demanda y mapeo en memoria. vs Ext4, XFS, OverlayFS Las imágenes se montan sin comprimir y se mapean en memoria, lo que permite a la VM leer solo las partes del bundle que realmente utiliza. |
| orchestration | Control Plane (custom) | Gestiona el estado y la salud de los compute nodes, orquesta despliegues de nuevas flotas y permite rollbacks rápidos. vs Kubernetes, Nomad, Mesos Permite despliegues 'blue/green' o 'canary' para nuevas flotas de compute nodes, asegurando que el tráfico se desvíe solo cuando la nueva flota está sana. |
| observability | Métricas de rendimiento (custom) | Recolección de datos detallados sobre el tiempo de arranque de MicroVMs, tiempo hasta el primer puerto abierto y tiempo de inicio del código de usuario para depuración y optimización. vs Prometheus, Datadog, New Relic Incluye métricas específicas para el ciclo de vida de la MicroVM y la ejecución de la función. |
Trade-offs
Ganancias
- ▲▲ Latencia de invocación (p50)
- ▲ Seguridad (aislamiento)
- ▲ Fiabilidad
- ▲ Control sobre la infraestructura de cómputo
- ▲ Capacidad para soportar npm packages y otras funcionalidades avanzadas
Costes
- ▲ Complejidad de la infraestructura (gestión de MicroVMs, control plane)
- △ Overhead de gestión de recursos (aunque optimizado por Firecracker)
Fundamentos Teóricos
El concepto de aislamiento y ejecución eficiente en entornos de nube tiene raíces profundas en la investigación de sistemas operativos y virtualización. La idea de MicroVMs como Firecracker se alinea con los principios de los 'unikernels' y sistemas operativos especializados, donde se minimiza la superficie de ataque y el overhead del kernel para cargas de trabajo específicas. Trabajos como 'The Case for Unikernels' (Madhavapeddy et al., 2013) exploraron cómo construir imágenes de sistema operativo mínimas y de propósito único para aplicaciones en la nube, optimizando el rendimiento y la seguridad.
El uso de rendezvous hashing para la asignación de servicios a nodos de cómputo es una aplicación práctica de algoritmos de hashing consistentes, que buscan distribuir la carga de manera uniforme mientras minimizan la reasignación de claves cuando los nodos se unen o abandonan el clúster. Aunque el hashing consistente clásico (Karger et al., 1997) se enfoca en la distribución uniforme, la variante de rendezvous hashing (también conocida como HRW hashing) es particularmente útil aquí por su propiedad de 'stickiness' o afinidad, que es crucial para la estrategia de caché de Netlify y la minimización de 'cold starts'. La gestión de 'hot spots' mediante la relajación de esta afinidad es un ejemplo de cómo los principios teóricos deben adaptarse a las realidades de la carga de trabajo en sistemas distribuidos a gran escala.