Azure Managed Grafana + Prometheus no AKS: alertas, multi-cluster e troubleshooting - Parte 2
Na Parte 1, provisionamos toda a infraestrutura de observabilidade — Azure Monitor workspace, Managed Grafana e Prometheus no AKS — além de configurar scrape de métricas customizadas e construir dashboards avançados com PromQL. Agora, vamos avançar para Recording Rules, Alerting Rules, monitoramento multi-cluster, dashboards como código, instrumentação de aplicações e troubleshooting dos problemas mais comuns em produção.
Recording Rules e Alerting Rules
Recording rules pré-computam queries complexas para performance, e alerting rules disparam notificações quando condições são atendidas. Ambas usam PromQL.
Recording Rules
Recording rules são executadas periodicamente e salvam o resultado como uma nova série temporal. Útil para queries caras que seriam lentas em dashboards.
# prometheus-rules.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: ama-metrics-prometheus-config
namespace: kube-system
data:
recording-rules: |
groups:
- name: kubernetes_resource_usage
interval: 60s
rules:
- record: namespace:container_cpu_usage:sum_rate5m
expr: |
sum by (namespace) (
rate(container_cpu_usage_seconds_total{container!="", container!="POD"}[5m])
)
- record: namespace:container_memory_working_set:sum
expr: |
sum by (namespace) (
container_memory_working_set_bytes{container!="", container!="POD"}
)
- record: node:cpu_utilization:ratio
expr: |
1 - avg by (instance) (
rate(node_cpu_seconds_total{mode="idle"}[5m])
)
- name: api_slis
interval: 30s
rules:
- record: api:http_request_error_rate:ratio_rate5m
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))
- record: api:http_request_latency_p99:seconds
expr: |
histogram_quantile(0.99,
sum by (le) (rate(http_request_duration_seconds_bucket[5m]))
)
Alerting Rules com Azure Managed Prometheus
Alerting rules no Azure Managed Prometheus são configuradas como Prometheus Rule Groups via Azure Resource Manager, não via ConfigMap. Isso é diferente do Prometheus self-hosted e pega muita gente de surpresa.
# Criar regra de alerta via CLI
az monitor account rule-group create \
--name "kubernetes-critical-alerts" \
--resource-group $RESOURCE_GROUP \
--location $LOCATION \
--cluster-name $AKS_CLUSTER \
--scope $MONITOR_WORKSPACE_ID \
--interval PT1M \
--rules '[
{
"alert": "PodCrashLooping",
"expression": "increase(kube_pod_container_status_restarts_total[1h]) > 5",
"for": "PT10M",
"severity": 2,
"labels": {
"team": "sre",
"severity": "critical"
},
"annotations": {
"summary": "Pod {{ $labels.namespace }}/{{ $labels.pod }} está em crash loop",
"description": "O pod {{ $labels.pod }} no namespace {{ $labels.namespace }} reiniciou mais de 5 vezes na última hora."
}
},
{
"alert": "NodeMemoryPressure",
"expression": "100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) > 90",
"for": "PT5M",
"severity": 1,
"labels": {
"team": "infra",
"severity": "critical"
},
"annotations": {
"summary": "Node {{ $labels.instance }} com memória acima de 90%",
"description": "O node {{ $labels.instance }} está usando {{ $value }}% de memória há mais de 5 minutos."
}
},
{
"alert": "HighErrorRate",
"expression": "api:http_request_error_rate:ratio_rate5m > 0.05",
"for": "PT5M",
"severity": 2,
"labels": {
"team": "backend",
"severity": "warning"
},
"annotations": {
"summary": "Taxa de erros HTTP acima de 5%",
"description": "A taxa de erros HTTP 5xx está em {{ $value | humanizePercentage }} nos últimos 5 minutos."
}
}
]'
Integrando alertas Prometheus com Action Groups
Para que os alertas disparem notificações reais (email, Teams, PagerDuty, webhook), você precisa conectar os Prometheus alerts com Azure Monitor Action Groups:
# Criar Action Group
az monitor action-group create \
--name "ag-sre-oncall" \
--resource-group $RESOURCE_GROUP \
--short-name "SRE" \
--email-receiver name="sre-lead" email-address="sre@empresa.com" \
--webhook-receiver name="pagerduty" uri="https://events.pagerduty.com/integration/XXXX/enqueue"
# Criar Alert Processing Rule que conecta alertas Prometheus ao Action Group
az monitor alert-processing-rule create \
--name "route-prometheus-alerts" \
--resource-group $RESOURCE_GROUP \
--rule-type AddActionGroups \
--scopes $MONITOR_WORKSPACE_ID \
--action-groups "/subscriptions/{sub-id}/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.Insights/actionGroups/ag-sre-oncall"
Cenário avançado: Monitoramento multi-cluster
Um dos maiores benefícios do Azure Managed Prometheus sobre o self-hosted é o monitoramento multi-cluster nativo. Com Prometheus self-hosted, você precisaria de Thanos ou Cortex para agregar métricas de vários clusters. Com Azure Managed Prometheus, basta apontar todos os clusters para o mesmo workspace.
Arquitetura multi-cluster
Configurando um segundo cluster
# Segundo cluster — mesmo workspace, mesmo Grafana
az aks update \
--name "aks-staging-brazil" \
--resource-group "rg-staging" \
--enable-azure-monitor-metrics \
--azure-monitor-workspace-resource-id $MONITOR_WORKSPACE_ID \
--grafana-resource-id $GRAFANA_ID
Queries multi-cluster em PromQL
Cada cluster adiciona um label cluster às métricas. Isso permite queries que comparam ou agregam entre clusters:
Uso de CPU por cluster:
avg by (cluster) (
1 - rate(node_cpu_seconds_total{mode="idle"}[5m])
) * 100
Pods com problema em todos os clusters:
sum by (cluster, namespace) (
kube_pod_status_phase{phase=~"Failed|Pending|Unknown"}
)
Comparação de latência entre ambientes:
histogram_quantile(0.95,
sum by (le, cluster) (
rate(http_request_duration_seconds_bucket{app="api-pedidos"}[5m])
)
)
Dashboards como código com Terraform
Para times que praticam GitOps, gerenciar dashboards manualmente pelo browser é inaceitável. Dashboards devem viver no repositório, versionados e deployados via pipeline.
Provisionamento com Terraform
# main.tf
resource "azurerm_monitor_workspace" "prometheus" {
name = "amw-prometheus-prod"
resource_group_name = azurerm_resource_group.observability.name
location = azurerm_resource_group.observability.location
}
resource "azurerm_dashboard_grafana" "grafana" {
name = "grafana-prod"
resource_group_name = azurerm_resource_group.observability.name
location = azurerm_resource_group.observability.location
sku = "Standard"
zone_redundancy_enabled = true
azure_monitor_workspace_integrations {
resource_id = azurerm_monitor_workspace.prometheus.id
}
identity {
type = "SystemAssigned"
}
}
# Role assignment para o Grafana ler métricas do workspace
resource "azurerm_role_assignment" "grafana_monitoring_reader" {
scope = azurerm_monitor_workspace.prometheus.id
role_definition_name = "Monitoring Reader"
principal_id = azurerm_dashboard_grafana.grafana.identity[0].principal_id
}
# Role assignment para SREs acessarem o Grafana
resource "azurerm_role_assignment" "sre_grafana_editor" {
scope = azurerm_dashboard_grafana.grafana.id
role_definition_name = "Grafana Editor"
principal_id = data.azuread_group.sre_team.object_id
}
# Habilitar Prometheus metrics no AKS
resource "azurerm_monitor_data_collection_rule" "prometheus" {
name = "dcr-prometheus-aks"
resource_group_name = azurerm_resource_group.observability.name
location = azurerm_resource_group.observability.location
destinations {
monitor_account {
monitor_account_id = azurerm_monitor_workspace.prometheus.id
name = "MonitoringAccount"
}
}
data_flow {
streams = ["Microsoft-PrometheusMetrics"]
destinations = ["MonitoringAccount"]
}
data_sources {
prometheus_forwarder {
name = "PrometheusDataSource"
streams = ["Microsoft-PrometheusMetrics"]
}
}
}
resource "azurerm_monitor_data_collection_rule_association" "aks_prometheus" {
name = "dcra-prometheus-aks"
target_resource_id = azurerm_kubernetes_cluster.aks.id
data_collection_rule_id = azurerm_monitor_data_collection_rule.prometheus.id
}
Exportando e versionando dashboards
# Exportar dashboard existente do Grafana
az grafana dashboard show \
--name $GRAFANA_NAME \
--resource-group $RESOURCE_GROUP \
--dashboard "AKS Cluster Overview" \
-o json > dashboards/aks-cluster-overview.json
# Importar dashboard a partir de arquivo versionado
az grafana dashboard create \
--name $GRAFANA_NAME \
--resource-group $RESOURCE_GROUP \
--definition @dashboards/aks-cluster-overview.json \
--overwrite
Instrumentando sua aplicação
Para que métricas de negócio apareçam no Grafana, suas aplicações precisam expor um endpoint /metrics no formato Prometheus. Aqui estão exemplos para as linguagens mais comuns no ecossistema Azure.
Python (FastAPI + prometheus-client)
# metrics.py
from prometheus_client import Counter, Histogram, Gauge, generate_latest
from fastapi import FastAPI, Request, Response
import time
app = FastAPI()
# Métricas de negócio
REQUEST_COUNT = Counter(
'http_requests_total',
'Total de requisições HTTP',
['method', 'endpoint', 'status']
)
REQUEST_LATENCY = Histogram(
'http_request_duration_seconds',
'Latência de requisições HTTP em segundos',
['method', 'endpoint'],
buckets=[0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0]
)
ACTIVE_CONNECTIONS = Gauge(
'http_active_connections',
'Conexões HTTP ativas'
)
ORDERS_PROCESSED = Counter(
'orders_processed_total',
'Total de pedidos processados',
['status'] # success, failed, cancelled
)
@app.middleware("http")
async def metrics_middleware(request: Request, call_next):
ACTIVE_CONNECTIONS.inc()
start_time = time.time()
response = await call_next(request)
duration = time.time() - start_time
REQUEST_COUNT.labels(
method=request.method,
endpoint=request.url.path,
status=response.status_code
).inc()
REQUEST_LATENCY.labels(
method=request.method,
endpoint=request.url.path
).observe(duration)
ACTIVE_CONNECTIONS.dec()
return response
@app.get("/metrics")
async def metrics():
return Response(
content=generate_latest(),
media_type="text/plain"
)
@app.post("/orders")
async def create_order(order: dict):
try:
# lógica de negócio
result = process_order(order)
ORDERS_PROCESSED.labels(status="success").inc()
return {"status": "created", "id": result.id}
except Exception as e:
ORDERS_PROCESSED.labels(status="failed").inc()
raise
.NET (ASP.NET Core + prometheus-net)
// Program.cs
using Prometheus;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
// Middleware para métricas HTTP automáticas
app.UseHttpMetrics(options =>
{
options.AddCustomLabel("host", context => context.Request.Host.Host);
});
app.MapControllers();
// Endpoint /metrics
app.MapMetrics();
// Métricas customizadas de negócio
var ordersProcessed = Metrics.CreateCounter(
"orders_processed_total",
"Total de pedidos processados",
new CounterConfiguration
{
LabelNames = new[] { "status" }
});
var orderProcessingDuration = Metrics.CreateHistogram(
"order_processing_duration_seconds",
"Tempo de processamento de pedidos",
new HistogramConfiguration
{
Buckets = Histogram.ExponentialBuckets(0.01, 2, 10)
});
app.Run();
Troubleshooting: os problemas mais comuns
Depois de ajudar dezenas de times a implementar essa stack, estes são os problemas que mais consomem tempo e como resolvê-los.
Problema 1: Métricas não aparecem no Grafana
Checklist de diagnóstico:
# 1. Verificar se o agente ama-metrics está rodando
kubectl get pods -n kube-system -l rsName=ama-metrics
# 2. Verificar logs do agente
kubectl logs -n kube-system -l rsName=ama-metrics -c prometheus-collector --tail=50
# 3. Verificar se o agente está conseguindo fazer scrape
kubectl logs -n kube-system -l rsName=ama-metrics -c prometheus-collector | grep "scrape"
# 4. Verificar ConfigMap de configuração
kubectl get configmap ama-metrics-prometheus-config -n kube-system -o yaml
# 5. Verificar se o endpoint /metrics da aplicação está acessível
kubectl port-forward svc/api-pedidos 8080:8080 -n production
curl http://localhost:8080/metrics
Causa mais comum: O namespace da aplicação não está incluído no ConfigMap de scrape. Por padrão, o agente só coleta métricas de kube-system e targets pré-configurados.
Problema 2: High cardinality - custos explodindo
Métricas com muitos labels únicos (alta cardinalidade) inflam o volume de ingestão e os custos. Exemplos perigosos:
# RUIM: user_id como label (milhares de valores)
http_requests_total{user_id="abc123", endpoint="/api/orders"}
# BOM: agregar por endpoint, sem user_id
http_requests_total{endpoint="/api/orders"}
Como identificar métricas de alta cardinalidade:
# Top 10 métricas por número de séries temporais
topk(10, count by (__name__) ({__name__!=""}))
Como limitar no agente:
# ama-metrics-settings-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: ama-metrics-settings-configmap
namespace: kube-system
data:
default-scrape-settings: |
[default-scrape-settings]
# Limitar o número de séries temporais por scrape target
sample_limit = 5000
# Limitar o tamanho do label
label_limit = 20
label_name_length_limit = 128
label_value_length_limit = 512
Problema 3: Grafana data source "Prometheus" não mostra dados
Isso acontece quando o data source foi configurado manualmente (em vez de automaticamente pelo --grafana-resource-id). Para verificar e corrigir:
# Listar data sources configurados
az grafana data-source list \
--name $GRAFANA_NAME \
--resource-group $RESOURCE_GROUP
# Se o data source Prometheus não existir, criar manualmente
az grafana data-source create \
--name $GRAFANA_NAME \
--resource-group $RESOURCE_GROUP \
--definition '{
"name": "Azure Managed Prometheus",
"type": "prometheus",
"access": "proxy",
"url": "https://amw-prometheus-prod-xxxxx.eastus2.prometheus.monitor.azure.com",
"jsonData": {
"httpMethod": "POST",
"azureCredentials": {
"authType": "msi"
}
}
}'
Problema 4: Prometheus rules não disparam alertas
Causas comuns:
- Rule group não associado ao cluster: O
--cluster-nameprecisa corresponder exatamente ao nome do cluster AKS. - Métrica referenciada não existe: A regra usa uma métrica que não está sendo coletada. Verifique no Grafana Explore:
# Verificar se a métrica existe
up{job="api-pedidos"}
# Listar todas as métricas disponíveis de um job
{job="api-pedidos"}
- Período de avaliação longo demais: Se o
foréPT30Mmas a métrica só fica anormal por 10 minutos, o alerta nunca vai disparar.
Problema 5: Latência alta nas queries do Grafana
Queries lentas geralmente indicam que você está consultando muitas séries temporais ou usando ranges muito longos.
Otimizações:
| Problema | Solução |
|---|---|
Query lenta com rate() em range grande | Usar recording rules para pré-computar |
| Dashboard carrega devagar | Reduzir o time range padrão (1h em vez de 24h) |
| Muitas séries no mesmo painel | Adicionar filtros por namespace ou label |
topk() em milhões de séries | Filtrar antes do topk() com label matchers |
Custos e otimização
Entender o modelo de custo é essencial para evitar surpresas na fatura.
Modelo de precificação
| Componente | Modelo de cobrança | Faixa de preço (referência) |
|---|---|---|
| Azure Monitor workspace (Prometheus) | Por milhão de amostras ingeridas | ~$0.30 por milhão de amostras (primeiros 50B) |
| Managed Grafana Standard | Instância fixa mensal | ~$150/mês por instância |
| Retenção | 18 meses incluídos | Sem custo adicional para retenção padrão |
| Queries | Incluídas | Sem custo por query PromQL |
Dicas para controlar custos
- Reduza o scrape interval: Mudar de 15s para 30s corta a ingestão pela metade
- Filtre métricas desnecessárias: Nem toda métrica do node-exporter é útil
- Use keep/drop no relabel_configs: Descarte séries que você nunca vai consultar
- Monitore a ingestão: Use as métricas internas do agente para acompanhar volume
# Exemplo: descartar métricas de filesystem de tmpfs e shm
relabel_configs:
- source_labels: [__name__]
regex: 'node_filesystem_(size_bytes|avail_bytes|free_bytes)'
action: keep
metric_relabel_configs:
- source_labels: [fstype]
regex: 'tmpfs|shm'
action: drop
Comparativo: Managed Prometheus vs Azure Monitor Metrics
Para fechar, uma comparação direta entre usar Prometheus gerenciado e Azure Monitor Metrics nativo. Muitos times se perguntam se precisam escolher um ou outro — a resposta é que podem coexistir.
| Critério | Azure Managed Prometheus | Azure Monitor Metrics (nativo) |
|---|---|---|
| Linguagem de query | PromQL | KQL (Kusto) |
| Formato de métricas | Prometheus / OpenMetrics | Azure Monitor metrics format |
| Visualização | Grafana (managed) | Azure Workbooks, Portal, Power BI |
| Alertas | Prometheus alert rules (PromQL) | Azure Monitor Alert Rules (KQL) |
| Ecossistema | Prometheus exporters, dashboards Grafana.com | Azure integrations nativas |
| Retenção padrão | 18 meses | 93 dias (métricas de plataforma) |
| Multi-cluster | Nativo (mesmo workspace) | Nativo (mesmo Log Analytics) |
| Curva de aprendizado | Familiar para quem já usa Prometheus/Grafana | Familiar para quem já usa Azure |
| Custo | Baseado em ingestão de amostras | Baseado em ingestão de dados + queries |
Recomendação: Use Prometheus + Grafana para métricas de infraestrutura Kubernetes e aplicações cloud-native. Use Azure Monitor para métricas de serviços PaaS do Azure (App Service, SQL Database, Storage, etc.) e correlação com logs via KQL. Os dois coexistem naturalmente.
Conclusão
A combinação de Azure Managed Grafana com Azure Monitor managed service for Prometheus representa o melhor dos dois mundos: a familiaridade e o ecossistema rico do stack open-source Prometheus/Grafana com a confiabilidade operacional e a integração nativa do Azure.
Neste artigo, implementamos uma stack completa de observabilidade moderna que vai desde o provisionamento via CLI e Terraform até dashboards avançados com PromQL, alerting rules com integração a Action Groups, monitoramento multi-cluster e instrumentação de aplicações em Python e .NET. Também cobrimos os cinco problemas mais comuns que travam times em produção e como resolvê-los de forma definitiva.
Os próximos passos naturais a partir daqui são:
- Integrar com SLOs: Usar as métricas Prometheus como base para SLIs e SLOs usando recording rules para calcular error budgets
- Adicionar traces: Complementar métricas com distributed tracing via OpenTelemetry + Azure Monitor Application Insights
- Automatizar resposta a incidentes: Conectar alertas Prometheus com runbooks automatizados
- Dashboards como código no pipeline: Versionar todos os dashboards em Git e deployar via CI/CD
Se você já tem Prometheus e Grafana self-hosted, a migração para o managed é incremental: comece com um cluster de staging, valide que suas queries PromQL funcionam identicamente e expanda para produção. A compatibilidade é excelente — na maioria dos casos, é literalmente plug and play.
Referências
- Azure Managed Grafana — Documentação oficial
- Azure Monitor managed service for Prometheus — Overview
- Configurar coleta de métricas Prometheus no AKS
- Prometheus Rule Groups no Azure Monitor
- Custom scrape configuration para Prometheus no AKS
- Grafana dashboards — Comunidade
- PromQL — Documentação oficial
- Terraform provider azurerm — azurerm_dashboard_grafana