1. Introdução

Em muitos ambientes corporativos, é comum manter instâncias ligadas mesmo durante períodos em que não estão sendo utilizadas. Em ambientes de desenvolvimento, homologação e testes, por exemplo, muitas instâncias EC2 permanecem em execução fora do horário comercial, gerando custos mesmo sem demanda. Uma alternativa simples para reduzir esse desperdício é automatizar o start/stop das máquinas, definindo horários para ligar e desligar de acordo com a necessidade do ambiente.

Neste artigo, será apresentada uma arquitetura na AWS, utilizando os serviços EC2, EventBridge, Lambda (o código será em Python), IAM e CloudWatch Logs para automatizar esse processo. A solução também utiliza tags para identificar quais recursos devem participar da automação. Além de reduzir custos, a automação ajuda a evitar processos manuais e cria um padrão para o gerenciamento de ambientes que não precisam permanecer ativos 24h por dia.

Abaixo, arquitetura da solução:

2. Serviços utilizados na arquitetura

2.1 Amazon EC2

O Amazon EC2 oferece as máquinas virtuais (instâncias) que a automação vai ligar e desligar. Para dizer à automação quais instâncias devem participar, será utilizada uma tag, que é uma etiqueta formada por uma chave e um valor.

Por exemplo:

tag_key = "lambda-start-stop" tag_value = "true" 

Assim, a função Lambda pode buscar somente as instâncias que possuem essa tag.

Documentação: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html

Documentação: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Instances.html

--

2.2 Amazon EventBridge

Responsável por definir quando a automação será executada. O EventBridge Scheduler chama a função Lambda no horário configurado.

Exemplo comum nas empresas:

  • 08h00 - ligar as instâncias
  • 18h00 - desligar as instâncias

Documentação: https://docs.aws.amazon.com/scheduler/latest/UserGuide/what-is-scheduler.html

--

2.3 AWS Lambda

É responsável por executar o código da automação, sem a necessidade de gerenciar servidores. Neste caso, a função roda um código em Python que usa o boto3 (o SDK da AWS para Python) para:

  1. Procurar as instâncias com a tag lambda-start-stop = true
  2. Executar a ação de start ou stop, conforme o horário que disparou a função.

Documentação: https://docs.aws.amazon.com/lambda/latest/dg/welcome.html

--

2.4 IAM Role

Define quais permissões a Lambda possui. Por exemplo, a função pode receber permissão para:

  • Iniciar instâncias EC2
  • Parar instâncias EC2
  • Consultar informações das instâncias
  • Enviar logs para o CloudWatch

Documentação: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html

--

2.5 Amazon CloudWatch

O CloudWatch guarda os logs de cada execução da Lambda. A Lambda envia esses logs automaticamente, desde que a role tenha as permissões adequadas. Os logs podem mostrar:

  • Quando a função foi executada
  • Status das instâncias encontradas
  • Quais ações foram realizadas
  • Possíveis erros durante a execução

Documentação: https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/WhatIsCloudWatchLogs.html

3. Implementação

3.1 Criação da IAM policy

Para que a Lambda consiga ligar e desligar as instâncias, é necessário criar no IAM uma policy com as permissões necessárias e uma role (função de execução) que será associada à Lambda. A policy deve permitir que a Lambda chame as ações da API do EC2 para consultar, iniciar e parar instâncias, além de enviar os logs para o CloudWatch.

1.Na barra de pesquisa, procurar por “IAM” e acessar o serviço:

2.Após acessar o IAM, clicar em “Policies” e “Create policy” para criar uma nova política:

3.Ao abrir a tela de criação, clicar em “JSON”, inserir o código abaixo e clicar em “Next” para dar continuidade:

Essa política define o que a função Lambda está autorizada a fazer. Neste caso, ela permite que a Lambda registre logs no CloudWatch e consulte, inicie e pare instâncias EC2.

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:*:*:*" }, { "Effect": "Allow", "Action": [ "ec2:Start*", "ec2:Stop*", "ec2:Describe*" ], "Resource": "*" } ] } 

4.Em seguida, revisar e clicar em “Create policy”:

5.A política foi criada com sucesso:

--

3.2 Criação da IAM role

1.Acessar a aba “Roles” e clicar em “Create role”:

2.Em seguida, selecionar o serviço “Lambda”, caso de uso e clicar em “Next”:

3.Na próxima tela, pesquisar e selecionar a política “ec2-start-stop-policy” criada anteriormente e clicar em “Next”:

4.Em seguida, definir um nome para a role, revisar e clicar em “Create role”, para criar a “ec2-start-stop-role”:

5.A role foi criada com sucesso:

--

3.3 Criação da função Lambda

1.Na barra de pesquisa, procurar por “Lambda” e acessar o serviço:

2.Em seguida, clicar em “Create a function”:

3.Na próxima tela, definir um nome para as duas funções (ec2-start e ec2-stop), selecionar “Python”, que será a linguagem de programação utilizada na automação e clicar em “Create funcion”:

