Blog de Amazon Web Services (AWS)

Entendiendo el funcionamiento de AWS Lambda con Amazon CloudWatch Logs Insights

Por Alice Xiao, analista de datos en la compañía de servicios financieros State Street Corporation

Introducción

En esta publicación, nuestra invitada, Alice Xiao, analista de datos en la compañía de servicios financieros State Street Corporation, analiza de qué manera State Street diseñó un marco con Cloudwatch Logs Insights para soportar su proceso de innovación que consiste en convertir rápidamente ideas empresariales en aplicaciones centradas en el cliente.

Los clientes desean crear un prototipo rápidamente y asegurar que las deficiencias críticas se resuelvan pronto, por lo que comprender el comportamiento de una aplicación es fundamental. Amazon CloudWatch Logs Insights permite crear consultas ad hoc para entender el comportamiento de AWS Lambda en función de los registros de Lambda almacenados en Amazon CloudWatch Logs. Es posible crear visualizaciones rápidas a partir de esos datos.

Información general

State Street utiliza aplicaciones serverless para respaldar su proceso de innovación, el cual se enfoca principalmente en la creación rápida de prototipos. En el caso de aplicaciones serverless (y más específicamente, funciones Lambda), los equipos de desarrollo de aplicaciones deben asegurarse de configurar apropiadamente los parámetros de tiempo de ejecución de Lambda.

Para llevar a cabo esta tarea, State Street usa CloudWatch Logs Insights a fin de realizar un análisis heurísitico de los registros de Lambda y comprender mejor el dimensionamiento y la facturación de cada una de sus funciones de Lambda. Asimismo, correlacionan el tiempo de ejecución y el uso de memoria para realizar un análisis posterior.

“El objetivo era tener una solución que sea concisa, use la funcionalidad nativa de AWS, empodere al desarrollador y soporte la creación rápida de prototipos”, señala Nauman Noor, Director Gerente de la Plataforma de Nube Pública en State Street Corporation.

Lambda controla automáticamente las funciones por usted, reportando las métricas a través de Amazon CloudWatch. Para ayudarlo a resolver las fallas en una función, Lamda registra todas las solicitudes que maneja su función, además de almacenar automáticamente los registros que genera su código en CloudWatch Logs.

Cada invocación de una función Lambda publica un registro de “REPORT” al finalizar esa invocación. Esta entrada proporciona detalles sobre la duración de la invocación, duración facturada y cantidad de memoria utilizada, según se muestra en el siguiente ejemplo:

REPORT RequestId: b68fd6c0-f38e-42d0-82f2-48b7484e5fe5 Duration: 172.63 ms Billed Duration: 200 ms Memory Size: 128 MB Max Memory Used: 35 MB
REPORT RequestId: f610598b-9adc-430e-96f4-58340016e6b9 Duration: 179.20 ms Billed Duration: 200 ms Memory Size: 128 MB Max Memory Used: 35 MB
REPORT RequestId: 0809c288-8b19-4685-9d1a-6b98852299ff Duration: 179.29 ms Billed Duration: 200 ms Memory Size: 128 MB Max Memory Used: 35 MB
REPORT RequestId: 8b4a32a4-7dc0-4342-a998-48b051d683e0 Duration: 182.29 ms Billed Duration: 200 ms Memory Size: 128 MB Max Memory Used: 35 MB

Con CloudWatch Logs Insights, se puede extraer esta información en los registros REPORT de Lambda, y comprender mejor y asegurar una configuración apropiada. En un nivel más profundo, esto implica determinar las características de la duración del tiempo de ejecución de la invocación y analizar el uso de la memoria para mitigar las ocurrencias de memoria. También se puede saber si existe una correlación entre el uso de memoria y la duración del tiempo de ejecución para una función Lambda específica.

Enfoque

Antes de CloudWatch Logs Insights, el enfoque común consistía en suscribir una función de Lambda a los CloudWatch Logs generados por Lambda. La función tenía que analizar la información del registro (duración, duración facturada y tamaño de memoria) y almacenarla en un almacén de datos, como Amazon DynamoDB. Aunque este enfoque funcionaba, requería un esfuerzo adicional del desarrollador para gestionar el código y orquestar el despliegue, configuración e integración en esta solución. Esto no se alineaba con los objetivos de State Street de empoderar al desarrollador.

Nuestro enfoque consistía en tomar ventaja de CloudWatch Logs Insights a fin de proporcionar las capacidades de análisis heurístico mientras se empoderaba al desarrollador y se eliminaba la sobrecarga operativa.

La siguiente captura de pantalla muestra la interfaz de usuario de CloudWatch Log Insights, incluidos la consulta, la distribución de eventos de registro a lo largo del tiempo y los datos del registro.

Sin necesidad de configuración, CloudWatch Logs Insights provee al desarrollador la capacidad de crear consultas ad hoc. El desarrollador puede usar las consultas para entender el comportamiento de la función de Lambda de acuerdo con los registros almacenados en CloudWatch Logs. Además se pueden crear visualizaciones rápidas.

