En Keepler hemos afrontado el desarrollo de plataformas tipo SaaS, que junto con el avance de la nube pública como AWS o Azure, hacen que este tipo de productos sean muy atractivos para nuestros clientes, ya que factores como la rapidez de desarrollo, escalabilidad, seguridad, etc. promueven que la entrega de valor sea inmediata y reducen el time-to-market.

Entrando en la construcción de un SaaS, una de la mejores prácticas que nos ha traído la filosofía DevOps es manejar nuestra infraestructura como cualquier otro código, es lo que se conoce como “IaC” (Infrastructure as Code). Estas prácticas nos permiten diseñar Arquitectura Efímeras, lo cual en nuestra contexto significa que podamos tanto desplegar como destruir infraestructura y software a petición, mejorando la gestión y optimización de nuestros recursos en la nube.

En este artículo vamos a proponer un acercamiento para realizar despliegues de infraestructura automatizados y “apificados, esto nos permite realizar despliegues y labores de aprovisionamiento bajo demanda. La solución propuesta está diseñada con AWS y los servicios API Gateway, AWS Lambda, CloudFormation y Elastic Container Service con Fargate.

Solución Propuesta

Siguiendo un símil con el patrón Master-Worker, a esta solución la hemos denominado “Worker”, básicamente el funcionamiento consiste en que a partir de una petición vía API, el Worker se encargue de orquestar y ejecutar los despliegues de infraestructura de manera automática, gestionando los errores y comunicando el estado en todo momento.

Diagrama de Arquitectura:
1.Tiempo de Creación (Worker Config&Sync):

Para este caso, desarrollado en Python, se realiza un primer despliegue para crear las imágenes de Docker, importación de repositorios e infraestructura de ECS:

Módulo 1
Templates de CloudFormation para generar el registro en ECR, el cluster de ECS y tasks de ECS (solo necesario la primera vez).
* En la task de ECS recuerda configurar correctamente las policy para poder interactuar/desplegar otros servicios de AWS dentro del contenedor.

Módulo 2
Importación/Sincronización de la infraestructura a desplegar a través de sus respectivos repositorios en Git, para facilitar y agilizar esta sincronización hemos utilizado git submodules, de esta forma con un comando actualizaremos todos los recursos a desplegar.

Módulo 3
Generación de la imagen del contenedor, la cual registramos en AWS ECR. Este es el contenedor que ejecutará, sobre ECS, un script en bash el cual a través de AWS CLI irá desplegando la infraestructura.

Estructura del código y función de cada módulo

Ejemplo Dockerfile


FROM python:2.7-alpine3.10

ARG CLI_VERSION=1.16.190
ENV AWS_REGION eu-west-1

RUN apk -uv add --no-cache groff jq less curl zip gcc build-base make linux-headers coreutils gettext openssh && \
pip install --upgrade pip && \
pip install --no-cache-dir awscli==$CLI_VERSION boto3

COPY worker.sh /opt
COPY lib /opt

WORKDIR /opt

CMD ["./worker.sh"]

Ejemplo Script de despliegue: worker.sh

Para el ejemplo y por sencillez el método utilizado para el despliegue de infraestructura es AWS CLI en un script de bash, pero nada impide que puedas utilizar cualquier otro SDK, como por ejemplo boto3 para Python.

A modo de traza o log de la ejecución se han incluido inserciones en DynamoDB para que un tercero pueda en todo momento conocer el estado del despliegue.


##################### DynamoDB log resource creation ###########################################
aws dynamodb put-item \
--table-name infra-deploy-status \
--item "{
"id": {"S": "$ID"},
"currentStatus": {"S": "Creating"} ,
"action": {"S": "Deploying lambda-function-example"} ,
"updatedAt": {"S": "`date --iso-8601=seconds`"} }"
##################### DynamoDB log resource creation ########################################


echo "Deploying lambda-function-example..."
cd lambda-function-example
make

##################### DynamoDB resource creation result ###########################################
if [ $? != 0 ]
then
echo -e "${H1}Fail${NC}"
aws dynamodb put-item \
--table-name infra-deploy-status \
--item "{
"id": {"S": "$ID"},
"currentStatus": {"S": "KO"} ,
"action": {"S": "Failed deploy lambda-function-example"} ,
"updatedAt": {"S": "`date --iso-8601=seconds`"} }"
exit -1
else
echo -e "${H1}Done${NC}"
aws dynamodb put-item \
--table-name infra-deploy-status \
--item "{
"id": {"S": "$ID"},
"currentStatus": {"S": "OK"} ,
"action": {"S": "Lambda-function-example successfully deployed"} ,
"updatedAt": {"S": "`date --iso-8601=seconds`"} }"
fi
cd -
##################### Fin Ingest Status Result ###########################################

.
.
.
2. Tiempo de Ejecución (Container Launcher)

Función Lambda invocada desde API Gateway, su función es lanzar la ejecución del contenedor, el cual contiene el script que despliega la infraestructura. No olvidar que se pueden incluir variables de entorno en el contenedor.

A continuación, este es un ejemplo para Python con boto3 de ejecución del cluster ECS en modo Fargate, recuerda que cuando despliegues el cluster de ECS es necesario un security group y luego en la ejecución de la tarea es necesario también indicárselo.


response = ecs_client.run_task(
cluster="worker-cluster",
taskDefinition="worker",
overrides={
'containerOverrides': [
{
'name': 'worker',
'environment': [
{
'name': 'foo',
'value':
},
{
'name': 'bar',
'value':
},
],
},
]
},
count=1,
launchType='FARGATE'
networkConfiguration={ 'awsvpcConfiguration': { 'subnets': , 'securityGroups': } }
)
Beneficios y Desventajas

Este modelo se podría trasladar a otras tecnologías de AWS como Step Functions o Lambdas, quizá requieran de una sobreingeniería para su desarrollo, pero todo depende del contexto y necesidades de cada cual.

Comentar también que recientemente AWS presentó la funcionalidad Lambda Destinations, con la cual sin código, en invocaciones asíncronas podemos gestionar la rutas de ejecución en función del resultado, este modelo también nos facilita el desarrollo de componentes serverless para el despliegue de infraestructura.

Volviendo a nuestra aproximación, el uso de ECS nos brinda ventajas como no tener limitación de tiempo de ejecución, mayor flexibilidad al controlar totalmente lo que se ejecuta en el contenedor o una mayor centralización de los componentes a desplegar, pero también es cierto que añade cierta complejidad para quien no esté acostumbrado a trabajar con este tipo de tecnologías.

Próximos pasos y mejoras

Utilizar AWS Service Catalog para crear y administrar catálogos de servicios de nuestra plataforma, este servicio nos evitaria la tarea de importación de recursos al worker ya que sería suficiente con una petición de despliegue al Service Catalog.
Añadir soporte para Terraform,
Reemplazar Bash por Ansible, etc.

Fuentes: Esta aproximación se inspiró en este ejemplo de AWS en cual se integra Lambda y ECS para procesado de imágenes.

Imagen: unsplash | @johnschno

Author

  • Alberto Cantalejo

    Cloud Engineer en Keepler Data Tech: "Passionate about Data Engineering and Cloud Architectures. In continuous learning of new technologies that help us to build software in a faster, more efficient and secure way. My latest work is related to the development of Datalakes and its ecosystem on Serverless Architectures."