El uso de FAAS es una tendencia que ha venido creciendo durante los últimos años, por ejemplo la frecuencia invocación de lambdas functions en AWS ha crecido 3.X ver Figura 1, una de las principales razones es la facilidad con la que se pueden lanzar funciones que realizan tareas muy específicas, su modelo de cobro favorece las pruebas de concepto y eventualmente su integración a producción

Figura 1: Crecimiento de la invocación de las lambda functions en AWS

Complejidad?

Me encuentro mucho con esta palabra complejidad, pues en este campo, mi trabajo consiste en reducirla manteniendo un balance con otros ítems medibles como costo. Al utilizar arquitecturas serverless si bien es cierto que ahorramos complejidades inherentes al cloud (específicamente al despliegue de código) como: pensar en máquinas virtuales, almacenamiento, procesos de sistemas, dependencias desactualizadas del OS etc, también heredamos otras complejidades Ver Figura 2, las lambdas deberían ejecutar tareas muy puntuales, por que no quieres tener un "monolito" en un servicio que te cobra por tiempo de ejecución, esto implica que tendrás que conectar tu lambda con otras lambdas  por diferentes medios , en donde la salida de una lambda seguramente será la entrada de otra, y probablemente tengas que comunicarte con apis de terceros, en conclusión al pensar en arquitectura serverless tendrás muchas lambdas ejecutándose y necesitarás auditarlas

Figura 2: Complejidad en lambdas https://medium.com/free-code-camp/serverless-is-cheaper-not-simpler-a10c4fc30e49

La magia

De las cosas "mágicas" que tenemos al utilizar lambdas y  desplegar gran parte de la arquitectura en una misma nube, es que se pueden utilizar sus servicios disponibles para realizar monitoreos sencillos, X-Ray es un servicio de AWS para realizar monitoreo de rendimiento de las lambdas y los servicios con los que interactúa, permitiendo de esta manera encontrar errores en cualquier ambiente que pueden ser un dolor de cabeza, para dar un contexto cuando tienes un flujo de comunicación entre varios componentes y no cumples los requerimientos de tiempo respuesta, encontrar que componente es el culpable es toda una aventura.., por ello configurar este tipo de servicio nos puede ayudar a entender que está sucediendo detrás esa caja mágica, para iniciar un poco en contexto, las lambdas tienen un ciclo de vida Ver Figura 3, en el que hay una etapa que se llama la inicialización o el "unfreezer" en donde la lambda abastece esa "máquina" con lo necesario para ejecutarse, en algunos problemas de rendimiento cuando la lambda no se usa mucho (es decir su invocación es muy poca) cada vez que se invoque se ejecutará este proceso lo cual agrega varios segundos a la ejecución, esto no es un problema por que generalmente la lambdas están hechas para procesar muchas peticiones al tiempo, cuando esto sucede la inicialización se hace una sola vez cada cierta cantidad de peticiones, por eso a veces es importante destapar esa caja mágica para entender que sucede, ahora vamos a utilizar X-Ray para seguir encontrando mas posibles problemas de rendimiento

Figura 3: Explicación ciclo de vida de la ejecución de un lambda

Ejemplo

Para activar X-Ray debemos hacer algunos pasos, yo usare serverless y en el archivo de configuración pondré lo siguiente

service: mylambdaprojectv2

frameworkVersion: '2'

custom:
  dynamodb_arn: ${file(./config.${opt:stage, 'dev'}.json):ARN}
provider:
  tracing:
    apiGateway: true
    lambda: true
  deploymentBucket:
    name: ejemplo-test
  name: aws
  runtime: python3.8
  lambdaHashingVersion: 20201221
  region: us-east-1
  environment:
    API: ${file(./config.${opt:stage, 'dev'}.json):API}
    TABLE_NAME: ${file(./config.${opt:stage, 'dev'}.json):TABLE_NAME}

  iam:
    role:
      statements:
        - Effect: Allow
          Action:
            - dynamodb:Query
            - dynamodb:Scan
            - dynamodb:GetItem
            - dynamodb:PutItem
            - dynamodb:UpdateItem
            - dynamodb:DeleteItem
            - dynamodb:DescribeTable
          Resource: ${self:custom.dynamodb_arn}

        - Effect: Allow
          Action:
            - xray:PutTraceSegments
            - xray:PutTelemetryRecords
          Resource: "*"



functions:
  register:
    handler: src.service.register.register
    timeout: 10
    events:
      - http:
          path: register
          method: post
          cors: true