Ilustración de escenario

Para ilustrar algunos de estos conceptos, haré de guía en un escenario centrado en la comprensión de las características de tiempo de ejecución de una función de Lambda.

  1. En CloudWatch Logs Insights, seleccione un grupo de registros de Lambda.
  2. Ejecute las siguientes consultas:filter @type="REPORT"
    | stats avg(@billedDuration) as mean_billed_duration,
    min(@billedDuration) as min_billed_duration,
    max(@billedDuration) as max_billed_duration,
    percentile(@billedDuration, 95) as Percentile95filter @type="REPORT"
    | stats avg(@maxMemoryUsed/1024/1024) as mean_MemoryUsed,
    min(@maxMemoryUsed/1024/1024) as min_MemoryUsed,
    max(@maxMemoryUsed/1024/1024) as max_MemoryUsed,
    percentile(@maxMemoryUsed/1024/1024, 95) as Percentile95

    Las consultas ejecutadas realizan lo siguiente:
    ·        Filtran los registros “REPORT”.
    ·        Seleccionan un campo principal (@duration, @billedDuration, o @maxMemoryUsed).
    ·        Obtienen las estadísticas, tales como promedio, mínimo, máximo y porcentual.

    Esto arroja el siguiente resultado:

    # mean_billed_duration min_billed_duration max_billed_duration Percentile95
    1 30 38 46 54
    # mean_MemoryUsed min_MemoryUsed max_MemoryUsed Percentile95
    1 33.7255 30.5176 69.6182 33.3786
  3. Para comprender mejor la distribución de los campos, se debe generar una tabla de frecuencia para @maxMemoryUsed con la función bin(period) y determinar el rango de cada bucket de la siguiente manera:

    filter @type="REPORT"
    | fields floor(@maxMemoryUsed/1024/1024) as floor_var,
      ceil(@maxMemoryUsed/1024/1024) as ceil_var
    | stats min(floor_var) as min_Memory_Used,
      (min_Memory_Used + (max_Memory_Used - min_Memory_Used)/5) as bucket1,
      (min_Memory_Used + 2 * (max_Memory_Used - min_Memory_Used)/5) as bucket2,
      (min_Memory_Used + 3 * (max_Memory_Used - min_Memory_Used)/5) as bucket3,
      (min_Memory_Used + 4 * (max_Memory_Used - min_Memory_Used)/5) as bucket4,
      max(ceil_var) as max_Memory_Used

    # min_Memory_Used bucket1 bucket2 bucket3 bucket4 max_Memory_Used
    1 30 38 46 54 62 70
  4. Contar la cantidad de invocaciones que entran en el rango del bucket @maxMemoryUsed:

    filter @type="REPORT"
    | fields (@maxMemoryUsed/1024/1024) as MemoryMB,
      (MemoryMB>=30 and MemoryMB<38) as R30_38,
      (MemoryMB>=38 and MemoryMB<46) as R38_46,
      (MemoryMB>=46 and MemoryMB<54) as R46_54,
      (MemoryMB>=54 and MemoryMB<62) as R54_62,
      (MemoryMB>=62 and MemoryMB<=70) as R62_70
    | stats sum(R30_38) as MemoryUsed30MB_38MB,
      sum(R38_46) as MemoryUsed38MB_46MB,
      sum(R46_54) as MemoryUsed46MB_54MB,
      sum(R54_62) as MemoryUsed54MB_62MB,
      sum(R62_70) as MemoryUsed62MB_70MB

    # MemoryUsed30MB_38MB MemoryUsed38MB_46MB MemoryUsed46MB_54MB MemoryUsed54MB_62MB MemoryUsed62MB_70MB
    1 1242 0 0 0 49

    Hasta ahora, la mayoría de las invocaciones de Lambda consumieron entre 30 y 38 MB de memoria, mientras que 49 invocaciones consumieron entre 62 y 70 MB. Estos análisis hacen cuestionar si estas situaciones de uso alto de memoria pueden correlacionarse con un mayor tiempo de ejecución de la invocación.

  5. Al repetir este proceso en @billedDuration se obtitenen los siguiente resultados:

    filter @type="REPORT"
    | stats min(@billedDuration) as min_billed_duration,
      (min_billed_duration + (max_billed_duration - min_billed_duration)/5) as bucket1,
      (min_billed_duration + 2 * (max_billed_duration - min_billed_duration)/5) as bucket2,
      (min_billed_duration + 3 * (max_billed_duration - min_billed_duration)/5) as bucket3,
      (min_billed_duration + 4 * (max_billed_duration - min_billed_duration)/5) as bucket4,
      max(@billedDuration) as max_billed_duration

    # min_billed_duration bucket1 bucket2 bucket3 bucket4 max_billed_duration
    1 200 360 520 680 840 100

    filter @type="REPORT"
    | fields (@billedDuration>=200 and @billedDuration<360) as R200_360,
      (@billedDuration>=360 and @billedDuration<520) as R360_520,
      (@billedDuration>=520 and @billedDuration<680) as R520_680,
      (@billedDuration>=680 and @billedDuration<840) as R680_840,
      (@billedDuration>=840 and @billedDuration<=1000) as R840_1000
    | stats sum(R200_360) as billedDuration200_360,
      sum(R360_520) as billedDuration360_520,
      sum(R520_680) as billedDuration520_600,
      sum(R680_840) as billedDuration680_840,
      sum(R840_1000) as billedDuration840_1000

    # billedDuration200_360 billedDuration360_520 billedDuration520_600 billedDuration680_840 billedDuration840_1000
    1 1121 161 4 3 2

    Como se puede observar a partir de estos resultados, la mayoría de las invocaciones se facturan entre 200 y 360 ms de tiempo de ejecución, con algunas que toman más de 520 ms.

    A esta altura, me pregunto si las 49 invocaciones que consumieron la mayor cantidad de memoria corresponden a los rangos @billedDuration más altos. En otras palabras, ¿la duración del tiempo de ejecución se correlaciona con el uso de memoria, de manera que un uso alto de memoria requiere un tiempo de ejecución mayor?

    La siguiente consulta muestra un conteo de las invocaciones que usaron entre 62 y 70 MB de memoria junto con la duración facturada y la memoria que se utilizó.

    fields (@maxMemoryUsed/1024/1024) as MemoryMB
    | filter @type="REPORT"and (MemoryMB>=62 and MemoryMB<=70)
    | stats count(@billedDuration) by MemoryMB, @billedDuration

    # MemoryMB @billedDuration count(@billedDuration)
    1 69.6182 300 7
    2 69.6182 400 21
    3 69.6182 500 4
    4 69.6182 200 16
    5 68.6646 1000 1

    Analizando las 49 invocaciones que utilizaron entre 62 y 70 MB de memoria y su @billedDuration correspondiente muestra que sólo una se correspondió con los 1000 ms de tiempo de ejecución. La mayoría de las otras se ejecutaron en 400 ms o menos. En este punto, la relación no es tan obvia y la correlación parece ser débil.

  6. Al expandir el alcance del estudio a un período mayor (y un tamaño de muestra mayor) de 10 horas, intenté visualizar un promedio de movimiento de 5 minutos de @billedDuration (en ms) versus @memoryUsed (en 0.1 MB), como se muestra en el siguiente código:

    filter @type="REPORT"
    | stats avg(@billedDuration) as BilledDuration,
    avg(@maxMemoryUsed/1024/102.4) as MemoryUsedMB_scaled by bin(5m)

    La siguiente captura de pantalla muestra una visualización de un gráfico de líneas de la duración facturada y la memoria utilizada (MB) en el eje Y contra el tiempo en el eje X.

    Luego de expandir el alcance de estudio a un período mayor, en algunas situaciones, un pico de uso de memoria se podría corresponder con una duración de tiempo de ejecución mayor. Sin embargo, pareciera que la correlación entre @maxMemoryUsed y @billedDuration no es del todo clara.

    Este análisis sugiere que el equipo de desarrollo de aplicaciones debe revisar el flujo de ejecución. Ellos pueden ver si el tiempo de ejecución es una función de manejo de excepciones, que no es el caso usual en otros escenarios. O bien, es posible que el consumo de memoria sea una función de la información que está siendo procesada.

    En última instancia, la información que proporciona CloudWatch Logs Insights ayuda al equipo a enfocarse en las invocaciones específicas de interés desde una perspectiva de tiempo de ejecución y optimización del rendimiento.

Conclusión

En esta publicación, pude demostrar de qué manera CloudWatch Logs Insights ofrece un entendimiento rápido del comportamiento del tiempo de ejecución de Lambda, como por ejemplo, la duración y el uso de memoria, y la posible correlación entre ambas dimensiones.

Con CloudWatch, se puede analizar, filtrar, clasificar e inferir resultados estadísticos en una forma simple y repetible sin sobrecargar al equipo de desarrollo de aplicaciones teniendo que instrumentar sus aplicaciones para permitir el análisis.

Disfruto mucho de la facilidad de uso de CloudWatch Logs Insights. Quedo a la espera de mejoras futuras, tales como funciones de consulta robustas adicionales y la capacidad de configurar atributos personalizados dentro de las consultas que ayuden a elaborar un análisis más complejo, mientras se simplifican algunas de las técnicas propuestas.

El contenido y las opiniones de esta publicación corresponden a su autor y AWS no se responsabiliza por el contenido o la precisión de esta publicación.


Este blogpost es una traducción por Juan Pablo Denegri (AWS Senior Enterprise Solutions Architect) del original en Inglés.