Desenvolvendo para Cloud AWS localmente com LocalStack
LocalStack é uma ferramenta que fornece toda estrutura necessário para o desenvolvimento local de aplicações para a Cloud. Ele gera um ambiente local que fornece a mesma funcionalidade e APIs que o ambiente real da Cloud AWS.
Dessa forma podemos executar funções em Lambda, armazenar dados em tabelas do DynamoDB, enviar eventos da fila SQS, alocar aplicações atrás de API Gateway e muito mais.
E tudo isso acontece na sua máquina local, sem nunca falar com a Cloud, economizando com o ambiente de teste.
O cliclo de desenvolvimento de uma aplicação para a Cloud utilizando o LocalStack é a seguinte:
- O desenvolvimento é realizado inteiramente na máquina local do desenvolveder, utilizando as APIs do LocalStack;
- O código é frequentemente enviado para um repositório de SCM como o
Git
para integração contínua que executa os testes automatizados O LocalStack provisiona todos os recursos "da Cloud" necessários no ambiente do servidor de integração contínua. - Após todos os testes estarem finalizados com sucesso, o próximo passo é realizar o deploy no ambiente real na Cloud AWS.
Uma das vantagens de usar o LocalStack tanto no desenvolvimento como nos testes integrados é a possibilidade de simular erros que acontecem frequentemente no ambiente real da Cloud.
Por exemplo, podemos aumentar taxa de transferência de leitura/gravação do DynamoDB ou Kinesis até a geração da
exceção ProvisionedThroughputExceededException
pelo Kinesis ou DynamoDB
Os resources atualmente disponibilizados na versão gratuita do LocalStack são:
Resources | EndPoint |
---|---|
API Gateway | http://localhost:4567 |
Kinesis | http://localhost:4568 |
DynamoDB | http://localhost:4569 |
DynamoDB Streams | http://localhost:4570 |
Elasticsearch | http://localhost:4571 |
S3 | http://localhost:4572 |
Firehose | http://localhost:4573 |
Lambda | http://localhost:4574 |
SNS | http://localhost:4575 |
SQS | http://localhost:4576 |
Redshift | http://localhost:4577 |
Elasticsearch | http://localhost:4578 |
SES | http://localhost:4579 |
Route53 | http://localhost:4580 |
CloudFormation | http://localhost:4581 |
CloudWatch | http://localhost:4582 |
SSM | http://localhost:4583 |
SecretsManager | http://localhost:4584 |
StepFunctions | http://localhost:4585 |
CloudWatch Logs | http://localhost:4586 |
CloudWatch Events | http://localhost:4587 |
STS | http://localhost:4592 |
IAM | http://localhost:4593 |
EC2 | http://localhost:4597 |
Instalação
Podemos realizar a instalação do LocalStack de duas formas.
A primeira é utilizando o pip
do Python. Não recomendo essa opção devido as necessidade de atender multiplas dependências.
$ pip install localstack
O recomendado é utilizar container por ser muito mais simples e ser facil integração. Nesse laboratório estou utilizando o podman
para executar o container.
$ podman run -it -p 4567-4599:4567-4599 -p 8080:8080 localstack/localstack
Se você tiver docker
, pode usar o docker-composer
.
ersion: '2.1'
services:
...
localstack:
image: localstack/localstack
ports:
- "4567-4584:4567-4584"
- "${PORT_WEB_UI-8080}:${PORT_WEB_UI-8080}"
environment:
- SERVICES=${SERVICES- }
- DEBUG=${DEBUG- }
- DATA_DIR=${DATA_DIR- }
- PORT_WEB_UI=${PORT_WEB_UI- }
- LAMBDA_EXECUTOR=${LAMBDA_EXECUTOR- }
- KINESIS_ERROR_PROBABILITY=${KINESIS_ERROR_PROBABILITY- }
- DOCKER_HOST=unix:///var/run/docker.sock
volumes:
- "${TMPDIR:-/tmp/localstack}:/tmp/localstack"
Uma interface do Admin Web Ui
é acessivel através do http://localhost:8080
.
Tutorial de Uso
Nesse tutorial vamos ver como utilizar o LocalStack. Vamos analisar os principais resources disponiveis, entrentado a utilização nao é diferente do uso do 'AWS CLI'.
Recurso: S3
Criar um novo Bucket com o nome Bucket-localstack
$ aws --endpoint-url=http://localhost:4572 s3 mb s3://bucket-localstack
Listando todos os bucket.
$ aws --endpoint-url=http://localhost:4572 s3 ls
$ aws --endpoint-url=http://localhost:4572 s3api list-buckets
Copiando o arquivo para o bucket
$ aws --endpoint-url=http://localhost:4572 s3 cp index.html s3://bucket-localstack
Removendo o bucket
$ aws --endpoint-url=http://localhost:4572 s3 rb s3://bucket-localstack --force
Recurso: SQS
Criando uma fila SQS
$ aws --endpoint-url=http://localhost:4576 sqs create-queue --queue-name fila-localstack
$ aws --endpoint-url=http://localhost:4576 sqs get-queue-attributes --queue-url http://localhost:4576/queue/fila-localstack --attribute-names All
Enviando mensagem para a fila
$ aws --endpoint-url=http://localhost:4576 sqs send-message --queue-url http://localhost:4576/queue/fila-localstack --message-body "Validando o localstack"
Lendo a mensagem da fila
$ aws --endpoint-url=http://localhost:4576 sqs receive-message --queue-url http://localhost:4576/queue/fila-localstack
Recurso: SNS
Criando um novo tópico.
$ aws --endpoint-url=http://localhost:4575 sns create-topic --name topic-localstack
Para assinar um tópico
aws --endpoint-url=http://localhost:4575 sns subscribe --topic-arn arn:aws:sns:us-east-1:000000000000:topic-localstack --protocol email --notification-endpoint clodonil@nisled.org
aws --endpoint-url=http://localhost:4575 sns subscribe --topic-arn arn:aws:sns:us-east-1:000000000000:topic-localstack --protocol sqs --notification-endpoint arn:aws:sqs:us-east-1:000000000000:fila-localstack
Publicar em um tópico
$ aws --endpoint-url=http://localhost:4575 sns publish --topic-arn arn:aws:sns:us-east-1:000000000000:topic-localstack --message "Hello World!"
Lista as subscrição
$ aws --endpoint-url=http://localhost:4575 sns list-subscriptions
Listando os tópicos:
$ aws --endpoint-url=http://localhost:4575 sns list-topics
Recurso: IAM
$ aws --endpoint-url=http://localhost:4593 iam list-roles
$ aws --endpoint-url=http://localhost:4593 iam list-policie
$ aws --endpoint-url=http://localhost:4593 iam create-group --group-name localstackgroup
$ aws --endpoint-url=http://localhost:4593 iam create-user --user-name localstack-user
$ aws --endpoint-url=http://localhost:4593 iam add-user-to-group --user-name localstack-user --group-name localstackgroup
$ aws --endpoint-url=http://localhost:4593 iam get-group --group-name localstackgroup
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SpecificTable",
"Effect": "Allow",
"Action": [
"dynamodb:BatchGet*",
"dynamodb:DescribeStream",
"dynamodb:DescribeTable",
"dynamodb:Get*",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:BatchWrite*",
"dynamodb:CreateTable",
"dynamodb:Delete*",
"dynamodb:Update*",
"dynamodb:PutItem"
],
"Resource": "arn:aws:dynamodb:*:*:table/MyTable"
}
]
}
$ aws --endpoint-url=http://localhost:4593 iam create-policy --policy-name my-policy --policy-document file://policy_dynamodb.json
Recurso: Route53
$ aws --endpoint-url=http://localhost:4580 route53 create-hosted-zone --name localstack.corp --caller-reference 2014-04-01-18:47 --hosted-zone-config Comment="command-line version"
$ aws --endpoint-url=http://localhost:4580 route53 change-resource-record-sets --hosted-zone-id /hostedzone/Z3TUCH569WK9YCQ --change-batch file://sample.json
$ aws --endpoint-url=http://localhost:4580 route53 get-change --id //change/C2682N5HXP0BZ4
Recurso: CloudFormation
O EndPoint do CloudFormation é http://localhost:4581
.
Como exemplo vamos utilizar o seguinte template de CloudFormation que instância uma lista SQS, cria role e armazena dados no SSM.
Vamos utilizar o aws cli
para criar uma nova stack de CloudFormation.
aws --endpoint-url=http://localhost:4581 cloudformation create-stack --stack-name infra \
--template-body file://cf/template.yml
Podemos utilizar o wait
para aguardar a stack ser criada.
aws --endpoint-url=http://localhost:4581 cloudformation wait stack-create-complete --stack-name infra
Podemos listar todas as stack criadas.
aws --endpoint-url=http://localhost:4581 cloudformation describe-stack-events
Recurso: CloudWatch
aws --endpoint-url=http://localhost:4582 cloudwatch set-alarm-state --alarm-name "myalarm" --state-value ALARM --state-reason "testing purposes"
aws --endpoint-url=http://localhost:4587 events put-rule --name my-scheduled-rule --schedule-expression 'rate(1 minutes)'
Recurso: SSM
$ aws --endpoint-url=http://localhost:4583 ssm put-parameter --name "nome" --type "String" --value "clodonil" --overwrite
$ aws --endpoint-url=http://localhost:4583 ssm get-parameters --names "nome"
Recurso: DynamoDB
Criando a tabela no Dynamdb.
$ aws --endpoint-url=http://localhost:4569 dynamodb create-table --table-name table1 \
--attribute-definitions AttributeName=Nome,AttributeType=S AttributeName=Idade,AttributeType=N \
--key-schema AttributeName=Nome,KeyType=HASH AttributeName=Idade,KeyType=RANGE \
--provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5
Lista as tabelas criadas do Dynamodb.
$ aws --endpoint-url=http://localhost:4569 dynamodb list-tables
Populando a tabela do Dynamodb.
$ aws --endpoint-url=http://localhost:4569 dynamodb put-item --table-name table1 --item '{ "Nome":{"S":"Clodonil"},"Idade":{"N":"10"}}' --return-consumed-capacity TOTAL
Realizando um scan na tabela para retornar todos os items.
$ aws --endpoint-url=http://localhost:4569 dynamodb scan --table-name table1 --return-consumed-capacity TOTAL
Obtendo um item especifico.
$ aws --endpoint-url=http://localhost:4569 dynamodb get-item --table-name table1 --key '{"Nome": {"S": "Clodonil"}, "Idade":{"N":"10"}}'
Recurso: Lambda
$ cd tutorial/lambda
$ zip lambda_v1.zip index.py
$ aws --endpoint-url=http://localhost:4574 lambda create-function --function-name my-function --zip-file fileb://./lambda_v1.zip --handler index.handler --runtime python3.7 --role arn:aws:iam::000000000000:role/roles2-CopyLambdaDeploymentRole-UTTWQYRJH2VQ
{
"FunctionName": "my-function",
"FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:my-function",
"Runtime": "python3.7",
"Role": "arn:aws:iam::000000000000:role/roles2-CopyLambdaDeploymentRole-UTTWQYRJH2VQ",
"Handler": "index.handler",
"CodeSize": 287,
"Description": "",
"Timeout": 3,
"LastModified": "2019-12-03T03:41:49.290+0000",
"CodeSha256": "+tIqyD399zm0ArPmCapjD14klK5pML/v9UQZcQQW4eM=",
"Version": "$LATEST",
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "796080cb-a3f0-48e9-8e9c-11607254fc8d"
}
Listando as lambdas:
$ aws --endpoint-url=http://localhost:4574 lambda list-functions
Envocando a lambda e passando um payload.
$ aws --endpoint-url=http://localhost:4574 lambda invoke --function-name my-function --payload '{}' saida.txt
No arquivo saida.txt
está a retorno da lambda.
$ cat saida.txt
Recurso: Api Gateway
$ aws --endpoint-url=http://localhost:4567 apigateway create-rest-api --name 'HelloWorld (AWS CLI)'
$ aws --endpoint-url=http://localhost:4567 apigateway apigateway get-resources --rest-api-id 36twoi21n1
$ aws --endpoint-url=http://localhost:4567 apigateway create-resource --rest-api-id 36twoi21n1 --parent-id kr9zrfk6bq --path-part greeting
$ aws --endpoint-url=http://localhost:4567 apigateway put-method --rest-api-id 36twoi21n1 --resource-id nxoi851xk0 --http-method GET --authorization-type "NONE" --request-parameters method.request.querystring.greeter=false
https://aws.amazon.com/pt/premiumsupport/knowledge-center/iam-assume-role-cli/