Los arranques en frío de funciones Lambda han intrigado a innumerables héroes y desarrolladores de AWS. Algunos juran que son un problema real en la actualidad, mientras que otros los desestiman y los descartan porque ya no son relevantes. Sin embargo, según mi experiencia, hay casos de uso en los que los arranques en frío pueden afectar la experiencia del usuario y es necesario abordarlos.
En esta publicación informativa, aprenderá si los arranques en frío siguen siendo un problema, cuándo minimizarlos y cómo.
TL;DR: Sí, pero el contexto importa.
Tabla de contenido
¿Qué es un arranque en frío?
Los arranques en frío en AWS Lambda ocurren cuando se invoca una función de AWS Lambda después de no usarse durante un período prolongado, o cuando AWS está escalando instancias de función en respuesta a una mayor carga. - AJ Stuyvenberg, héroe sin servidor
Un inicio en frío de Lambda se refiere al retraso inicial que se experimenta cuando se invoca una función de AWS Lambda por primera vez o después de estar inactiva, mientras se inicializa el entorno de ejecución de la función. Esto incluye la descarga del código de la función y el inicio del entorno de ejecución.
En pocas palabras, es una latencia única añadida al tiempo de ejecución general de su función. La duración de un inicio en frío varía de menos de 100 ms a más de 1 segundo, según la documentación oficial de AWS . Una vez que la función está "caliente", las invocaciones posteriores no sufrirán el inicio en frío a menos que, debido a las demandas de tráfico, el servicio Lambda aumente la cantidad de ejecuciones simultáneas, lo que da como resultado nuevas invocaciones de funciones Lambda y inicios en frío.
Además, al actualizar el código de la función, volverá a producirse un inicio en frío durante su invocación.
Ahora que entendemos la definición técnica de los arranques en frío, hablemos del impacto.
¿Qué tan malo es realmente?
Impacto del arranque en frío con Lambda
Según un análisis de las cargas de trabajo de producción de Lambda, los arranques en frío suelen ocurrir en menos del 1 % de las invocaciones. - AWS
Un 1 % no parece mucho, ¿no? Bueno, si tiene un uso crítico que enfrenta un flujo sincrónico y millones de invocaciones, ese 1 % ahora se traduce en diez mil clientes insatisfechos, lo que no es ideal.
Sin embargo, todo es muy subjetivo y el contexto importa, ya que hay casos de uso en los que esto funciona perfectamente.
Comencemos con los casos simples, los casos de uso en los que un arranque en frío probablemente esté bien.
Casos de uso de impacto menor
Saquemos del camino el caso de uso más simple: casos en los que los arranques en frío son tan rápidos que no suponen un problema para usted. Por lo general, ese es el caso de las funciones que utilizan entornos de ejecución como C++, Go, Rust y LLRT . Sin embargo, debe seguir las mejores prácticas y optimizaciones en cada entorno de ejecución para mantener un arranque en frío de bajo impacto.
Invocación asincrónica
Otro uso simple es cuando su función se invoca de forma asincrónica, ya sea una transmisión SQS, SNS, EventBridge, DynamoDB, etc. En tales casos de uso, es probable que un segundo más de tiempo de ejecución esté bien y no sea un factor decisivo.
Flujo no crítico
¿Importa una latencia adicional de 0,5 a 1 segundo en flujos no críticos para el 1 % de los clientes? Lo más probable es que no, especialmente si se trata de un flujo que no está orientado al cliente, como las llamadas de servicio a servicio.
Sin embargo, algunas acciones de cara al cliente también podrían estar bien con un arranque en frío ocasional.
Depende del tiempo total: ¿el cliente notará la diferencia entre 2 segundos y 1 segundo? Tal vez, pero puede que no sea un problema para la mayoría.
Por otro lado, si el cliente está esperando 5 segundos y ahora podría tener que esperar 6 o 7, eso podría ser bastante notorio ya que el cliente ya está esperando mucho tiempo y el tiempo extra puede aumentar la molestia.
Patrón de tráfico
El comportamiento del tráfico también influye. Si tienes tráfico constante sin cambios drásticos en la escala, es posible que no experimentes arranques en frío, ya que siempre hay funciones en caliente.
Casos de uso de gran impacto: aquí es donde duele
Repasemos los casos de uso de mi experiencia donde los arranques en frío tuvieron demasiado impacto en la experiencia del usuario y tuvimos que tomar medidas para minimizarlos.
El desempeño crítico es imprescindible
Los arranques en frío son los más perjudiciales cuando se trata de flujos orientados al cliente en los que el rendimiento es fundamental, incluso para ese 1 % de clientes. Por ejemplo, si tiene microservicios dedicados a la autenticación o autorización que deben funcionar con una alta concurrencia y finalizar la ejecución en menos de una docena de milisegundos, el 1 % de 0,5 a 1 segundo adicional puede ser un factor decisivo. Yo diría que Lambda puede ser demasiado caro o inadecuado para esos casos de uso, así que investigue.
Patrón de tráfico errático
Los números mágicos del 1 % que proporcionó AWS podrían ser diferentes en su caso, según el patrón de tráfico. Si el tráfico es errático, está por todas partes y es impredecible, su función podría tener más arranques en frío que el promedio.
Arranques en frío encadenados
Repasemos la arquitectura a continuación. Tiene tres microservicios, cada uno de los cuales contiene una función Lambda.
El usuario envía una solicitud de API a la primera función (1). La primera función llama al microservicio (2) y luego llama al tercero (3).
El primer microservicio espera que ambos microservicios respondan, por lo que su tiempo de ejecución total depende de su rendimiento. Supongamos que el primer microservicio tuvo un inicio en frío de 1 segundo, pero cuando llamó al segundo microservicio, también tuvo un inicio en frío de un segundo. Esto sucedió nuevamente cuando llamó al tercer servicio.
Ahora tenemos en total un inicio en frío de 3 segundos, lo cual es bastante malo desde la perspectiva de la experiencia del usuario.
Depende de los patrones de tráfico, pero puede suceder. Puede obtener tiempos de arranque en frío de cero, uno, dos o tres segundos; todo vale. El problema se vuelve aún más complicado cuando diferentes equipos gestionan los microservicios y cada uno es responsable de definir sus funciones y optimizaciones.
La conclusión es que cada función lambda de la que depende en un flujo crítico aumenta la posibilidad de una penalización de inicio en frío agregada, y debe considerarlo de antemano.
Optimizaciones de arranque en frío
Los arranques en frío pueden disuadir a las personas de usar sistemas sin servidor, y puedo entender por qué lo hacen, pero no todo es blanco o negro. Los arranques en frío se pueden mejorar y optimizar hasta el punto en que pueden no ser relevantes para sus casos de uso.
Existen muchos parámetros que pueden reducir o aumentar la duración del arranque en frío y es fundamental comprenderlos. Enumeremos los factores más críticos.
Tamaño de la memoria de funciones
Más memoria se traduce en más núcleos de CPU y mejor rendimiento, lo que a su vez puede acortar la duración del arranque en frío. Sin embargo, el costo total aumentará. Utilice herramientas como AWS Lambda Powertuning para ajustar la relación rendimiento-costo.
Arquitectura de CPU
La elección de una arquitectura de CPU ARM64 puede aumentar el rendimiento general en algunos casos. Utilice Lambda Powertuning para comprobar si hay alguna diferencia en su funcionamiento.
Tiempo de ejecución de la función
Los distintos entornos de ejecución tienen un rendimiento de arranque en frío diferente. Java es notoriamente lento, pero podría mejorarse con SnapStart .
Puede seleccionar Rust o LLRT (tiempo de ejecución de baja latencia lambda) para los arranques en frío más cortos.
Para contenido relacionado con Rust, te sugiero que sigas el contenido de Pyle de Benjamin .
AJ resumió las diferencias entre los tiempos de ejecución de forma bastante clara (busque el ícono de nieve como medida de arranque en frío):
Método de creación de funciones
AJ analiza esta discrepancia en su sesión re: invent (ver el vídeo a continuación).
Puede crear un archivo ZIP que contenga todas las dependencias y el código fuente de su función Lambda y cargarlo en AWS, o puede crear una imagen de contenedor que contenga todo lo que necesita.
Las funciones basadas en contenedores tienen arranques en frío más cortos. Sin embargo, no cambiaría todas mis creaciones ZIP a contenedores por ahora. Las funciones basadas en contenedores sufren un mayor tiempo de compilación e implementación, y son más difíciles de administrar (los archivos Docker no son divertidos). Lo usaría solo si quiero el mejor rendimiento o si mi función tiene dependencias de más de 250 MB, que es la limitación del método ZIP.
Puedes leer más sobre ello en mi publicación de blog aquí .
Las importaciones importan
Los desarrolladores tienden a agregar importaciones que traen bibliotecas enteras solo para una pequeña función.
Cada pequeño detalle importa y se suma al tiempo total de importación como parte del inicio en frío. Necesitamos optimizar nuestro código y las importaciones. Si usa Python, puede analizar su código con una herramienta como Tuna y optimizar sus bibliotecas (quizás reemplazar las más lentas) y sus importaciones.
Sin embargo, no importa cuán optimizado esté tu código, en algún momento sufrirás arranques en frío.
Analicemos otra solución que puede ir más allá de las optimizaciones.
Como nota al margen, recomiendo encarecidamente la sesión re:invent 2023 de AJ si quieres profundizar en Lambda y comprender los arranques en frío.
Concurrencia aprovisionada al rescate
Supongamos que ha realizado todas las optimizaciones posibles que enumeré anteriormente, pero aún experimenta inicios en frío significativos en sus flujos críticos orientados al cliente.
La siguiente solución será configurar la concurrencia aprovisionada para sus funciones Lambda. La concurrencia aprovisionada es la
Número de entornos de ejecución preinicializados asignados a su función. Estos entornos de ejecución están listos para responder de inmediato a las solicitudes de funciones entrantes. La configuración de la concurrencia aprovisionada genera cargos adicionales en su cuenta de AWS. - Documentación de AWS
Pague por funciones que siempre estén calientes: no más arranques en frío. Sí, funciona y es caro.
Sin embargo, no es magia. Si define 10 funciones para la concurrencia aprovisionada y el servicio Lambda necesita escalar a la undécima función debido a los requisitos de tráfico y la escala, obtendrá un arranque en frío en esa undécima función y más allá. Sin embargo, si ajusta la cantidad de concurrencia aprovisionada para que se ajuste a sus necesidades de escalado, no debería haber arranques en frío (a menos que cargue un nuevo código fuente de Lambda, por supuesto).
El único problema con la concurrencia aprovisionada es que puede volverse bastante costosa rápidamente. Imagine que la habilita en varias cuentas y regiones; el costo se multiplica rápidamente. Una vez me equivoqué en la calculadora de precios de AWS y el costo real fue mucho más alto de lo que esperaba, por lo que tuvimos que reducirlo rápidamente.
En esta publicación , describimos cómo puede optimizar aún más la concurrencia aprovisionada y habilitar una concurrencia aprovisionada dinámica: un enfoque que maximiza el rendimiento y reduce los costos.
Optimizaciones de costos
En primer lugar, defina la concurrencia aprovisionada solo en los casos de uso que requieren un rendimiento óptimo y que estén orientados al usuario. Incluso en ese caso, asegúrese de definirla solo en la cuenta de producción y no en las cuentas de desarrollo o prueba.
Otra práctica recomendada para ahorrar costos es configurar diferentes configuraciones de concurrencia para varias cuentas y regiones. AWS recomienda calcular las configuraciones necesarias; puede leer su fórmula aquí .
Por último, una vez configurado, se requieren monitoreo y alertas para garantizar que no se gasta de más ni de menos. Puede seguir esta guía de AWS para comprender cómo crear sus alertas y panel de control.
Resumen
En esta publicación, definimos los arranques en frío y analizamos sus efectos: cuándo pueden ser perjudiciales y cuándo, en su mayoría, no.
Hemos analizado cómo optimizar y minimizar el impacto del inicio en frío, y cuando todas las optimizaciones han fallado, la solución sugerida pero costosa es habilitar la concurrencia aprovisionada.
En la próxima publicación de la serie , proporcionaremos una optimización de costos para la concurrencia aprovisionada junto con ejemplos de código CDK.