<h2>Introdução ao Prometheus em Kubernetes</h2>
<p>Prometheus é uma solução open-source de monitoramento e alertas que se tornou padrão na indústria para ambientes Cloud Native e Kubernetes. Diferentemente de ferramentas legadas que usam polling ou agentes push, Prometheus funciona sob o modelo pull: ele periodicamente conecta nos endpoints de suas aplicações e coleta métricas no formato Prometheus Text Format. Em um cluster Kubernetes, a força real do Prometheus emerge quando você o combina com o Operator e o ServiceMonitor, criando um sistema declarativo, dinâmico e autoexplicativo.</p>
<p>O maior diferencial do Prometheus em Kubernetes não é apenas a coleta de métricas, mas sua integração profunda com o modelo declarativo do próprio Kubernetes. Você define ServiceMonitor como um Custom Resource Definition (CRD), e o Prometheus Operator automaticamente sincroniza a configuração sem necessidade de recarregar o Prometheus. Isso elimina o tradicional problema de editar arquivos YAML de scrape configs e fazer rolling restart de pods.</p>
<h2>Prometheus Operator e ServiceMonitor</h2>
<h3>O que é o Prometheus Operator?</h3>
<p>O Prometheus Operator é um controlador Kubernetes que gerencia a lifecycle completa do Prometheus através de Custom Resources. Em vez de gerenciar ConfigMaps e StatefulSets manualmente, você descreve o estado desejado via CRDs como <code>Prometheus</code>, <code>ServiceMonitor</code>, <code>PrometheusRule</code> e <code>AlertManager</code>. O Operator observa essas definições e automaticamente gera e atualiza os objetos Kubernetes necessários, incluindo o arquivo de configuração do Prometheus.</p>
<p>A instalação do Operator é feita tipicamente via Helm. Aqui está um exemplo prático:</p>
<pre><code class="language-bash"># Adicionar o repositório Prometheus Community
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
Instalar o kube-prometheus-stack (que inclui Operator, Prometheus, Grafana, etc)
helm install prometheus prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace \
--values values.yaml</code></pre>
<p>Aqui está um arquivo <code>values.yaml</code> mínimo e funcional:</p>
<pre><code class="language-yaml"># values.yaml para kube-prometheus-stack
prometheus:
prometheusSpec:
retention: 15d
storageSpec:
volumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 50Gi
Importante: define qual ServiceMonitor o Prometheus vai scrappear
serviceMonitorSelectorNilUsesHelmValues: false
grafana:
adminPassword: "admin123"
persistence:
enabled: true
size: 10Gi
alertmanager:
enabled: true</code></pre>
<h3>Entendendo ServiceMonitor</h3>
<p>ServiceMonitor é um CRD que funciona como um contrato entre sua aplicação e o Prometheus. Em vez de adicionar endereços IP ou nomes de serviço diretamente na configuração do Prometheus, você cria um ServiceMonitor que diz: "monitore todos os pods com o label <code>app=minha-app</code> na porta 8080 do endpoint <code>/metrics</code>". O Prometheus Operator descobre esses ServiceMonitors e dinamicamente adiciona as configurações de scrape.</p>
<p>A seleção funciona através de label matching. Você define em qual namespace buscar, quais labels de Service selecionar e quais labels de Pod selecionar. Isso é poderoso porque permite que diferentes times criem seus próprios ServiceMonitors sem precisar acessar a configuração central do Prometheus.</p>
<p>Vamos a um exemplo prático. Suponha que você tem uma aplicação Node.js que expõe métricas em <code>localhost:3000/metrics</code>. Primeiro, você cria um Deployment:</p>
<pre><code class="language-yaml"># deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-app
namespace: production
spec:
replicas: 2
selector:
matchLabels:
app: api-app
template:
metadata:
labels:
app: api-app
version: v1
spec:
containers:
- name: api
image: myregistry/api-app:1.0.0
ports:
- name: metrics
containerPort: 3000
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 10</code></pre>
<p>Depois, você cria um Service:</p>
<pre><code class="language-yaml"># service.yaml
apiVersion: v1
kind: Service
metadata:
name: api-app
namespace: production
labels:
app: api-app
spec:
ports:
- name: metrics
port: 3000
targetPort: metrics
protocol: TCP
selector:
app: api-app</code></pre>
<p>E finalmente, o ServiceMonitor que conecta tudo:</p>
<pre><code class="language-yaml"># servicemonitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: api-app-monitor
namespace: production
labels:
release: prometheus # Importante: deve corresponder ao seletor do Prometheus
spec:
selector:
matchLabels:
app: api-app
endpoints:
- port: metrics
interval: 30s
path: /metrics
scrapeTimeout: 10s</code></pre>
<p>Note o label <code>release: prometheus</code> no ServiceMonitor. Isso é crucial: no spec do Prometheus (via values.yaml do Helm), você configurou <code>serviceMonitorSelector</code> para selecionar ServiceMonitors com esse label. Sem isso, o Prometheus nunca vai descobrir seu ServiceMonitor.</p>
<h2>PromQL Avançado</h2>
<h3>Conceitos Fundamentais de PromQL</h3>
<p>PromQL é a linguagem de consulta do Prometheus, e dominar PromQL avançado é o que separa um operador iniciante de um especialista. PromQL trabalha com séries temporais, onde cada métrica é identificada por seu nome e um conjunto de labels. Uma consulta PromQL retorna um conjunto de pontos de dados em um intervalo de tempo.</p>
<p>Antes de mergulhar em construções complexas, é essencial entender os tipos de dados: vetores instantâneos (um valor por série no momento atual), vetores de intervalo (múltiplos valores por série em um período) e escalares (um único número). Funções diferentes operam sobre esses tipos e, frequentemente, o erro em uma consulta PromQL ocorre quando você tenta aplicar uma função esperando um tipo de dado que ela não retorna.</p>
<h3>Operadores e Funções Essenciais</h3>
<p>A maioria das queries PromQL começa simples: <code>http_requests_total</code> retorna todas as séries com esse nome. Mas conforme você monitora ambientes complexos, você precisa filtrar, agrupar e agregar. Aqui estão as operações mais poderosas:</p>
<p><strong>Filtros</strong>: <code>http_requests_total{job="api-app", method="GET"}</code> seleciona apenas requisições GET.</p>
<p><strong>Operadores Aritméticos</strong>: <code>rate(http_requests_total[5m])</code> calcula a taxa de requisições por segundo nos últimos 5 minutos. Este é talvez o operador mais importante em PromQL.</p>
<p><strong>Operadores Booleanos</strong>: <code>http_requests_total{status=~"5.."}</code> usa regex para selecionar status 5xx.</p>
<p><strong>Funções de Agregação</strong>: <code>sum(rate(http_requests_total[5m])) by (job)</code> soma as taxas agrupadas por job.</p>
<p>Vamos a um exemplo real. Suponha que você quer alertar quando a latência P95 de uma API fica acima de 500ms. Primeiro, sua aplicação precisa exportar um histograma:</p>
<pre><code class="language-yaml"># Em sua aplicação (exemplo com Prometheus client Python)
from prometheus_client import Histogram, start_http_server
request_latency = Histogram(
'http_request_duration_seconds',
'HTTP request latency in seconds',
buckets=(0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1.0, 2.5, 5.0)
)
@app.route('/api/users')
def get_users():
with request_latency.time():
seu código
return jsonify(users)</code></pre>
<p>A métrica exportada terá nomes como <code>http_request_duration_seconds_bucket</code>, <code>http_request_duration_seconds_sum</code> e <code>http_request_duration_seconds_count</code>. Para calcular o P95:</p>
<pre><code class="language-promql">histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))</code></pre>
<p>Essa função calcula o percentil 95 dos latências nos últimos 5 minutos. Mas isso agrega globalmente. Se você quer P95 por endpoint:</p>
<pre><code class="language-promql">histogram_quantile(0.95, rate(http_request_duration_seconds_bucket{job="api-app"}[5m])) by (le)</code></pre>
<p>Não, espera. O <code>by (le)</code> está errado aqui. Você quer agrupar por endpoint, não por bucket label. A query correta é:</p>
<pre><code class="language-promql">histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket{job="api-app"}[5m])) by (handler, le))</code></pre>
<p>Isso é PromQL avançado: você primeiro agrega os buckets por <code>handler</code> (nome do endpoint), mantendo o label <code>le</code> (limite de bucket) necessário para a função <code>histogram_quantile</code> funcionar, e depois calcula o percentil.</p>
<h3>Operações com Offset e Comparações Temporais</h3>
<p>Um padrão poderoso é comparar o comportamento atual com o comportamento passado. Se você quer verificar se o tráfego atual está 50% acima da média dos últimos 7 dias:</p>
<pre><code class="language-promql">rate(http_requests_total[5m]) > (avg_over_time(rate(http_requests_total[5m])[7d:5m]) * 1.5)</code></pre>
<p>Quebra-se assim: <code>avg_over_time(...[7d:5m])</code> calcula a média de pontos de 5 minutos ao longo de 7 dias. O <code>:5m</code> é essencial — ele diz "use pontos a cada 5 minutos" em vez de recuperar milhões de pontos.</p>
<p>Outra técnica é o <code>offset</code>:</p>
<pre><code class="language-promql">rate(http_requests_total[5m]) - rate(http_requests_total[5m] offset 1h)</code></pre>
<p>Isso compara o tráfego atual com o de 1 hora atrás. Útil para detectar mudanças súbitas.</p>
<h3>Subqueries e Funções Complexas</h3>
<p>PromQL 2.7+ suporta subqueries, que permitem usar o resultado de uma query como entrada para outra. Por exemplo, para encontrar jobs com mais de 100 requisições por segundo no P99:</p>
<pre><code class="language-promql">topk(3, (histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (job, le))) > 0.5)</code></pre>
<p>Aqui, <code>histogram_quantile</code> é a "subquery" interna, e <code>topk(3, ...)</code> retorna os 3 piores jobs.</p>
<p>Funções de utilidade incluem <code>predict_linear(v range-vector, t scalar)</code> que extrapola uma tendência linear, útil para alertas proativos:</p>
<pre><code class="language-promql">predict_linear(disk_free_bytes[1h], 3600) < 1073741824 # Alerta se disco vai encher em 1 hora</code></pre>
<h2>PrometheusRule e Alertas Práticos</h2>
<h3>Estrutura de PrometheusRule</h3>
<p>PrometheusRule é o CRD que define regras de gravação (recording rules) e alertas. Uma recording rule pré-calcula uma expressão PromQL e a armazena como uma nova série temporal, reduzindo a carga de queries custosas. Um alerta define uma condição PromQL e a ação correspondente (enviar para AlertManager).</p>
<p>Aqui está um exemplo completo de PrometheusRule para uma aplicação em produção:</p>
<pre><code class="language-yaml"># prometheusrule.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: api-app-rules
namespace: production
labels:
prometheus: prometheus-stack
spec:
groups:
- name: api-app.rules
interval: 30s
rules:
Recording rule: pré-calcula taxa de requisições por endpoint
- record: job:http_requests:rate5m
expr: sum(rate(http_requests_total[5m])) by (job, handler)
Alert: taxa de erro acima de 5%
- alert: HighErrorRate
expr: |
(
sum(rate(http_requests_total{status=~"5.."}[5m])) by (job)
/
sum(rate(http_requests_total[5m])) by (job)
) > 0.05
for: 5m
labels:
severity: critical
team: backend
annotations:
summary: "High error rate detected for {{ $labels.job }}"
description: "Error rate is {{ $value | humanizePercentage }} for job {{ $labels.job }}"
Alert: latência P95 acima de 500ms
- alert: HighLatencyP95
expr: |
histogram_quantile(0.95,
sum(rate(http_request_duration_seconds_bucket[5m])) by (job, le)
) > 0.5
for: 10m
labels:
severity: warning
team: backend
annotations:
summary: "High P95 latency for {{ $labels.job }}"
description: "P95 latency is {{ $value | humanizeDuration }}"
Alert: tráfego anormalmente baixo (possível outage)
- alert: AbnormallyLowTraffic
expr: |
rate(http_requests_total[5m])
< (avg_over_time(rate(http_requests_total[5m])[7d:5m]) * 0.5)
for: 15m
labels:
severity: warning
annotations:
summary: "Traffic is abnormally low for {{ $labels.job }}"
description: "Current rate: {{ $value | humanize }}/s"</code></pre>
<p>Note alguns detalhes importantes:</p>
<ul>
<li><code>for: 5m</code> significa que o alerta só dispara se a condição permanecer verdadeira por 5 minutos. Isso evita alertas fluttuantes causados por picos momentâneos.</li>
<li><code>labels</code> são adicionados ao alerta e usados pelo AlertManager para roteamento e silenciamento.</li>
<li><code>annotations</code> usam templates Golang com <code>$labels</code> e <code>$value</code> para contextualizar o alerta.</li>
<li><code>humanizePercentage</code> e <code>humanizeDuration</code> são funções de formatação que transformam valores brutos em formatos legíveis.</li>
</ul>
<h3>Correlacionando Múltiplas Métricas</h3>
<p>Um passo além é correlacionar múltiplas métricas para reduzir false positives. Por exemplo, em vez de alertar por alto uso de CPU simplesmente, você pode alertar apenas se CPU está alta E memória está alta E I/O de disco está elevado:</p>
<pre><code class="language-yaml">- alert: SystemUnderStress
expr: |
(node_cpu_seconds_total > 0.8)
and (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes < 0.2)
and (rate(node_disk_io_time_seconds_total[5m]) > 0.5)
for: 10m
labels:
severity: critical</code></pre>
<p>Isso reduz significativamente alertas falsos porque é muito mais provável que um nó realmente tenha problemas se múltiplas métricas estão ruins simultaneamente.</p>
<h2>Integração Prática: Do Zero ao Monitoramento</h2>
<h3>Passo a Passo Completo</h3>
<p>Vamos criar um cenário real: você tem uma aplicação Python Flask e quer monitore-a com Prometheus em Kubernetes. Começa-se pelos pré-requisitos:</p>
<ol>
<li>Cluster Kubernetes rodando com <code>kube-prometheus-stack</code> instalado (conforme mostrado antes).</li>
<li>Sua aplicação exportando métricas Prometheus.</li>
</ol>
<p>Aqui está a aplicação Flask instrumentada:</p>
<pre><code class="language-python"># app.py
from flask import Flask, jsonify
from prometheus_client import Counter, Histogram, generate_latest, CONTENT_TYPE_LATEST
import time
app = Flask(__name__)
Métricas
request_count = Counter(
'http_requests_total',
'Total HTTP requests',
['method', 'endpoint', 'status']
)
request_duration = Histogram(
'http_request_duration_seconds',
'HTTP request duration in seconds',
['method', 'endpoint'],
buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0)
)
db_query_duration = Histogram(
'db_query_duration_seconds',
'Database query duration',
['query_type']
)
@app.before_request
def before_request():
app.request_start_time = time.time()
@app.after_request
def after_request(response):
duration = time.time() - app.request_start_time
request_duration.labels(
method=request.method,
endpoint=request.path
).observe(duration)
request_count.labels(
method=request.method,
endpoint=request.path,
status=response.status_code
).inc()
return response
@app.route('/health')
def health():
return jsonify({'status': 'ok'}), 200
@app.route('/api/users')
def get_users():
Simular query ao banco
with db_query_duration.labels(query_type='select').time():
time.sleep(0.05)
return jsonify([{'id': 1, 'name': 'Alice'}, {'id': 2, 'name': 'Bob'}]), 200
@app.route('/metrics')
def metrics():
return generate_latest(), 200, {'Content-Type': CONTENT_TYPE_LATEST}
if __name__ == '__main__':
app.run(host='0.0.0.0', port=3000)</code></pre>
<p>Dockerfile:</p>
<pre><code class="language-dockerfile">FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 3000
CMD ["python", "app.py"]</code></pre>
<p>requirements.txt:</p>
<pre><code>Flask==3.0.0
prometheus-client==0.19.0</code></pre>
<p>Agora, os manifests Kubernetes:</p>
<pre><code class="language-yaml"># deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-app
namespace: production
labels:
app: flask-app
spec:
replicas: 3
selector:
matchLabels:
app: flask-app
template:
metadata:
labels:
app: flask-app
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "3000"
prometheus.io/path: "/metrics"
spec:
containers:
- name: app
image: myregistry/flask-app:1.0.0
imagePullPolicy: Always
ports:
- name: http
containerPort: 3000
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 5
periodSeconds: 5
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
---
apiVersion: v1
kind: Service
metadata:
name: flask-app
namespace: production
labels:
app: flask-app
spec:
type: ClusterIP
ports:
- name: http
port: 3000
targetPort: http
protocol: TCP
selector:
app: flask-app
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: flask-app
namespace: production
labels:
release: prometheus
spec:
selector:
matchLabels:
app: flask-app
endpoints:
- port: http
interval: 30s
path: /metrics
scrapeTimeout: 10s
---
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: flask-app-alerts
namespace: production
labels:
prometheus: prometheus-stack
spec:
groups:
- name: flask-app.rules
interval: 30s
rules:
- record: flask:request_rate:5m
expr: sum(rate(http_requests_total{job="flask-app"}[5m])) by (endpoint, status)
- alert: HighRequestErrorRate
expr: |
(
sum(rate(http_requests_total{job="flask-app", status=~"5.."}[5m])) by (endpoint)
/
sum(rate(http_requests_total{job="flask-app"}[5m])) by (endpoint)
) > 0.1
for: 5m
labels:
severity: warning
app: flask-app
annotations:
summary: "High error rate on {{ $labels.endpoint }}"
description: "Error rate is {{ $value | humanizePercentage }}"
- alert: HighP95Latency
expr: |
histogram_quantile(0.95,
sum(rate(http_request_duration_seconds_bucket{job="flask-app"}[5m])) by (endpoint, le)
) > 0.5
for: 10m
labels:
severity: warning
app: flask-app
annotations:
summary: "Slow responses on {{ $labels.endpoint }}"
description: "P95 latency is {{ $value | humanizeDuration }}"</code></pre>
<p>Após aplicar esses manifests com <code>kubectl apply -f</code>, o Prometheus Operator detectará o ServiceMonitor e o PrometheusRule, sincronizará com o Prometheus, e os alertas estarão ativos automaticamente.</p>
<h3>Verificação e Debug</h3>
<p>Para verificar se tudo está funcionando:</p>
<pre><code class="language-bash"># Verificar se o ServiceMonitor foi descoberto
kubectl get servicemonitor -n production
Ver targets descobertos no Prometheus
kubectl port-forward -n monitoring svc/prometheus-operated 9090:9090
Acesso em http://localhost:9090/targets
Consultar logs do Prometheus Operator
kubectl logs -n monitoring -l app.kubernetes.io/name=prometheus-operator -f
Testar a aplicação
kubectl port-forward -n production svc/flask-app 3000:3000
curl http://localhost:3000/metrics</code></pre>
<h2>Conclusão</h2>
<p>Dominar Prometheus em Kubernetes significa entender três pilares: o Prometheus Operator, que torna a configuração declarativa e dinâmica; o ServiceMonitor, que permite descoberta automática e desacoplamento entre aplicações e monitoramento; e PromQL avançado, que transforma dados brutos em insights acionáveis. O verdadeiro valor emerge quando você combina esses três: usar PromQL para escrever queries que capturam o comportamento real de suas aplicações, expressar essas queries como PrometheusRules para automatizar detecção de problemas, e usar ServiceMonitor para garantir que novas aplicações sejam monitoradas sem necessidade de reconfiguração manual. A chave é começar simples, com métricas bem instrumentadas e alertas bem baseados em dados, e iterativamente aumentar a sofisticação conforme você entende os padrões de seu sistema.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://prometheus-operator.dev/" target="_blank" rel="noopener noreferrer">Prometheus Operator Documentation</a></li>
<li><a href="https://prometheus.io/docs/prometheus/latest/querying/examples/" target="_blank" rel="noopener noreferrer">PromQL Query Examples - Prometheus Official</a></li>
<li><a href="https://prometheus.io/docs/operating/integrations/#kubernetes" target="_blank" rel="noopener noreferrer">Kubernetes Monitoring with Prometheus - Official CNCF Guide</a></li>
<li><a href="https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack" target="_blank" rel="noopener noreferrer">kube-prometheus-stack Helm Chart</a></li>
<li><a href="https://www.oreilly.com/library/view/prometheus-up-and-running/9781492034131/" target="_blank" rel="noopener noreferrer">Prometheus Up and Running - O'Reilly Media (Capítulos sobre PromQL e Alertas)</a></li>
</ul>
<p><!-- FIM --></p>