Cloudflare cuenta con una plataforma de computación en la nube llamada Workers. A diferencia de prácticamente cualquier otra plataforma de computación en la nube que conozco, esta no usa contenedores o máquinas virtuales. Creemos que es el futuro de la computación Serverless y en la nube en general, y voy a intentar convencerles de por qué.
Isolates
Hace dos años tuvimos un problema. Estábamos limitados con respecto a la cantidad de opciones y características podíamos desarrollar de manera interna. Necesitábamos una manera de que los clientes fueran capaces de desarrollar por su cuenta. Nos propusimos encontrar una manera que permitiera a la gente escribir código en nuestros servidores implementados en todo el mundo (entonces contábamos con poco más de cien centros de datos, 155 a la fecha de este escrito). Nuestro sistema debía ejecutar código no confiable de manera segura y con poca sobrecarga. Hablamos de 10 millones de sitios, millones de procesos y millones de peticiones por segundo, y, además todo debía ejecutarse de forma rápida.
El Lua que habíamos usado anteriormente no se ejecutaba en un entorno de pruebas; los clientes no podían escribir su propio código sin nuestra supervisión. Tecnologías tradicionales de virtualización y contenedores como Kubernetes habrían sido extremadamente caras para todas las partes involucradas. La ejecución de miles de pods de Kubernetes en una sola ubicación requeriría muchos recursos, peor aún en 155 ubicaciones. Escalarlos sería más sencillo que sin un sistema de gestión, pero tampoco sería algo trivial.
Lo que acabamos utilizando fue una tecnología desarrollada por el equipo de Google Chrome para alimentar el motor de Javascript en ese navegador, V8: Isolates.
Los Isolates son contextos ligeros que agrupan las variables con un código que les permite mutarlos. Y lo que es más importante, un solo proceso puede ejecutar cientos o miles de Isolates, conmutando entre ellos sin dificultades. Posibilitan la ejecución de código no confiable de diferentes clientes con un único proceso de sistema operativo. Están diseñados para iniciarse de manera muy rápida (varios tuvieron que iniciarse en su navegador web para cargar esta página web), y para impedir que un Isolate acceda a la memoria de otro.
Pagamos una vez la sobrecarga del tiempo de ejecución de Javascript y entonces logramos ejecutar scripts básicamente ilimitados con casi ninguna sobrecarga individual. Cualquier Isolate determinado puede iniciarse aproximadamente cien veces más rápido de lo que puedo conseguir que un proceso de Node se inicie en mi máquina. Lo que es más importante aún, consumen una orden de magnitud menos de memoria que ese proceso.
Todos cuentan con la hermosa ergonomía de función como servicio de poder simplemente escribir código y no preocuparse por cómo se ejecuta o escala. Al mismo tiempo, no utilizan una máquina virtual o un contenedor, lo que significa que la ejecución está más cerca del metal que cualquier otra forma de computación en la nube que conozco. Con este modelo, creo que es posible acercarse a la rentabilidad de ejecutar código en metal desnudo, pero en un entorno totalmente Serverless.
Esto no pretende ser un anuncio para Workers, pero quiero mostrarles un gráfico que refleja la enorme diferencia que hay, para exponer por qué creo que esta no es una mejora iterativa, sino un verdadero cambio de paradigma:
Estos datos reflejan las solicitudes reales (incluida la latencia de red) realizadas desde un centro de datos cercano donde se implementaron todas las funciones, y se lleva a cabo una carga de trabajo intensiva de CPU. Fuente
Estos datos reflejan las solicitudes reales (incluida la latencia de red) realizadas desde un centro de datos cercano donde se implementaron todas las funciones, y se lleva a cabo una carga de trabajo intensiva de CPU. Fuente
Arranques en frío
No todo el mundo entiende totalmente cómo funciona una plataforma Serverless tradicional como Lambda. Crea un proceso en contenedores para su código. No ejecuta su código en ningún entorno más ligero que la ejecución de Node en sus propias máquinas. Lo que hace es autoescalar los procesos (de una manera algo torpe). Ese autoescalado crea arranques en frío.
Un arranque en frío es lo que ocurre cuando se tiene que iniciar una nueva copia de su código en una máquina. En el mundo Lambda, esto equivale a crear un nuevo proceso en contenedores, que puede tardar entre 500 milisegundos y 10 segundos. Cualquier solicitud que obtenga quedará colgada hasta diez segundos, lo que resulta ser una experiencia de usuario terrible. Ya que un Lambda solo puede procesar una sola solicitud a la vez, se debe arrancar en frío un nuevo Lambda cada vez que reciba una solicitud simultánea adicional. Esto quiere decir que esa solicitud con lag puede suceder una y otra vez. Si su Lambda no recibe una solicitud lo suficientemente pronto, se cerrará y se iniciará de nuevo. Cada vez que implemente nuevo código, todo sucederá de nuevo, ya que se tiene que volver a implementar cada uno de los Lambda. Esto se ha citado correctamente como prueba de que Serverless no está tan bien como se supone.
Debido a que Workers no tiene que iniciar un proceso, Isolates se inicia en 5 milisegundos, una duración apenas perceptible. De manera similar, Isolates escala y se implementa igual de rápido, y elimina completamente este problema con las actuales tecnologías Serverless.
Conmutación de contexto
Una característica clave de un sistema operativo es que le ofrece la posibilidad de ejecutar muchos procesos a la vez. Conmuta de manera transparente entre los distintos procesos que quieren ejecutar código en un momento dado. Para lograr esto, realiza lo que se conoce como «conmutación de contexto»: saca toda la memoria necesaria para un proceso e introduce la memoria que se requiere para el siguiente.
Esa conmutación de contexto puede tardar hasta 100 microsegundos. Al multiplicar por todos los procesos de Node, Python o Go que se ejecutan en su servidor Lambda promedio, se crea una intensa sobrecarga que hace que no toda la energía de las CPU se pueda dedicar realmente a ejecutar el código del cliente, ya que se gasta al conmutar entre estos.
Un sistema basado en Isolate ejecuta todo el código en un único proceso, y utiliza sus propios mecanismos para garantizar el acceso seguro a la memoria. Esto quiere decir que no hay costosas conmutaciones de contexto, sino que la máquina gasta prácticamente todo su tiempo en la ejecución de su código.
Memoria
Los tiempos de ejecución de Node o Python estaban pensados para uso de particulares en sus propios servidores. Nunca estuvieron diseñados para ejecutarse en un entorno de tenencia múltiple con código de miles de personas y requisitos estrictos de memoria. Una Lambda de Node básica que ejecute código no real consume 35 MB de memoria. Cuando se puede compartir el tiempo de ejecución entre todos los Isolates como hacemos nosotros, la cifra baja a 3 MB aproximadamente.
La memoria supone a menudo el coste más alto de ejecución del código de un cliente (incluso superior al de la CPU). Bajarlo un orden de magnitud cambia por completo la rentabilidad.
Básicamente, V8 fue diseñado para ser de tenencia múltiple. Está pensado para ejecutar el código de muchas pestañas en su navegador en entornos aislados dentro de un solo proceso. Node y tiempos de ejecución similares no lo fueron, y se muestra en los sistemas de tenencia múltiple, que se diseñaron sobre ello.
Seguridad
Ejecutar el código de varios clientes dentro del mismo proceso requiere obviamente que se preste especial atención a la seguridad. No habría sido productivo o eficiente para Cloudflare que hubiéramos desarrollado esa capa de aislamiento nosotros mismos. Se necesita una cantidad astronómica de pruebas, fuzzing, pruebas de penetración y recompensas para desarrollar un sistema verdaderamente seguro de esa complejidad.
La única razón por la que esto fue posible es por la naturaleza de código abierto de V8, y debido a que es quizá la pieza de software más probada para su seguridad del mundo. También hemos desarrollado algunas capas de seguridad, que incluyen varias protecciones contra ataques sincronizados, pero el V8 es la verdadera maravilla que hace posible este modelo de computación.
Facturación
Esto no pretende ser un referéndum sobre la facturación de AWS, pero merece la pena una breve mención, ya que la rentabilidad es interesante. Los Lambdas se facturan en base al tiempo en que se ejecutan. Esa facturación se redondea hacia arriba hasta los 100 milisegundos más cercanos, lo que implica que la gente esté pagando de más un promedio de 50 milisegundos en cada ejecución. Peor aún, le facturan por todo el tiempo que se ejecuta el Lambda, incluso si se está esperando a que se complete una solicitud externa. Debido a que las solicitudes externas pueden tardar cientos o miles de ms, puede terminar pagando cantidades ridículas a escala.
Los Isolates tienen un rastro de memoria tan pequeño que nos podemos permitir facturarle solo cuando su código esté ejecutándose.
En nuestro caso, debido a la sobrecarga inferior, Workers acaba siendo tres veces más barato por ciclo de CPU. Un Worker que ofrezca 50 milisegundos de CPU vale 0,50 $ por millón de solicitudes. El equivalente en Lambda es de 1,84 $ por millón. Creo que disminuir los costes por tres es, de por sí, una motivación lo suficientemente fuerte que alentará a las empresas a cambiarse a proveedores basados en Isolate.
La red es el ordenador
Amazon tiene un producto llamado Lambda@Edge, que se implementa en sus centros de datos CDN. Por desgracia, es tres veces más caro que el Lambda tradicional, y necesita 30 minutos para la implementación inicial. Además, no permite solicitudes arbitrarias, lo que limita su utilidad su utilidad a funciones de CDN.
En cambio, como ya he mencionado, con Isolates podemos implementar cada archivo origen en los 155 centros de datos a una mejor rentabilidad de lo que Amazon puede hacer en uno. De hecho, podría ser más barato ejecutar 155 Isolates que un único contenedor, o tal vez Amazon esté cobrando lo que el mercado es capaz de soportar y eso sea mucho mayor que sus costes. No conozco la rentabilidad de Amazon, pero sí sé que estamos muy cómodos con la nuestra.
Hace tiempo quedó claro que para tener un sistema verdaderamente fiable, debe implementarse en más de un lugar del mundo. Lambda se ejecuta en una sola zona de disponibilidad, en una sola región y en un solo centro de datos.
Desventajas
Ninguna tecnología es mágica, pero toda transición tiene sus desventajas. Un sistema basado en Isolate no puede ejecutar código compilado de forma arbitraria. El aislamiento a nivel de proceso permite que su Lambda cree tanto código binario como sea necesario. En un universo de Isolate, uno tiene que escribir su código en Javascript (nosotros usamos mucho TypeScript) o en un lenguaje que aborde WebAssembly, como Go o Rust.
Si no puede volver a compilar sus procesos, no puede ejecutarlos en un Isolate. Esto podría hacer que el Serverless basado en Isolate solo valga, en un futuro inmediato, para aplicaciones más nuevas y más modernas. También podría hacer que las aplicaciones de legado solo consigan que sus componentes sensibles a latencia se muevan a un Isolate inicialmente. La comunidad también podría encontrar nuevas y mejores formas de transpilar las aplicaciones existentes a WebAssembly, lo que acabaría con este debate.
Su ayuda
Me encantaría que probara Workers y le contara a la comunidad y a nosotros sobre su experiencia. Todavía nos queda mucho por desarrollar, así que sus comentarios nos resultarían muy útiles.
También necesitamos ingenieros y gestores de producto que piensen que esto es interesante y que quieran llevarlo a nuevos caminos. Si está en San Francisco, Austin, o Londres, comuníquese con nosotros.
¿Le interesa implementar un Cloudflare Worker sin crear un dominio en Cloudflare? Estamos facilitando el comienzo del desarrollo de aplicaciones serverless con subdominios personalizados en workers.dev. Si ya es cliente de Cloudflare, puede añadir Workers a su sitio web actual aquí.