Existe uma correlação quase universal entre o crescimento de um sistema em produção e o crescimento de seus custos de infraestrutura. O que começa como algumas instâncias EC2 e um banco de dados RDS evolui, ao longo de meses, para dezenas de serviços, centenas de recursos e uma fatura AWS que surpreende até os mais experientes quando chega ao fim do mês.
A disciplina de FinOps — Financial Operations — trata a gestão de custos de cloud como uma responsabilidade de engenharia, não apenas financeira. O princípio central é que as pessoas que tomam decisões de arquitetura — engenheiros e arquitetos — devem ter visibilidade dos custos que essas decisões geram, e responsabilidade por otimizá-los. O FinOps não é sobre cortar custos a qualquer custo; é sobre maximizar o valor entregue por cada dólar gasto em infraestrutura.
Performance e custo estão intrinsecamente ligados: um sistema mal dimensionado paga pelo excesso de capacidade; um sistema sobrecarregado degrada a experiência do usuário e frequentemente gera custos de incidente superiores ao que seria economizado com dimensionamento correto. Otimizar os dois simultaneamente — entregando a performance que o negócio precisa pelo menor custo possível — é o objetivo desta disciplina.
Visibilidade de Custos com AWS Cost Explorer e Etiquetas
O primeiro passo de qualquer programa FinOps é visibilidade. Sem saber quanto cada parte do sistema custa, não é possível decidir onde otimizar.
Estratégia de Etiquetas para Alocação de Custos
# tags.tf — convenção de etiquetas para rastreamento de custos
locals {
tags_comuns = {
# Identificação do recurso
Project = var.project_name
Environment = var.environment
ManagedBy = "terraform"
# Alocação de custos
CostCenter = var.cost_center # ex: "eng-backend", "eng-plataforma"
Team = var.team # ex: "checkout", "catalog", "infra"
Owner = var.owner # email do responsável técnico
# Lifecycle
CreatedAt = timestamp()
ExpiresAt = var.expires_at # para recursos temporários
# Compliance
DataClass = var.data_class # "publico", "interno", "confidencial"
}
}
# Política SCP (Service Control Policy) que exige etiquetas obrigatórias
# Aplicada no nível de AWS Organization para todos os recursos
resource "aws_organizations_policy" "exigir_etiquetas" {
name = "ExigirEtiquetasObrigatorias"
type = "SERVICE_CONTROL_POLICY"
content = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "ExigirEtiquetaProject"
Effect = "Deny"
Action = [
"ec2:RunInstances",
"rds:CreateDBInstance",
"elasticache:CreateCacheCluster",
"eks:CreateCluster",
]
Resource = "*"
Condition = {
"Null" = {
"aws:RequestedRegion" = "false"
"aws:ResourceTag/Project" = "true"
}
}
}
]
})
}
Dashboard de Custos com AWS Cost Explorer
#!/bin/bash
# scripts/relatorio-custos.sh
# Gera relatório de custos por serviço e equipe para o mês atual
MES_INICIO=$(date +%Y-%m-01)
MES_FIM=$(date -d "$(date +%Y-%m-01) +1 month" +%Y-%m-%d 2>/dev/null || \
date -v+1m -v1d +%Y-%m-%d)
echo "=== Relatório de Custos AWS — $(date +%B/%Y) ==="
echo ""
# Custo total do mês
TOTAL=$(aws ce get-cost-and-usage \
--time-period Start=${MES_INICIO},End=${MES_FIM} \
--granularity MONTHLY \
--metrics BlendedCost \
--query 'ResultsByTime[0].Total.BlendedCost.Amount' \
--output text)
echo "💰 Custo total do mês: USD ${TOTAL}"
echo ""
# Custo por serviço AWS
echo "📊 Custo por serviço:"
aws ce get-cost-and-usage \
--time-period Start=${MES_INICIO},End=${MES_FIM} \
--granularity MONTHLY \
--metrics BlendedCost \
--group-by Type=DIMENSION,Key=SERVICE \
--query 'ResultsByTime[0].Groups[*].{
Servico: Keys[0],
Custo: Metrics.BlendedCost.Amount
}' \
--output json \
| jq 'sort_by(.Custo | tonumber) | reverse | .[:10][] |
" \(.Servico): USD \(.Custo | tonumber | . * 100 | round / 100)"' \
-r
echo ""
# Custo por equipe (via tag Team)
echo "👥 Custo por equipe:"
aws ce get-cost-and-usage \
--time-period Start=${MES_INICIO},End=${MES_FIM} \
--granularity MONTHLY \
--metrics BlendedCost \
--group-by Type=TAG,Key=Team \
--query 'ResultsByTime[0].Groups[*].{
Equipe: Keys[0],
Custo: Metrics.BlendedCost.Amount
}' \
--output json \
| jq '.[] | " \(.Equipe): USD \(.Custo | tonumber | . * 100 | round / 100)"' \
-r
echo ""
# Anomalias de custo detectadas
echo "⚠️ Anomalias detectadas:"
aws ce get-anomalies \
--date-interval StartDate=${MES_INICIO},EndDate=${MES_FIM} \
--query 'Anomalies[*].{
Servico: AnomalyScore.CurrentScore,
ImpactoUSD: Impact.TotalImpact,
Inicio: AnomalyStartDate
}' \
--output json \
| jq '.[] | select(.ImpactoUSD > 100) |
" Impacto USD \(.ImpactoUSD | round) desde \(.Inicio)"' \
-r
Alertas de Orçamento
# budget.tf
resource "aws_budgets_budget" "mensal" {
name = "${var.project_name}-${var.environment}-mensal"
budget_type = "COST"
limit_amount = var.budget_mensal_usd
limit_unit = "USD"
time_unit = "MONTHLY"
time_period_start = "2025-01-01_00:00"
# Alertas em 70%, 90% e 100% do orçamento
notification {
comparison_operator = "GREATER_THAN"
threshold = 70
threshold_type = "PERCENTAGE"
notification_type = "FORECASTED"
subscriber_email_addresses = [var.finops_email]
}
notification {
comparison_operator = "GREATER_THAN"
threshold = 90
threshold_type = "PERCENTAGE"
notification_type = "FORECASTED"
subscriber_email_addresses = [var.finops_email]
subscriber_sns_topic_arns = [aws_sns_topic.alertas_custo.arn]
}
notification {
comparison_operator = "GREATER_THAN"
threshold = 100
threshold_type = "PERCENTAGE"
notification_type = "ACTUAL"
subscriber_email_addresses = [var.finops_email]
subscriber_sns_topic_arns = [aws_sns_topic.alertas_custo.arn]
}
cost_filters = {
TagKeyValue = ["user:Environment$${var.environment}"]
}
}
# Detecção automática de anomalias de custo
resource "aws_ce_anomaly_monitor" "servicos" {
name = "${var.project_name}-monitor-anomalias"
monitor_type = "DIMENSIONAL"
monitor_dimension = "SERVICE"
}
resource "aws_ce_anomaly_subscription" "alertas" {
name = "${var.project_name}-alertas-anomalias"
frequency = "DAILY"
monitor_arn_list = [aws_ce_anomaly_monitor.servicos.arn]
subscriber {
type = "SNS"
address = aws_sns_topic.alertas_custo.arn
}
# Alerta apenas se o impacto for maior que USD 50
threshold_expression {
dimension {
key = "ANOMALY_TOTAL_IMPACT_ABSOLUTE"
values = ["50"]
match_options = ["GREATER_THAN_OR_EQUAL"]
}
}
}
Otimização de Compute: EC2, ECS e Lambda
Savings Plans e Reserved Instances
Para cargas de trabalho previsíveis — servidores de aplicação que rodam 24/7 — o uso de On-Demand é o tipo mais caro de compute. Duas alternativas oferecem descontos significativos:
Compute Savings Plans — compromisso de uso de uma quantidade de compute (em USD/hora) por 1 ou 3 anos, com desconto de até 66% sobre On-Demand. Aplicam-se automaticamente a EC2, Fargate e Lambda.
Reserved Instances — reserva de instâncias específicas com desconto de até 72% para comprometimento de 3 anos com pagamento antecipado.
# Identifica instâncias candidatas a Savings Plans
# Instâncias que rodam > 80% do tempo no último mês são boas candidatas
aws ce get-reservation-coverage \
--time-period Start=$(date -d "30 days ago" +%Y-%m-%d),End=$(date +%Y-%m-%d) \
--granularity MONTHLY \
--group-by Type=DIMENSION,Key=INSTANCE_TYPE \
--query 'CoveragesByTime[0].Groups[*].{
Tipo: Keys[0],
Cobertura: Coverage.CoverageHours.CoverageHoursPercentage,
OnDemandCusto: Coverage.CoverageHours.OnDemandHours
}' \
--output table
# Recomendações de Savings Plans da AWS
aws ce get-savings-plans-purchase-recommendation \
--savings-plans-type COMPUTE_SP \
--term-in-years ONE_YEAR \
--payment-option NO_UPFRONT \
--lookback-period-in-days THIRTY_DAYS \
--query 'SavingsPlansPurchaseRecommendation.{
PoupancaMensalEstimada: SavingsPlansDetails[0].EstimatedMonthlySavingsAmount,
CompromissoHorario: SavingsPlansDetails[0].HourlyCommitmentToPurchase
}'
Instâncias Spot para Cargas de Trabalho Tolerantes a Interrupção
Instâncias Spot oferecem desconto de até 90% sobre On-Demand. São adequadas para cargas que toleram interrupção — jobs de CI/CD, processamento em batch, workers de fila:
# spot-workers.tf
# Auto Scaling Group com mix de On-Demand e Spot
resource "aws_autoscaling_group" "workers" {
name = "${var.project_name}-${var.environment}-workers"
vpc_zone_identifier = module.vpc.ids_subnets_privadas
min_size = 1
max_size = 20
desired_capacity = 2
mixed_instances_policy {
instances_distribution {
# 20% On-Demand para baseline, 80% Spot para escala
on_demand_base_capacity = 1
on_demand_percentage_above_base_capacity = 20
spot_allocation_strategy = "price-capacity-optimized"
}
launch_template {
launch_template_specification {
launch_template_id = aws_launch_template.worker.id
version = "$Latest"
}
# Múltiplos tipos de instância aumentam disponibilidade de Spot
override {
instance_type = "m6i.large"
}
override {
instance_type = "m6a.large"
}
override {
instance_type = "m5.large"
}
override {
instance_type = "m5a.large"
}
override {
instance_type = "m7i.large"
}
}
}
tag {
key = "Name"
value = "${var.project_name}-${var.environment}-worker"
propagate_at_launch = true
}
}
# Handler de interrupção Spot — drena o worker antes da instância ser terminada
# A AWS notifica 2 minutos antes de interromper uma instância Spot
resource "aws_cloudwatch_event_rule" "spot_interruption" {
name = "spot-interruption-${var.environment}"
description = "Detecta notificações de interrupção de instâncias Spot"
event_pattern = jsonencode({
source = ["aws.ec2"]
detail-type = ["EC2 Spot Instance Interruption Warning"]
})
}
resource "aws_cloudwatch_event_target" "spot_interruption_lambda" {
rule = aws_cloudwatch_event_rule.spot_interruption.name
target_id = "DrenaWorker"
arn = aws_lambda_function.drena_worker.arn
}
Rightsizing: Dimensionamento Correto de Instâncias
Um dos maiores desperdícios em cloud é o over-provisioning — instâncias dimensionadas para picos que raramente ocorrem. O AWS Compute Optimizer analisa métricas históricas e recomenda o tipo de instância ideal:
#!/bin/bash
# scripts/rightsizing-report.sh
# Gera relatório de recomendações de rightsizing
echo "=== Recomendações de Rightsizing — AWS Compute Optimizer ==="
echo ""
# Recomendações para instâncias EC2
echo "📦 Instâncias EC2 com recomendação de downsizing:"
aws compute-optimizer get-ec2-instance-recommendations \
--filters Name=Finding,Values=Overprovisioned \
--query 'instanceRecommendations[*].{
Instancia: instanceName,
AtualTipo: currentInstanceType,
RecomendadoTipo: recommendationOptions[0].instanceType,
PoupancaMensal: recommendationOptions[0].estimatedMonthlySavings.value,
Moeda: recommendationOptions[0].estimatedMonthlySavings.currency,
UsoMaxCPU: utilizationMetrics[?name==`CPU`].value | [0]
}' \
--output json \
| jq '.[] | "\(.Instancia): \(.AtualTipo) → \(.RecomendadoTipo) | CPU max: \(.UsoMaxCPU)% | Economia: \(.Moeda) \(.PoupancaMensal)/mês"' \
-r
echo ""
# Recomendações para funções Lambda
echo "λ Funções Lambda sub ou sobredimensionadas:"
aws compute-optimizer get-lambda-function-recommendations \
--filters Name=Finding,Values=Overprovisioned \
--query 'lambdaFunctionRecommendations[*].{
Funcao: functionArn,
MemoriaAtual: memorySizeRecommendationOptions[0].memorySize,
MemoriaRecomendada: memorySizeRecommendationOptions[0].memorySize,
Poupanca: memorySizeRecommendationOptions[0].projectedUtilizationMetrics[0].value
}' \
--output json \
| jq '.[] | "\(.Funcao | split(":") | .[-1]): → \(.MemoriaRecomendada)MB"' \
-r
Otimização de Banco de Dados
Identificando Queries Lentas com Performance Insights
#!/usr/bin/env python3
# scripts/analise-queries-lentas.py
# Identifica as queries mais custosas no RDS via Performance Insights
import boto3
import json
from datetime import datetime, timedelta, timezone
pi = boto3.client('pi', region_name='us-east-1')
rds = boto3.client('rds', region_name='us-east-1')
INSTANCIA = 'minha-api-production-db'
PERIODO_HORAS = 24
# Obtém o ResourceIdentifier da instância RDS
response = rds.describe_db_instances(
DBInstanceIdentifier=INSTANCIA
)
resource_id = response['DBInstances'][0]['DbiResourceId']
fim = datetime.now(timezone.utc)
inicio = fim - timedelta(hours=PERIODO_HORAS)
# Busca as top 10 queries por tempo total de execução
response = pi.get_resource_metrics(
ServiceType='RDS',
Identifier=resource_id,
StartTime=inicio,
EndTime=fim,
PeriodInSeconds=3600,
MetricQueries=[
{
'Metric': 'db.load.avg',
'GroupBy': {
'Group': 'db.sql',
'Dimensions': ['db.sql.statement'],
'Limit': 10,
}
}
]
)
print(f"=== Top 10 Queries por Carga — Últimas {PERIODO_HORAS}h ===\n")
for i, key in enumerate(
response['MetricList'][0].get('Keys', []), 1
):
statement = key['Dimensions'].get('db.sql.statement', 'N/A')
# Trunca queries longas para exibição
if len(statement) > 120:
statement = statement[:120] + '...'
# Calcula a carga média
valores = [
p['Value'] for p in key.get('DataPoints', [])
if 'Value' in p
]
carga_media = sum(valores) / len(valores) if valores else 0
print(f"{i}. Carga média: {carga_media:.2f} AAS")
print(f" Query: {statement}")
print()
print("\n💡 Queries com carga > 1.0 AAS merecem análise de índices.")
Otimizando Leituras com Connection Pooling
// src/database/pool.js — configuração otimizada do pool de conexões
const { Pool } = require('pg');
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: { rejectUnauthorized: true },
// Dimensionamento do pool baseado na instância RDS
// Regra geral: min(max_connections / num_pods, 10)
max: parseInt(process.env.DB_POOL_MAX || '10'),
min: parseInt(process.env.DB_POOL_MIN || '2'),
// Tempo de idle antes de fechar conexão extra
idleTimeoutMillis: 30000,
// Timeout para obter conexão do pool
connectionTimeoutMillis: 5000,
// Timeout para queries individuais
statement_timeout: 30000, // 30 segundos
query_timeout: 30000,
// Keepalive para conexões de longa duração
keepAlive: true,
keepAliveInitialDelayMillis: 10000,
});
// Métricas do pool expostas para o Prometheus
const { Gauge } = require('prom-client');
const poolTotal = new Gauge({
name: 'db_pool_total_connections',
help: 'Total de conexões no pool',
});
const poolIdle = new Gauge({
name: 'db_pool_idle_connections',
help: 'Conexões ociosas no pool',
});
const poolWaiting = new Gauge({
name: 'db_pool_waiting_clients',
help: 'Clientes aguardando conexão disponível',
});
setInterval(() => {
poolTotal.set(pool.totalCount);
poolIdle.set(pool.idleCount);
poolWaiting.set(pool.waitingCount);
}, 5000);
pool.on('error', (err) => {
console.error(JSON.stringify({
level: 'error',
msg: 'Erro inesperado no pool de conexões',
err: err.message,
}));
});
module.exports = pool;
Otimização de Storage e Transferência de Dados
S3 Intelligent-Tiering e Lifecycle Policies
O storage é frequentemente o item de custo mais negligenciado — buckets crescem indefinidamente sem políticas de ciclo de vida:
# s3-lifecycle.tf
resource "aws_s3_bucket_lifecycle_configuration" "dados" {
bucket = aws_s3_bucket.dados.id
# Logs de aplicação — transitam para Glacier e são deletados
rule {
id = "lifecycle-logs-aplicacao"
status = "Enabled"
filter {
prefix = "logs/"
}
transition {
days = 30
storage_class = "STANDARD_IA" # Acesso infrequente após 30 dias
}
transition {
days = 90
storage_class = "GLACIER_IR" # Glacier Instant Retrieval após 90 dias
}
expiration {
days = 365 # Deleta após 1 ano
}
noncurrent_version_expiration {
noncurrent_days = 30 # Versões antigas deletadas em 30 dias
}
}
# Backups — retidos mais tempo, transitam para Glacier Deep Archive
rule {
id = "lifecycle-backups"
status = "Enabled"
filter {
prefix = "backups/"
}
transition {
days = 7
storage_class = "STANDARD_IA"
}
transition {
days = 30
storage_class = "GLACIER"
}
transition {
days = 90
storage_class = "DEEP_ARCHIVE" # USD 0.00099/GB — mais barato possível
}
expiration {
days = 2555 # 7 anos para compliance
}
}
# Assets de frontend — Intelligent-Tiering gerencia automaticamente
rule {
id = "lifecycle-assets-frontend"
status = "Enabled"
filter {
prefix = "frontend/"
}
transition {
days = 0
storage_class = "INTELLIGENT_TIERING"
}
}
}
# Configuração do Intelligent-Tiering para assets
resource "aws_s3_bucket_intelligent_tiering_configuration" "assets" {
bucket = aws_s3_bucket.dados.id
name = "assets-inteligente"
filter {
prefix = "frontend/"
}
tiering {
access_tier = "DEEP_ARCHIVE_ACCESS"
days = 180
}
tiering {
access_tier = "ARCHIVE_ACCESS"
days = 90
}
}
Reduzindo Custos de Transferência de Dados
Transferência de dados entre regiões AWS e para a internet é uma das fontes de custo mais surpreendentes. Estratégias para reduzir:
# vpc-endpoints.tf — acessa serviços AWS sem tráfego de internet
# Gateway Endpoint para S3 — gratuito, elimina tráfego para internet
resource "aws_vpc_endpoint" "s3" {
vpc_id = module.vpc.vpc_id
service_name = "com.amazonaws.${var.aws_region}.s3"
vpc_endpoint_type = "Gateway"
route_table_ids = concat(
module.vpc.ids_route_tables_privadas,
module.vpc.ids_route_tables_publicas
)
tags = merge(local.tags_comuns, {
Name = "${var.project_name}-${var.environment}-s3-endpoint"
})
}
# Interface Endpoint para Secrets Manager
# Evita tráfego de internet — cobra por hora + GB processado
resource "aws_vpc_endpoint" "secrets_manager" {
vpc_id = module.vpc.vpc_id
service_name = "com.amazonaws.${var.aws_region}.secretsmanager"
vpc_endpoint_type = "Interface"
private_dns_enabled = true
subnet_ids = module.vpc.ids_subnets_privadas
security_group_ids = [aws_security_group.vpc_endpoints.id]
tags = merge(local.tags_comuns, {
Name = "${var.project_name}-${var.environment}-secretsmanager-endpoint"
})
}
# Interface Endpoint para ECR — acelera pull de imagens em EKS/ECS
resource "aws_vpc_endpoint" "ecr_api" {
vpc_id = module.vpc.vpc_id
service_name = "com.amazonaws.${var.aws_region}.ecr.api"
vpc_endpoint_type = "Interface"
private_dns_enabled = true
subnet_ids = module.vpc.ids_subnets_privadas
security_group_ids = [aws_security_group.vpc_endpoints.id]
tags = local.tags_comuns
}
resource "aws_vpc_endpoint" "ecr_dkr" {
vpc_id = module.vpc.vpc_id
service_name = "com.amazonaws.${var.aws_region}.ecr.dkr"
vpc_endpoint_type = "Interface"
private_dns_enabled = true
subnet_ids = module.vpc.ids_subnets_privadas
security_group_ids = [aws_security_group.vpc_endpoints.id]
tags = local.tags_comuns
}
Dashboard FinOps no Grafana
Um dashboard dedicado a custos conecta métricas técnicas ao impacto financeiro:
{
"dashboard": {
"title": "FinOps — Custos e Eficiência",
"panels": [
{
"title": "Custo por Requisição Atendida",
"type": "stat",
"description": "USD gasto por cada 1000 requisições processadas — métrica de eficiência de custo",
"targets": [
{
"expr": "(aws_billing_estimated_charges / on()(sum(increase(http_requests_total[24h])) / 1000))",
"legendFormat": "USD / 1k req"
}
]
},
{
"title": "Custo Estimado do Dia (Projeção Mensal)",
"type": "timeseries",
"targets": [
{
"expr": "aws_billing_estimated_charges",
"legendFormat": "Custo acumulado USD"
}
]
},
{
"title": "Utilização de CPU dos Nós EKS",
"description": "Baixa utilização indica oportunidade de rightsizing ou consolidação",
"type": "gauge",
"targets": [
{
"expr": "avg(100 - (avg by(node) (irate(node_cpu_seconds_total{mode='idle'}[5m])) * 100))",
"legendFormat": "CPU média dos nós"
}
],
"thresholds": {
"steps": [
{"color": "red", "value": 0},
{"color": "yellow", "value": 40},
{"color": "green", "value": 60}
]
}
},
{
"title": "Memória Não Utilizada nos Nós",
"type": "timeseries",
"targets": [
{
"expr": "sum(node_memory_MemAvailable_bytes) / sum(node_memory_MemTotal_bytes) * 100",
"legendFormat": "% memória livre nos nós"
}
]
}
]
}
}
Custo do Pipeline de CI/CD
Os próprios pipelines de CI/CD têm um custo que cresce com a frequência de commits e o tamanho do time. Otimizações que reduzem o tempo de pipeline reduzem diretamente o custo de GitHub Actions ou do runner:
# Estratégias para pipelines mais eficientes
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Cache de node_modules — evita npm install em cada run
- uses: actions/cache@v4
id: cache-deps
with:
path: |
~/.npm
node_modules
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Instala dependências (apenas se cache miss)
if: steps.cache-deps.outputs.cache-hit != 'true'
run: npm ci
# Cache de camadas Docker — evita rebuild de camadas inalteradas
- uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v5
with:
context: .
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
tags: ghcr.io/empresa/api:${{ github.sha }}
push: true
# Rotaciona o cache — evita crescimento indefinido
- name: Rotaciona cache do Buildx
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
Relatório Semanal de FinOps Automatizado
# .github/workflows/relatorio-finops-semanal.yml
name: Relatório Semanal de FinOps
on:
schedule:
- cron: '0 8 * * MON' # Segunda-feira às 8h
workflow_dispatch:
jobs:
relatorio:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_FINOPS_ROLE_ARN }}
aws-region: us-east-1
- name: Gera relatório de custos
run: bash scripts/relatorio-custos.sh > relatorio.txt
- name: Gera recomendações de rightsizing
run: python3 scripts/analise-queries-lentas.py >> relatorio.txt
- name: Publica no Slack
uses: slackapi/slack-github-action@v1.26.0
with:
channel-id: ${{ vars.SLACK_FINOPS_CHANNEL }}
payload: |
{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "📊 Relatório Semanal de FinOps"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "```${{ steps.relatorio.outputs.summary }}```"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {"type": "plain_text", "text": "Ver no AWS Cost Explorer"},
"url": "https://console.aws.amazon.com/cost-management/home"
}
]
}
]
}
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
O Que Vem a Seguir
O próximo artigo cobre Resiliência e Chaos Engineering — como construir sistemas que falham graciosamente, como usar o AWS Fault Injection Simulator para testar a resiliência em produção e como implementar os padrões de resiliência fundamentais: circuit breaker, retry com backoff exponencial e bulkhead.
Referências para Aprofundamento
FinOps e otimização de custos - FinOps Foundation — finops.org — Definição oficial do FinOps, incluindo o modelo de maturidade e as práticas recomendadas para times de engenharia e finanças. - AWS Cost Optimization Hub — docs.aws.amazon.com — Centro de otimização de custos da AWS com recomendações consolidadas de Savings Plans, rightsizing e recursos ociosos. - AWS Compute Optimizer — docs.aws.amazon.com — Documentação do Compute Optimizer com guia de interpretação das recomendações e integração com AWS Organizations.
Performance - AWS Performance Efficiency Pillar — docs.aws.amazon.com — Pilar de Eficiência de Performance do AWS Well-Architected Framework, cobrindo seleção de recursos, monitoramento e trade-offs de arquitetura. - Use the Right Tool — AWS Prescriptive Guidance — docs.aws.amazon.com — Guia prescritivo da AWS para otimização de performance em aplicações cloud-native