DEV Community

Cover image for FinOps na AWS: reduzindo custos ao ligar e desligar instâncias EC2 automaticamente
Bianca Szimanski
Bianca Szimanski

Posted on

FinOps na AWS: reduzindo custos ao ligar e desligar instâncias EC2 automaticamente

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"
Enter fullscreen mode Exit fullscreen mode

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": "*"
  }
 ]
}
Enter fullscreen mode Exit fullscreen mode

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'
    }
Enter fullscreen mode Exit fullscreen mode

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'
    }
Enter fullscreen mode Exit fullscreen mode

--

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:

No CloudWatch, é possível visualizar os logs no grupo “ec2-start-logs”:

--

3.6 Configuração do EventBridge

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

2.Em seguida, selecionar a opção “Schedules”e clicar em “Create schedule” para agendar o start e stop da intância:

3.Definir o nome, recorrência, horário e data do agendamento:

A expressão 30 21 ? * MON-FRI * configura o EventBridge para executar o agendamento às 21h30, de segunda a sexta-feira, considerando o fuso horário America/Sao_Paulo.

  • 30: significa 30 minutos
  • 21: significa 21h00
  • ?: qualquer dia do mês
  • *: todos os meses
  • MON-FRI: segunda a sexta-feira
  • *: todos os anos

4.Na próxima tela, selecionar o serviço Lambda, função “ec2-stop” e clicar em “Next”:

Revisar e concluir a criação:

O agendamento foi criado com sucesso:

Às 21h30, conforme o agendamento configurado, a instância foi desligada:

5.Para criar o agendamento que liga a instância, podemos seguir o mesmo caminho acima, selecionando a função Lambda “ec2-start”. O horário de agendamento será às 21h50, com a expressão cron(50 21 ? * MON-FRI *).

O agendamento foi criado com sucesso:

Às 21h50, conforme o agendamento configurado, a instância foi ligada:


4. Considerações sobre custos

A solução usa apenas serviços serverless e gerenciados, cobrados por uso. Mesmo sendo uma automação simples, vale entender quanto cada componente custa e, principalmente, quais custos continuam existindo mesmo com as instâncias desligadas.

  • EventBridge Scheduler: é cobrado pelo número de invocações. O free tier inclui 14 milhões de invocações por mês, e acima disso o preço é de US$1,00 por milhão. https://aws.amazon.com/eventbridge/pricing/
  • Lambda: é cobrada por requisições e por tempo de execução (GB por segundo). O free tier inclui um milhão de solicitações gratuitas por mês e 400.000 GB/s de tempo de computação por mês. https://aws.amazon.com/lambda/pricing/
  • CloudWatch Logs: armazena os logs de execução da Lambda. Como cada execução gera poucas linhas de log, o volume é mínimo. Mesmo assim, é importante definir um período de retenção no log group (por exemplo, 5 dias), já que o padrão é manter os logs indefinidamente.
  • IAM: não possui custo adicional.
  • EC2: desligar a instância não zera o custo dela. Alguns recursos continuam sendo cobrados normalmente, como por exemplo, EBS e IP.

5. Conclusão

Ambientes que não precisam ficar disponíveis 24h por dia, como desenvolvimento, homologação e testes, estão entre as maiores fontes de desperdício em cloud. Automatizar o ligar e desligar das instâncias é uma das ações de FinOps mais simples de implementar e com retorno mais rápido. A solução apresentada combina 3 benefícios:

  • Redução de custos: ao manter as instâncias ligadas apenas no horário de uso, o custo de computação pode cair cerca de 64% em um cenário de 12h por dia em dias úteis. A automação em si custa praticamente nada, já que o EventBridge Scheduler, a Lambda e o CloudWatch Logs operam dentro dos limites gratuitos na maioria dos casos.
  • Automação confiável: com o EventBridge Scheduler configurado no fuso horário correto e a Lambda selecionando as instâncias por tags, a rotina funciona sozinha. Incluir uma nova instância é só questão de aplicar a tag, sem alterar código.
  • Menos intervenção manual: o time deixa de depender de alguém lembrar de desligar os servidores no fim do dia e ligá-los novamente pela manhã. Isso elimina esquecimentos que geram custo e libera as pessoas para tarefas de maior valor.

Por fim, vale lembrar que FinOps é um processo contínuo. Automatizar o desligamento das instâncias é um excelente passo para reduzir custos, mas também deve caminhar com outras práticas, como revisar volumes EBS e IPs públicos não utilizados, ajustar o tamanho das instâncias (rightsizing), avaliar savings plans para os recursos que precisam rodar o tempo todo, entre outros processos.

Top comments (1)

Collapse
 
suppdevbot profile image
DEV SUPPORTS •
You need to verify your account.
Enter fullscreen mode Exit fullscreen mode

tr.ee/dev-to