Prometheus para AWS: Guia Completo de Deploy
Introdução
Monitorar aplicações em produção não é opcional. É a diferença entre descobrir um problema pelo alerta do seu dashboard ou pelo cliente reclamando no Twitter. O Prometheus se consolidou como a ferramenta padrão de monitoramento no ecossistema cloud native, e quando combinado com a infraestrutura da AWS, forma uma stack de observabilidade robusta e escalável.
Neste guia, vamos configurar o Prometheus na AWS do zero, passando pelo deploy, pela integração com serviços gerenciados e pela instrumentação de aplicações em TypeScript. O objetivo é que, ao final da leitura, você tenha um caminho claro para implementar monitoramento de verdade nos seus projetos.
Se você já acompanha o blog, este post complementa o guia DevOps para Devs: Docker, CI/CD, Kubernetes e AWS em Produção, onde cobrimos a base de infraestrutura que vamos usar aqui.
Por que usar Prometheus com AWS
O problema que estamos resolvendo
A AWS oferece o CloudWatch como solução nativa de monitoramento. Ele funciona bem para métricas de infraestrutura básica (CPU, memória, rede), mas apresenta limitações sérias quando você precisa de métricas customizadas de aplicação, alertas complexos baseados em queries PromQL e dashboards granulares.
O Prometheus resolve exatamente essas lacunas. Ele permite que você defina métricas de negócio (quantas transações por segundo, latência do endpoint de checkout, taxa de erro por tenant), e consulte esses dados com uma linguagem de query poderosa.
Quando essa combinação brilha
Existem cenários onde Prometheus na AWS é a escolha ideal:
- Microserviços em ECS ou EKS: quando você tem dezenas de serviços e precisa de métricas padronizadas com service discovery automático.
- Aplicações multi-tenant em SaaS: onde métricas por cliente são essenciais para SLAs e billing.
- Workloads com auto scaling: o Prometheus descobre automaticamente novas instâncias via EC2 service discovery ou Kubernetes SD.
- Equipes que já usam Grafana: a integração entre Prometheus e Grafana é nativa e madura.
Amazon Managed Service for Prometheus (AMP)
A AWS oferece o Amazon Managed Service for Prometheus, que elimina a necessidade de gerenciar o servidor Prometheus manualmente. Ele é compatível com PromQL, aceita remote write do Prometheus padrão e escala automaticamente. Para equipes que não querem manter infraestrutura de monitoramento, o AMP é a escolha mais pragmática.
Setup do Prometheus na AWS
Opção 1: Prometheus self-hosted no ECS
Vamos começar com o deploy do Prometheus como container no ECS Fargate. Primeiro, crie o arquivo de configuração do Prometheus:
# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: "api-service"
ec2_sd_configs:
- region: sa-east-1
port: 3000
filters:
- name: "tag:Environment"
values: ["production"]
- name: "tag:Monitoring"
values: ["enabled"]
relabel_configs:
- source_labels: [__meta_ec2_tag_Name]
target_label: instance_name
- source_labels: [__meta_ec2_instance_id]
target_label: instance_id
- source_labels: [__meta_ec2_availability_zone]
target_label: availability_zone
- job_name: "ecs-services"
dns_sd_configs:
- names:
- "api.production.local"
- "worker.production.local"
type: SRV
port: 9090Para subir o Prometheus no ECS, use a AWS CLI:
# Criar o repositório ECR para a imagem customizada do Prometheus
aws ecr create-repository \
--repository-name monitoring/prometheus \
--region sa-east-1
# Build e push da imagem
docker build -t prometheus-custom .
docker tag prometheus-custom:latest \
123456789012.dkr.ecr.sa-east-1.amazonaws.com/monitoring/prometheus:latest
aws ecr get-login-password --region sa-east-1 | \
docker login --username AWS --password-stdin \
123456789012.dkr.ecr.sa-east-1.amazonaws.com
docker push \
123456789012.dkr.ecr.sa-east-1.amazonaws.com/monitoring/prometheus:latest
# Registrar a task definition
aws ecs register-task-definition \
--cli-input-json file://prometheus-task-definition.json
# Criar o serviço
aws ecs create-service \
--cluster monitoring \
--service-name prometheus \
--task-definition prometheus:1 \
--desired-count 1 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-abc123],securityGroups=[sg-abc123],assignPublicIp=DISABLED}"Opção 2: Amazon Managed Prometheus (AMP)
Para quem prefere o serviço gerenciado, a configuração é mais direta:
# Criar o workspace do AMP
aws amp create-workspace \
--alias production-metrics \
--region sa-east-1
# O comando retorna um workspaceId, que vamos usar para configurar o remote write
# Exemplo de saída: ws-abc123def456
# Configurar o remote write no prometheus.ymlAdicione a seção de remote write ao seu prometheus.yml:
remote_write:
- url: https://aps-workspaces.sa-east-1.amazonaws.com/workspaces/ws-abc123def456/api/v1/remote_write
queue_config:
max_samples_per_send: 1000
max_shards: 200
capacity: 2500
sigv4:
region: sa-east-1A autenticação é feita via IAM usando SigV4, então a role do seu ECS task precisa da policy AmazonPrometheusRemoteWriteAccess.
Exemplo Prático: Instrumentando uma API TypeScript
Exportando métricas customizadas com prom-client
Vamos instrumentar uma API Express real com métricas que importam em produção. Este primeiro exemplo mostra a configuração completa do módulo de métricas:
// src/metrics/prometheus.ts
import express, { Request, Response, NextFunction } from "express";
import client, {
Registry,
Counter,
Histogram,
Gauge,
Summary,
} from "prom-client";
const register = new Registry();
client.collectDefaultMetrics({
register,
prefix: "api_",
labels: {
service: "checkout-api",
environment: process.env.NODE_ENV || "development",
},
});
export const httpRequestsTotal = new Counter({
name: "http_requests_total",
help: "Total de requisicoes HTTP recebidas",
labelNames: ["method", "route", "status_code"] as const,
registers: [register],
});
export const httpRequestDuration = new Histogram({
name: "http_request_duration_seconds",
help: "Duracao das requisicoes HTTP em segundos",
labelNames: ["method", "route", "status_code"] as const,
buckets: [0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10],
registers: [register],
});
export const activeConnections = new Gauge({
name: "active_connections",
help: "Numero de conexoes ativas no momento",
registers: [register],
});
export const checkoutProcessingTime = new Summary({
name: "checkout_processing_duration_seconds",
help: "Tempo de processamento do checkout",
labelNames: ["payment_method", "tenant_id"] as const,
percentiles: [0.5, 0.9, 0.95, 0.99],
registers: [register],
});
export function metricsMiddleware(
req: Request,
res: Response,
next: NextFunction
): void {
const start = process.hrtime.bigint();
activeConnections.inc();
res.on("finish", () => {
const durationNs = Number(process.hrtime.bigint() - start);
const durationSeconds = durationNs / 1e9;
const route = req.route?.path || req.path || "unknown";
const labels = {
method: req.method,
route: route,
status_code: String(res.statusCode),
};
httpRequestsTotal.inc(labels);
httpRequestDuration.observe(labels, durationSeconds);
activeConnections.dec();
});
next();
}
export async function metricsHandler(
_req: Request,
res: Response
): Promise<void> {
res.set("Content-Type", register.contentType);
const metrics = await register.metrics();
res.end(metrics);
}
export { register };Usando as métricas em rotas de negócio
O segundo exemplo mostra como instrumentar lógica de negócio real, integrando as métricas com o fluxo de checkout:
// src/routes/checkout.ts
import { Router, Request, Response } from "express";
import {
checkoutProcessingTime,
metricsMiddleware,
metricsHandler,
} from "../metrics/prometheus";
interface CheckoutPayload {
cartId: string;
paymentMethod: "credit_card" | "pix" | "boleto";
tenantId: string;
}
interface CheckoutResult {
orderId: string;
status: "confirmed" | "pending" | "failed";
processedAt: string;
}
const router = Router();
router.use(metricsMiddleware);
router.get("/metrics", metricsHandler);
async function processPayment(
payload: CheckoutPayload
): Promise<CheckoutResult> {
const delay = Math.random() * 2000 + 500;
await new Promise((resolve) => setTimeout(resolve, delay));
return {
orderId: `ORD-${Date.now()}`,
status: "confirmed",
processedAt: new Date().toISOString(),
};
}
router.post("/checkout", async (req: Request, res: Response): Promise<void> => {
const payload: CheckoutPayload = req.body;
const startTime = performance.now();
try {
const result = await processPayment(payload);
const durationSeconds = (performance.now() - startTime) / 1000;
checkoutProcessingTime.observe(
{
payment_method: payload.paymentMethod,
tenant_id: payload.tenantId,
},
durationSeconds
);
res.status(200).json({
success: true,
data: result,
});
} catch (error: unknown) {
const durationSeconds = (performance.now() - startTime) / 1000;
checkoutProcessingTime.observe(
{
payment_method: payload.paymentMethod,
tenant_id: payload.tenantId,
},
durationSeconds
);
const message =
error instanceof Error ? error.message : "Erro desconhecido";
res.status(500).json({
success: false,
error: message,
});
}
});
router.get("/health", (_req: Request, res: Response): void => {
res.status(200).json({
status: "healthy",
uptime: process.uptime(),
timestamp: new Date().toISOString(),
});
});
export default router;Com essa instrumentação, você consegue responder perguntas como: "Qual a latência p99 do checkout via Pix no tenant X?" usando PromQL:
histogram_quantile(0.99,
rate(checkout_processing_duration_seconds{payment_method="pix", tenant_id="tenant-42"}[5m])
)Boas Práticas de Monitoramento com Prometheus na AWS
Nomenclatura e cardinalidade
Siga a convenção de nomes do Prometheus: snake_case, com sufixos que indicam a unidade (_seconds, _bytes, _total). Cuidado absoluto com a cardinalidade das labels. Nunca use valores como user ID ou request ID como label. Isso vai explodir sua série temporal e derrubar o Prometheus.
Regra prática: se uma label pode ter mais de 100 valores distintos, repense se ela deve existir.
Retenção e armazenamento de longo prazo
O Prometheus local mantém dados por 15 dias, que é suficiente para troubleshooting imediato. Para retenção de longo prazo na AWS, configure o remote write para o Amazon Managed Prometheus ou para um bucket S3 via Thanos. A combinação AMP com Grafana Managed (via Amazon Managed Grafana) elimina toda a operação de manter servidores de monitoramento.
Segurança e rede
Nunca exponha o endpoint /metrics para a internet. Use security groups para permitir acesso apenas do Prometheus ao port de métricas dos seus serviços. Em EKS, utilize network policies. Para o AMP, a autenticação via IAM com SigV4 já fornece controle granular.
Alertas que funcionam
Configure alertas baseados em sintomas, não em causas. Em vez de alertar quando a CPU passa de 80%, alerte quando a latência p95 do seu endpoint crítico ultrapassa o SLA:
# alerting-rules.yml
groups:
- name: sla_alerts
rules:
- alert: HighCheckoutLatency
expr: |
histogram_quantile(0.95,
rate(http_request_duration_seconds_bucket{route="/checkout"}[5m])
) > 2.0
for: 5m
labels:
severity: critical
annotations:
summary: "Latencia p95 do checkout acima de 2 segundos"
description: "O endpoint /checkout esta com latencia p95 de {{ $value }}s nos ultimos 5 minutos."Custo
O Amazon Managed Prometheus cobra por amostras ingeridas e armazenadas. Monitore o volume de métricas para evitar surpresas na fatura. Use metric_relabel_configs para descartar métricas desnecessárias antes da ingestão.
Conclusão
Prometheus na AWS não é apenas sobre instalar uma ferramenta de monitoramento. É sobre construir uma cultura de observabilidade onde cada serviço expõe métricas relevantes, os alertas são acionáveis e os dashboards contam a história da saúde do sistema.
O caminho mais pragmático para a maioria das equipes é começar com o Amazon Managed Prometheus para eliminar a operação do servidor, instrumentar as aplicações com prom-client, e usar o Grafana para visualização. Conforme a maturidade cresce, você adiciona alertas baseados em SLOs e expande a cobertura para métricas de negócio.
Comece simples: instrumente um serviço, crie um dashboard, configure um alerta. Depois expanda. Monitoramento é um processo iterativo, e a pior métrica é aquela que não existe.
Guias Relacionados
Explore mais guias técnicos
Mais de 100 guias práticos de integração entre frameworks, ferramentas e plataformas.
Ver todos os guias