Por que PLG e não ELK?#

Simples: custo e operação.

ELK (Elasticsearch + Logstash + Kibana) consome muito mais recurso e é complexo de manter. A stack PLG (Prometheus + Loki + Grafana) roda mais leve, integra nativamente com K8s e o Grafana unifica métricas, logs e traces em um único lugar.


Arquitetura#

                    ┌─────────────────┐
                    │     Grafana     │  ← única UI pra tudo
                    └────────┬────────┘
                             │
              ┌──────────────┼──────────────┐
              │              │              │
       ┌──────┴──────┐ ┌─────┴────┐ ┌─────┴─────┐
       │  Prometheus  │ │   Loki   │ │  Tempo    │
       │  (métricas) │ │  (logs)  │ │ (traces)  │
       └──────┬───────┘ └────┬─────┘ └───────────┘
              │              │
     ┌────────┴──────┐  ┌────┴──────┐
     │ Node Exporter │  │  Promtail │
     │ kube-state    │  │  (agente) │
     └───────────────┘  └───────────┘

1. kube-prometheus-stack (Prometheus + Grafana + Alertmanager)#

1
2
3
4
5
6
7
8
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

helm upgrade --install kube-prometheus-stack \
  prometheus-community/kube-prometheus-stack \
  --namespace monitoring \
  --create-namespace \
  --values prometheus-values.yaml

prometheus-values.yaml essencial:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# prometheus-values.yaml
grafana:
  enabled: true
  adminPassword: "TROCA_ISSO_POR_SECRET"
  ingress:
    enabled: false    # usamos IngressRoute do Traefik
  persistence:
    enabled: true
    size: 10Gi

prometheus:
  prometheusSpec:
    retention: 15d
    retentionSize: "40GB"
    storageSpec:
      volumeClaimTemplate:
        spec:
          storageClassName: oci-bv-high-perf
          resources:
            requests:
              storage: 50Gi
    resources:
      requests:
        cpu: 500m
        memory: 2Gi
      limits:
        cpu: 2
        memory: 4Gi

alertmanager:
  alertmanagerSpec:
    storage:
      volumeClaimTemplate:
        spec:
          storageClassName: oci-bv-high-perf
          resources:
            requests:
              storage: 5Gi

# ServiceMonitors automáticos para todos os namespaces
prometheus:
  prometheusSpec:
    serviceMonitorSelectorNilUsesHelmValues: false
    podMonitorSelectorNilUsesHelmValues: false

2. Loki com Object Storage (OCI ou S3)#

1
2
3
4
5
helm repo add grafana https://grafana.github.io/helm-charts

helm upgrade --install loki grafana/loki \
  --namespace monitoring \
  --values loki-values.yaml

loki-values.yaml com OCI Object Storage:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# loki-values.yaml
loki:
  auth_enabled: false
  commonConfig:
    replication_factor: 1     # single node; use 3 em HA

  storage:
    type: s3
    s3:
      endpoint: https://<namespace>.compat.objectstorage.<region>.oraclecloud.com
      bucketnames: loki-chunks
      region: sa-saopaulo-1
      access_key_id: "${OCI_ACCESS_KEY}"
      secret_access_key: "${OCI_SECRET_KEY}"
      s3forcepathstyle: true

  schemaConfig:
    configs:
      - from: "2024-01-01"
        store: tsdb
        object_store: s3
        schema: v13
        index:
          prefix: loki_index_
          period: 24h

  limits_config:
    retention_period: 744h   # 31 dias
    ingestion_rate_mb: 16
    ingestion_burst_size_mb: 32

singleBinary:
  replicas: 1
  resources:
    requests:
      cpu: 200m
      memory: 512Mi
    limits:
      cpu: 1
      memory: 1Gi

promtail:
  enabled: true
  config:
    clients:
      - url: http://loki:3100/loki/api/v1/push

3. Expor Grafana via Traefik#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# grafana-ingressroute.yaml
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: grafana
  namespace: monitoring
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`grafana.castroti.com.br`)
      kind: Rule
      services:
        - name: kube-prometheus-stack-grafana
          port: 80
  tls:
    certResolver: le

4. Alertas que importam#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# custom-alerts.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: infra-alerts
  namespace: monitoring
spec:
  groups:
    - name: infra.critical
      rules:
        - alert: NodeDiskSpaceCritical
          expr: (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) < 0.10
          for: 5m
          labels:
            severity: critical
          annotations:
            summary: "Disco {{ $labels.instance }} com menos de 10% livre"

        - alert: PodCrashLooping
          expr: rate(kube_pod_container_status_restarts_total[15m]) > 0
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "Pod {{ $labels.pod }} em CrashLoop"

        - alert: NodeMemoryPressure
          expr: (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) < 0.15
          for: 10m
          labels:
            severity: warning
          annotations:
            summary: "Memória disponível abaixo de 15% em {{ $labels.instance }}"

5. Dashboards úteis (IDs Grafana.com)#

Dashboard ID O que mostra
Kubernetes Cluster 15661 Overview geral do cluster
Node Exporter Full 1860 CPU, mem, disco, rede por node
Loki Dashboard 13639 Logs + métricas unificados
Traefik 17346 Requests, latência, erros
PostgreSQL 9628 Conexões, queries, locks

Import via: Grafana → Dashboards → Import → ID


Custo estimado (OCI Always Free + Paid)#

Prometheus (50Gi Block Volume): ~$5/mês
Loki (Object Storage 50GB):     ~$1.5/mês
Grafana (ephemeral, 10Gi):      ~$1/mês
Total:                          ~$8/mês para toda a stack

Versus ELK equivalente: $50-100/mês. O argumento tá aí.