Appearance
LocalStack
AWS Local es posible
Si te has preguntado si usar AWS Localmente es posible, existen algunos mecanismos para poder probar ciertos componentes localmente mas no productivos. LocalStack provee un entorno local y simulado de AWS facilitando un numero interesante de caracteristicas para desarrollo en tu máquina o red privada, es decir, sin consumir recursos de AWS en tu etapa de desarrollo.
Ten presente que lo que hace es simular ciertos servicios de AWS sin que esto sustituya una operación productiva. Sin embargo, esto permite replicar nuestros ambientes para buscar tener una alta consistencia en el despliegue. Por lo que podrías usar la nube solo para el entorno productivo, dependiendo de los servicios que implementes.
LocalStack posibilita bajo la versión OpenSource usar los siguietes servicios:
- ACM
- API Gateway (v1)
- CloudFormation
- CloudWatch
- CloudWatch Logs
- DynamoDB
- DynamoDB Streams
- ElasticSearch Service (OpenSearch)
- EventBridge (CloudWatch Events)
- IAM
- Kinesis
- Kinesis Data Firehose
- KMS
- Lambda
- Route53
- S3
- SecretsManager
- SES (v1)
- SNS
- SQS
- SSM
- StepFunctions
- STS
En versión comunitaria, no se encuentran disponibles servicios como
RDS,EC2,ECS,EKS,EFS,ElastiCache,Cognito,Amplify,CloudFrontySSM(pues hacen parte de versión comercial).
En el caso deCognitose puede usar directamente la nube AWS (mezclando LocalStack).
En el caso deAmplifytambién se puede usar la nube de AWS (cuidando costos respectivos si quieres conservar capa gratuita)
Instalación de LocalStack
Para instalar LocalStack se puede descargar el paquete, por ejemplo ejecutando:
bash
python -m pip install localstack
localstack --versionEsto instalará una CLI usando Python3 (por lo que debe estar previamente instalado). Para macOS se usa
python3.
La última línea simplemente visualiza la versión buscando verificar su instalación.
Para iniciar el servicio se ejecutaría:
bash
localstack start
stopse usa para detener el servicio
Y para verificar los servicios disponibles se usaría:
bash
localstack status servicesAlternativa de instalación con Docker
Para instalar mediante Docker se ejecutaría lo siguiente:
bash
docker run --rm -it -p 4566:4566 localstack/localstack:3.8.0Esto inicializa el servicio en la dirección:
localhost:4566
De hecho, se puede verificar consultando con un navegador la dirección:localhost:4566/health
Puede ser que una manera alterna de gestionar la instalación sea usando los siguientes comandos:
bash
docker pull docker.io/localstack/localstack:3.8.0
docker run -d -p 4566:4566 --name localstack localstack/localstack:3.8.0
docker logs localstackPodemos probar la disponiblidad del servicio S3 ejecutando desde otra terminal lo siguiente:
bash
aws --endpoint-url http://localhost:4566 s3 ls
aws(CLI) debe estar instalado previamente, por ejemplo, para Windows se descarga desde enlace en AWS.
Para acceder al contenedor de modo interactivo se podría ejecutar:
bash
docker exec -u root -it localstack bash
./bin/localstack status servicesLa útima línea se ejecuta dentro de la instancia para verificar los servicios disponibles.
Alternativa usando Docker Compose
Si se desea utilizar una imágen mas ajustada es posible usar Docker Compose, por ejemplo, estableciendo un archivo docker-compose.yml (dentro de una carpeta de proyecto) con un contenido como el siguiente:
yaml
version: "3.8"
services:
localstack:
container_name: localstack
image: localstack/localstack:3.8.0
ports:
- "127.0.0.1:4566:4566"
- "127.0.0.1:4510-4559:4510-4559"
environment:
- SERVICES=serverless,cognito,sqs,sns,cloudformation
- DEBUG=1
- LAMBDA_EXECUTOR=docker
- DOCKER_HOST=unix:///var/run/docker.sock
- DATA_DIR=/tmp/localstack/data
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"Para armar nuestro contenedor debemos estar ubicados en la carpeta del proyecto que contiene el archivo docker-compose.yml y ejecutamos:
bash
docker-compose upSe puede verificar consultando con un navegador la dirección:
localhost:4566/health
Contexto sobre la configuración
Usando la CLI de LocalStack se podría ejecutar el comando anterior usando awslocal s3 ls. Para ello se instala lo siguiente:
bash
pip install awscli-localPara macOS se usa
pip3.
Además de tener instalada la CLI de AWS, debe estar configurada. Por ejemplo, usando:
bash
aws configure --profile defaultAl ejecutar este comando se debe indicar el valor
testen ambas claves de acceso (o variables de acceso) y la regionus-east-1
Podría también definirse un perfillocaly aplicarlo:aws configure --profile local
Ejemplo creando un Bucket S3
Para una mejor ilustración podemos crear un bucket de S3 y verficarlo, usando por ejemplo, los siguientes comandos:
bash
awslocal s3api create-bucket --bucket my-bucket
awslocal s3api list-bucketsCon primer comando creamos un
buckety con el segundo lo listamos para verificarlo
Podemos verificar desde el navegador consultando la dirección: http://s3.localhost.localstack.cloud:4566/
Ejemplo creando una Lambda
Para crear una Lambda se pueden usar comandos con un Script como el siguiente:
bash
#!/bin/bash
LAMBDA_FUNCTION_NAME="my-lambda"
zip -r $LAMBDA_FUNCTION_NAME.zip *.js package.json node_modules
awslocal lambda get-function --function-name $LAMBDA_FUNCTION_NAME > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "La función Lambda ya existe. Eliminándola..."
awslocal lambda delete-function --function-name $LAMBDA_FUNCTION_NAME
fi
awslocal lambda create-function \
--function-name $LAMBDA_FUNCTION_NAME \
--runtime nodejs20.x \
--handler index.handler \
--zip-file fileb://$LAMBDA_FUNCTION_NAME.zip \
--role arn:aws:iam::000000000000:role/lambda-role
awslocal lambda create-function-url-config \
--function-name $LAMBDA_FUNCTION_NAME \
--auth-type NONEBásicamente se comprimen los fuentes (usando
zip) y se usa comandocreate-functionpara desplegar función por primera vez. Se pueden reportar variables de entorno usando--environment(asociando por ejemplo, un archivojson).
El comando concreate-function-url-configpuede ser util para consumir la función a través dehttp
AWS CDK (Cloud Developer Kit) & LocalStack
En la plataforma de AWS (Amazon WebServices) es posible definir la infraesctructura usando código con un lenguaje de marcado como YAML, lo que se conoce como Infraestructura como Código (IaC - Infraestructure As Code). El componente tecnológico que permite esto se denomina CloudFormation. Es decir, con un archivo de estructura YAML se especifica un conjunto de servicios y como se aprovisionan en la plataforma de AWS de modo que puede ser replicable.
Luego, encontramos que también es posible usar un lenguaje de programación reconocido (tales como Javascript, Python, Java, C#, Go) para implementar IaC con un componente tecnológico que se denomina Cloud Developer Kit (CDK). Esto nos ofrece el uso de control de flujo con el lenguaje de programación y otras características para tener mayor control al definir nuestra IaC.
Como prerrequisitos de debe contar con un entorno Node.js instalado previamente, así como también contar con una cuenta configurada y la CLI (Command Line Interface) de AWS. A continuación veremos unos apuntes ágiles para iniciar con CDK.
Instalación de CDK
LocalStack provee un entorno local y simulado de AWS. Si se tiene LocalStack debidamente instalado y corriendo, es posible incorporar el uso del módulo cdklocal, el cual se instala ejecutando:
bash
npm install -g aws-cdk aws-cdk-localPuede comprobarse la instalación con el comando
npx cdk --version
Proyecto de inicio rápido
Para iniciar nuestro proyecto, nos ubicamos en una carpeta que se use como espacio de trabajo de proyectos y creamos nuestra subcarpeta para el proyecto. Luego inicializamos el proyecto, es decir, ejecutamos lo siguiente:
bash
mkdir project
cd project
cdk init app --language typescript
projectcorresponde a la carpeta asignada al proyecto (puede cambiarse).
En algunos casos puede ser necesario anteponernpxal comandocdk
A continuación estableceremos un Construct, que es un componente de servicio base para crear recursos. En nuestro ejercicio incluiremos DynamoDB para crear una tabla, por lo que ejecutamos lo siguiente:
bash
npm install @aws-cdk/aws-dynamodbNos dirigimos a nuestro proyecto para modificar el archivo lib/cdk-stack.ts, de modo que quede con el siguiente contenido:
typescript
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
export class CdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new dynamodb.Table(this, "user", {
partitionKey: {
name: "userId",
type: dynamodb.AttributeType.STRING
},
sortKey: {
name: "email",
type: dynamodb.AttributeType.STRING
}
});
}
}Mediante el uso de
dynamodb.Tablese define nuestra tabla
CDK genera código de CloudFormation, así que para ver el resultado ejecutamos:
bash
cdklocal synthEn algunos casos puede ser necesario anteponer
npxal comandocdklocal
Procedemos a establecer o asociar la cuenta simulada de AWS que usaremos con CDK ejecutando:
bash
cdklocal bootstrapFinalmente, para desplegar nuestra IaC ejecutamos:
bash
cdklocal deploy
cdklocal destroyelimina lo que desplegamos
Completando nuestro ejercicio con una Lambda
Para acceder a la base de datos usaremos una Lambda, por lo que ejecutamos lo siguiente:
bash
npm install @aws-cdk/aws-lambdaEl código de ejemplo a continuación sustituye el contenido anterior, siendo actualizado así:
typescript
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import { Runtime, FunctionUrlAuthType } from 'aws-cdk-lib/aws-lambda';
import * as lambda from 'aws-cdk-lib/aws-lambda-nodejs';
import * as path from 'path';
export class CdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// dynamo
const table = new dynamodb.Table(this, "user", {
partitionKey: {
name: "userId",
type: dynamodb.AttributeType.STRING
},
sortKey: {
name: "email",
type: dynamodb.AttributeType.STRING
}
});
// lambda
const handler = new lambda.NodejsFunction(this, "userHandler", {
runtime: Runtime.NODEJS_18_X,
entry: path.join(__dirname, `/../run/lambda.ts`),
handler: "handler",
environment: {
USER_TABLE_NAME: table.tableName,
},
});
// prvileges
table.grantReadWriteData(handler);
const userUrl = handler.addFunctionUrl({
authType: FunctionUrlAuthType.NONE,
cors: {
allowedOrigins: ['*'],
}
});
new cdk.CfnOutput(this, 'userUrl', {
value: userUrl.url,
});
}
}Nótese que se hace referencia a una lambda en el archivo
run/lambda.ts(el cual contiene el código de la función y deberá implementarse)