En esta publicación analizaremos como podemos automatizar tareas con AWS ECS y Scheduled Tasks, esto nos ayudará a crear crons que puedan ejecutar grandes cargas de trabajo por grandes periodos de tiempo. Antes de comenzar quiero compartir la razón detrás de esta elección y por qué los Scheduled Tasks en ECS pueden ser una mejor alternativa a Lambda en ciertos casos.
Problematica
Hoy en día la creación de procesos de automatización como ETL’s para procesamiento de datos representan en algunas ocasiones un consumo de recursos muy considerable. Comúnmente se configuran utilizando lambdas, sin embargo, muchas veces estos procesos requieren de horas para completarse correctamente y recordemos que las lambdas tienen un tiempo de ejecución máximo de 15 minutos, es por eso que se requiere de un servicio que permita ejecutar pesadas cargas de trabajo y además tengan un bajo costo.
Solución
Para solucionar esto muchos suelen mover las cargas de trabajo de lambdas a instancias EC2 donde pueden ejecutar cargas con mayor performance, pero esto incrementa los gastos y además generamos recursos con gastos pasivos, otra opción pueden ser las Step Functions, pero la configuración es muy complicada.
Aquí es donde entra en juego AWS ECS y Scheduled Tasks, ECS es un servicio de orquestación de contenedores administrado por AWS. Al usar ECS con Fargate, no es necesario gestionar servidores, ya que AWS maneja el aprovisionamiento y escalado automáticamente. Esto reduce costos, ya que solo se paga por los recursos utilizados en cada tarea, evitando instancias EC2 inactivas. En cargas intermitentes, Fargate puede ahorrar entre un 30% y 50% comparado con EC2. Además, simplifica la infraestructura y mejora la eficiencia operativa.
Configuración de nuestro stack
A continuación te muestro la configuración de una infra utilizando AWS CDK para poder correr un contenedor desde event Bridge utilizando ECS Scheduler Task. Este Pattern de CDK nos permite crear un Task que sea disparado mediante un Evento de Event Bridge.
Nota: Si tienes dudas respecto a AWS CDK Puedes comenzar leyendo esta publicación que brinda una introducción.
import * as cdk from 'aws-cdk-lib';
import { Construct } from "constructs";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as iam from "aws-cdk-lib/aws-iam";
import * as ecsPatterns from "aws-cdk-lib/aws-ecs-patterns";
import * as ecs from "aws-cdk-lib/aws-ecs";
import * as ecr from "aws-cdk-lib/aws-ecr";
import { Schedule } from 'aws-cdk-lib/aws-events';
import * as secrets from 'aws-cdk-lib/aws-secretsmanager';
import * as logs from 'aws-cdk-lib/aws-logs';
interface cdkProps extends cdk.StackProps {
environment: string;
}
export class etlCronEcsStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: cdkProps) {
super(scope, id, props);
// Definimos una variable para pasarle el tag de la imagen que utilizaremos en nuestro contenedor.
const IMAGETAG = new cdk.CfnParameter(this, 'IMAGETAG', {
type: 'String',
description: 'The name of the tag in the ECR repo to be deployed',
});
//Creamos un Role para nuestro task en aws
const executionRole = new iam.Role(this, "ETLRole", {
assumedBy: new iam.ServicePrincipal("ecs-tasks.amazonaws.com"),
});
// Creamos un cluster ECS
const cluster = new ecs.Cluster(this, "CronCluster", {
clusterName: `${props.environment}-etl-task`,
});
//Utilizando la imagen tag, seleccionamos una imagen en nuestro repositorio de ECR
const repo = ecr.Repository.fromRepositoryName(
this,
'etlECRImage',
'etl-image',
);
//Creamos un schedule task donde le pasaremos nuestra configuración
const scheduleFargateTask = new ecsPatterns.ScheduledFargateTask(
this,
"Task",
{
cluster, //Asignamos el cluster que acabamos de crear
scheduledFargateTaskImageOptions:{
image: ecs.ContainerImage.fromEcrRepository(repo, IMAGETAG.valueAsString),
memoryLimitMiB: 1024, //asignamos un limite de memoria a nuestro contenedor
cpu: 1024, //asignamos limite de CPU
logDriver: ecs.LogDriver.awsLogs({
streamPrefix: `etl`,
logGroup: new logs.LogGroup( this, 'log_group', {
logGroupName: `etl-task-${props.environment}`,
})
}),
},
schedule: Schedule.expression('cron(0 11,14,20,02 * * ? *)'), //Definimos un cron para nuestro task
platformVersion: ecs.FargatePlatformVersion.LATEST,
}
)
}
}
Con esta definición puedes fácilmente configurar tu stack para poder ejecutar tareas con contenedores docker utilizando AWS ECS y Event Bridge.