4.O Timeout define o tempo máximo que a função Lambda pode permanecer em execução. O valor padrão é de 3 segundos, mas pode ser alterado conforme a necessidade da aplicação. Para essa automação, será utilizado um timeout de 30 segundos, proporcionando tempo suficiente para consultar e executar as operações nas instâncias EC2.

  • Além do timeout, também será necessário selecionar a role que foi criada “ec2-start-stop-role”. Esses procedimentos deverão ser realizados tanto na Lambda “ec2-start” quanto na “ec2-stop”:

5.Agora, podemos inserir o código na função criada “ec2-start” e clicar em “Deploy”:

Código Python “ec2-start”:

 import json import boto3 import logging logger = logging.getLogger() logger.setLevel(logging.INFO) def lambda_handler(event, context): region = 'us-east-1' client = boto3.client('ec2', region_name=region) tag_key = "lambda-start-stop" tag_value = "true" try: response = client.describe_instances( Filters=[ { 'Name': f'tag:{tag_key}', 'Values': [tag_value] }, ] ) for reservation in response['Reservations']: for instance in reservation['Instances']: instanceId = instance['InstanceId'] state = instance['State']['Name'] logger.info(f"Instance ID: {instanceId} - State: {state}") if state == 'stopped': logger.info(f"Starting instance {instanceId}") client.start_instances(InstanceIds=[instanceId]) else: logger.info(f"Instance {instanceId} is not in 'stopped' state, current state: {state}") except Exception as e: logger.error(f"Error managing EC2 instances: {str(e)}") raise return { 'statusCode': 200, 'body': 'Start script executed successfully' } 

6.Em seguida, podemos fazer o mesmo com a função Lambda “ec2-stop”:

Código Python “ec2-stop”:

import json import boto3 import logging logger = logging.getLogger() logger.setLevel(logging.INFO) def lambda_handler(event, context): region = 'us-east-1' client = boto3.client('ec2', region_name=region) tag_key = "lambda-start-stop" tag_value = "true" try: response = client.describe_instances( Filters=[ { 'Name': f'tag:{tag_key}', 'Values': [tag_value] }, ] ) for reservation in response['Reservations']: for instance in reservation['Instances']: instanceId = instance['InstanceId'] state = instance['State']['Name'] logger.info(f"Instance ID: {instanceId} - State: {state}") if state == 'running': logger.info(f"Stopping instance {instanceId}") client.stop_instances(InstanceIds=[instanceId]) else: logger.info(f"Instance {instanceId} is not in 'running' state, current state: {state}") except Exception as e: logger.error(f"Error managing EC2 instances: {str(e)}") raise return { 'statusCode': 200, 'body': 'Stop script executed successfully' } 

--

3.4 Configuração do CloudWatch para visualizar os logs

1.Na barra de pesquisa, procurar por “CloudWatch” e acessar o serviço:

2.Na aba “Logs”, clicar em “Log Management” e em “Create log group”:

3.Na próxima tela, definir o nome dos grupos de logs (ec2-start-logs e ec2-stop-logs), retenção (5 dias, por exemplo), classe, habilitar a proteção contra deleção e clicar em “Create”:

4.Os grupos de logs foram criados com sucesso:

5.Agora será necessário acessar a função Lambda “ec2-start” e editar o destino dos logs que vem por padrão, apontando para o grupo de logs criado acima “ec2-start-logs”:

Destino alterado com sucesso:

6.Em seguida, repetir o mesmo com a função Lambda “ec2-stop” removendo o destino padrão e apontando para o grupo de logs “ec2-stop-logs”:

Destino alterado com sucesso:

--

3.5 Criação das instâncias EC2 para testar a Lambda

1.Para testar se as funções Lambda estão funcionando corretamente, será necessário criar uma instância para teste.

  • Na aba de pesquisa, procurar por “EC2”, acessar e clicar em “Launch instance”:

2.Definir um nome para a instância (por exemplo: srv-test-01) e clicar em “Add additional tags” para inserir a chave “lambda-start-stop” e valor “true”, conforme está no código:

3.A máquina foi criada com sucesso e está ligada:

4.Para testar se a função “ec2-stop” vai desligar a instância, podemos clicar em “Test” e na barra que irá abrir, escrever algo como “Test” também, seguido e um Enter:

5.Na próxima tela, será necessário definir um nome (por exemplo: test-01) e clicar em “Save”:

6.Em seguida, podemos clicar em “Test” para iniciar a execução:

7.A execução do código está em andamento:

O desligamento da máquina “srv-test-01” foi executado com sucesso:

No CloudWatch, é possível visualizar os logs no grupo “ec2-stop-logs”, onde aparece o estado da instância antes da execução da função Lambda (running) e depois da execução (stopped).

8.Em seguida, podemos repetir o mesmo procedimento com a função “ec2-start”, para ligar a máquina “srv-test-01”. Para isso, outro caminho é clicar em “Create new test event”, na aba “TEST EVENTS”, definir um nome (por exemplo: test-02) e clicar em “Save”:

Em seguida, clicar em “Test” para executar. A função foi executada com sucesso:

A instância foi ligada com sucesso:

Source:https://dev.to/biancaszimanski/finops-na-aws-reduzindo-custos-ao-ligar-e-desligar-instancias-ec2-automaticamente-34mj