DEV Community

Cover image for Despliegue Canary en funciones lambdas
Kevin Catucuamba
Kevin Catucuamba

Posted on • Updated on

Despliegue Canary en funciones lambdas

En esta ocasión se realizará un ejemplo sencillo para comprender los fundamentos de un despliegue Canario usando servicios de AWS, este tipo de despliegue está siendo muy usado en la industria por el crecimiento de serverless y contenedores.

¿Qué es el despliegue de tipo Canary?

El despliegue Canary consiste en desplegar nuevas versiones de software de manera incremental, es decir, durante un tiempo una porción de usuarios acceden a la nueva versión desplegada y otro grupo de usuarios siguen consumiendo a la versión anterior, los usuarios sirven como carnada para consumir los servicios e indicar que todo está marchando bien y esto permite seguir incrementando de forma gradual a la última versión desplegada. Si durante el uso existe un error, se realiza un rollback dejando en la versión anterior y sin afectar a los demás usuarios.

Image description

Ejemplo práctico

Para el ejemplo práctico se crea dos funciones lambdas y se usa el servicio de Api Gateway para poder realizar pruebas mediante Postman y evidenciar el despliegue incremental Canary.

Requisitos previos

  • Cuenta de AWS y tener configurado AWS CLI y SAM CLI
  • Conocimientos esenciales cloudformation, lambdas y api gateway
  • Manejo de Postman y Jmeter
  • NodeJs con typescript

Inicio de proyecto

Se inicia el proyecto a partir de una plantilla básica proporcionada en SAM CLI, ejecutando el siguiente comando:

sam init
Enter fullscreen mode Exit fullscreen mode

Seguir las instrucciones

Config 1

Config 2

Se genera un proyecto básico de un hello world, se borra la entrada principal app.ts y todos los test. Se crea una estructura de carpetas de la siguiente manera:

Nota: la carpeta hello-world ahora es app.

Estructura de proyecto

En el directorio donde está definido el package.json instalar las dependencias:

npm install
Enter fullscreen mode Exit fullscreen mode

A continuación se muestra el código de ejemplo de cada función, no se está conectando a ninguna base de datos para este ejemplo, pero ya depende de lo que necesitemos, el objetivo es demostrar el despliegue:

createCustomer.ts

import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
import { Customer } from '../models/Customer';

export const handler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
    const customer: Customer = JSON.parse(event.body ?? '{}');
    //TODO: generate id for customer
    customer.id = '1234';
    return {
        statusCode: 200,
        body: JSON.stringify({
            message: 'Customer created',
            customer
        }),
    };
};
Enter fullscreen mode Exit fullscreen mode

getCustomerById.ts

import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
import { Customer } from '../models/Customer';

export const handler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {

    const id = event.pathParameters?.id;

    const customer: Customer = {
        id: id ?? '',
        name: 'John Doe',
        email: 'doe@qw.com',
        age: 30
    };

    return {
        statusCode: 200,
        body: JSON.stringify({
            message: 'Customer found',
            customer
        }),
    };
};
Enter fullscreen mode Exit fullscreen mode

Customer.ts

En models solo se tiene definido un tipo de dato Customer:

export type Customer = {
    id: string;
    name: string;
    email: string;
    age: number;
};
Enter fullscreen mode Exit fullscreen mode

Archivo template.yaml

Listo ahora que está todo el código fuente, es necesario configurar la infraestructura a desplegar en AWS.

Se realiza las configuraciones globales de las funciones y de la api:

Globals

Nota: La propiedad DeploymentPreference configura a la función lambda para realizar el despliegue incremental. Para conocer todos los tipos que existen se puede revisar el siguiente enlace: Deploy Canary.

En los recursos se definen las Lambdas y el ApiGateway a utilizar:

Image properties

Nota: Se configura un alias de producción para simular sobre ese escenario.

Se muestra toda la configuración en template.yaml

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  init-app

  Sample SAM Template for customers

Globals:
  Function:
    CodeUri: app
    Timeout: 10
    Tracing: Active
    Runtime: nodejs16.x
    Architectures:
      - x86_64
    DeploymentPreference:
      Enabled: true
      Type: 'Linear10PercentEvery1Minute'
  Api:
    TracingEnabled: True

Resources:

  ApiGateway:
    Type: AWS::Serverless::Api
    Properties:
      StageName: v1
      Variables:
        version_alias: 'production'
      EndpointConfiguration:
        Type: REGIONAL 

  CreateCustomerFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: src/functions/createCustomer.handler
      FunctionName: !Sub ${AWS::StackName}-create-customer
      AutoPublishAlias: production
      Events:
        CreateCustomer:
          Type: Api
          Properties:
            Path: /customers
            Method: post
            RestApiId: !Ref ApiGateway
    Metadata:
      BuildMethod: esbuild
      BuildProperties:
        Minify: true
        Target: "es2020"
        EntryPoints: 
        - src/functions/createCustomer.ts

  GetCustomerByIdFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: src/functions/getCustomerById.handler
      FunctionName: !Sub ${AWS::StackName}-get-customer-by-id
      AutoPublishAlias: production
      Events:
        CreateCustomer:
          Type: Api
          Properties:
            Path: /customers/{id}
            Method: get
            RestApiId: !Ref ApiGateway
    Metadata: 
      BuildMethod: esbuild
      BuildProperties:
        Minify: true
        Target: "es2020"
        EntryPoints: 
        - src/functions/getCustomerById.ts

Enter fullscreen mode Exit fullscreen mode

Nota: Algunos recursos como permisos y roles se crea detrás de escena gracias a la ayuda de SAM.

Despliegue de stack

En el directorio principal en donde está la plantilla template.yaml ejecutar los siguientes comandos para empaquetar y desplegar:

sam build
sam deploy --guided --capabilities CAPABILITY_NAMED_IAM
Enter fullscreen mode Exit fullscreen mode

Pasos

Se revisa en la consola de cloudformation para ver los recursos creados en el stack:

Stack

Si se revisa una de las funciones podemos ver el alias y una tercera versión creada, esto debido a que anteriormente se hizo unos despliegues de prueba, si es primer despliegue desde cero estará una versión 1:

Version

Para el ejemplo se toma la función que simula crear un Cliente para realizar las pruebas, revisar el recurso de ApiGateway para obtener la URL a consultar:

Url

Se comprueba el funcionamiento en Postman:

Postman

Actualizar código para evidenciar el despliegue Canary

Para poder ver la ejecución de Canary hay que editar el código y desplegar, se podrá ver que de a poco va incrementando el peso de la versión.

En este caso se edita la función que simula crear un Cliente.

Se instala las siguientes dependencias y se edita el código como se muestra:

npm i short-uuid
Enter fullscreen mode Exit fullscreen mode

Code actualizado

Una vez realizado los cambios se debe ejecutar los comandos:

sam build
sam deploy --capabilities CAPABILITY_NAMED_IAM
Enter fullscreen mode Exit fullscreen mode

Prueba de Canary

El despliegue tipo Canary empieza, por cada minuto se incrementa de forma lineal un 10% para pasar a la versión 4.

Canary despliegue

Para poder evidenciar que la carga se está distribuyendo entre las dos versiones se realiza una prueba de carga con Jmeter.

Image description

Nota: Si no conoces sobre Jmeter para pruebas de carga puedes revisar el siguiente recurso.

En los logs de la función de Cloudwatch se puede observar como ha realizado llamadas entre las dos versiones 3 y 4.

Jmeter

Espero hayas aprendido las bases sobre canary con este sencillo ejemplo en funciones lambdas, gracias.

Referencias

Top comments (0)