Despliegue Canary en funciones lambdas
Kevin Catucuamba
Posted on December 5, 2022
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.
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
Seguir las instrucciones
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.
En el directorio donde está definido el package.json instalar las dependencias:
npm install
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
}),
};
};
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
}),
};
};
Customer.ts
En models solo se tiene definido un tipo de dato Customer:
export type Customer = {
id: string;
name: string;
email: string;
age: number;
};
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:
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:
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
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
Se revisa en la consola de cloudformation para ver los recursos creados en el 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:
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:
Se comprueba el funcionamiento en 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
Una vez realizado los cambios se debe ejecutar los comandos:
sam build
sam deploy --capabilities CAPABILITY_NAMED_IAM
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.
Para poder evidenciar que la carga se está distribuyendo entre las dos versiones se realiza una prueba de carga con Jmeter.
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.
Espero hayas aprendido las bases sobre canary con este sencillo ejemplo en funciones lambdas, gracias.
Referencias
Posted on December 5, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.