package:
  exclude:
    - node_modules/**
    - venv/**


plugins:
  - serverless-deployment-bucket
  - serverless-python-requirements



debemos activar al tracing (es muy importante) y debemos dar permisos para poner segmentos e información de telemetry, esto lo pueden hacer por lambda, yo al ponerlo en provider lo hago global para todas las lambdas, en el código debemos instalar primero el SDK de X-Ray, posteriormente deben poner la siguiente configuración en su código

import json
import os
from logging import basicConfig, getLogger, INFO

import boto3
from aws_xray_sdk.core import patch_all

patch_all()

logger = getLogger(__name__)
basicConfig(level=INFO)

dynamodb = boto3.resource("dynamodb")
TABLE_NAME = os.environ["TABLE_NAME"]


def register(event, context):
    user_info = json.loads(event["body"])
    user_table = dynamodb.Table(TABLE_NAME)
    logger.info(user_info)
    response = user_table.put_item(Item=user_info)
    logger.info("User created")
    logger.info(response)

    return {"statusCode": 200, "body": json.dumps({"status": "ok"})}

el código lo importante aquí es el import de patch_all(), que es la función que se encarga de mandar la información de telemetría de una gran cantidad de librerías que ya tiene en el radar, si alguna libreria no esta agregada debe hacerse manual pero la diferencia es muy poca, solo son algunas lineas de mas, ahora con esta configuración lista, yo puedo hacer algún llamado ami API y dirigirme a cloudwatch  de mi lambda y veré lo siguiente

Figura 4: Vistazo general de los componentes gráficos generados por X-Ray

Como se aprecio en el código, se hacía una petición por medio de una API expuesta, este llamaba a un dynamodb y al final devuelve la respuesta, si yo paso el mouse por encima de cada componente tendrá cuanto tiempo demoro cada uno como se aprecia  en la siguiente Figura

Figura 5: Vistazo general por cada componente gráficos generados por X-Ray

y si se desea se puede ver en la misma sección más a detalle que ocurrió en cada componente

Figura 6: Vistazo específico de los componentes gráficos generados por X-Ray

Si ustedes ven, hay un componente que yo nunca escribí que se llama context, bueno aquí es donde se abre la caja mágica de lambdas, context es un proceso que realiza AWS por mi, en donde abastece de recursos luego mueve el código de los buckets a este contenedor y ejecuta dependencias, básicamente es un proceso de abastecimiento, este proceso el cual mencione al principio que se llama unfreeze, en teoría este proceso sucede cada vez que se llama la lambda, sin embargo cuando una lambda se llama de manera continua, este proceso crea un "caché" haciendo que no se realice este abastecimiento hasta que baje la cantidad de peticiones, esto tiene que ver también mucho con el concepto de versionamiento, donde el downtime por actualizacion de codigo es basicamente 0, debido a que las lambdas son versionadas y prácticamente no existe el " e cae un momento mientras termina el deploy", gracias a X-Ray se puede entender mejor el ciclo de vida de las lambdas. Vamos a agregar un llamado a una api de terceros utilizando requests que es una librería incluida en el patch de X-Ray por tanto lo agregara automáticamente al monitoreo, el código se vería de la siguiente manera

import json
import os
from logging import basicConfig, getLogger, INFO
import requests
import boto3
from aws_xray_sdk.core import patch_all

patch_all()

logger = getLogger(__name__)
basicConfig(level=INFO)

dynamodb = boto3.resource("dynamodb")
TABLE_NAME = os.environ["TABLE_NAME"]


def register(event, context):
    user_info = json.loads(event["body"])
    user_table = dynamodb.Table(TABLE_NAME)
    logger.info(user_info)
    response = user_table.put_item(Item=user_info)
    logger.info("User created")
    logger.info(response)
    response = requests.get("https://jsonplaceholder.typicode.com/todos/1")
    user = response.json()
    return {"statusCode": 200, "body": json.dumps(user)}

de esta forma quedaría el nuevo cambio

Figura 7: Vistazo general de los componentes con el cambio gráficos generados por X-Ray

y el tiempo a detalle

Figura 8: Vistazo específico con el cambio de los componentes gráficos generados por X-Ray

se puede apreciar un aumento considerable con respecto al código anterior, a pesar de que el llamado a la api de terceros solo toma 66 ms, donde queda el otro tiempo? el otro tiempo esta ubicado en el context ya que mientras se realizó el cambio la "lambda se en frío" y el llamado tomo el tiempo del abastecimiento, esto tiene algunas opciones para gestionar este ciclo, pero eso será para otro post

Conclusión

En diferentes artículos he podido hablar de telemetría de la información, creo que es vital saber donde ocurrió un problema o donde esta fallando algun componente sin tener entrar a debuguear, pues esto puede tomar mas tiempo y con estos sistema de monitoreo se pueden tener acciones "antes de" y actuar de formas más eficiente, en este caso al utilizar una misma nube, se puede tener una integración más sencilla, sin embargo hay una gran cantidad de herramientas que permite robustecer este proceso

Publicidad no paga

Ya esta llegando el fin de este año, e inicialmente inicie un reto personal el cual consistia en escribir un post semanalmente, ya quedan unas pocas semanas , por eso si quieres que escriba sobre algun tema, o si algun articulo mio te ha podido ayudar o te ha servido, sientanse libres de escribirme en cualquiera de mis redes sociales , y para compartirme sus apreciaciones