MÓDULO 10 · CONCEITO 11 DE 12

Observabilidade em Kubernetes

kube-state-metrics, node-exporter, events, OTel Operator, Kubernetes-native observability stack e debugging de pods

Tempo de leitura ~22 min Pré-requisito M10C05 — OpenTelemetry Próximo M10C12 — Debugging Orientado a Dados

As Camadas de Observabilidade no Kubernetes

Kubernetes adiciona múltiplas camadas de infraestrutura entre a aplicação e o hardware. Cada camada tem seu próprio conjunto de métricas, logs e eventos. Para debugar um problema em produção, você precisa ter visibilidade em todas elas simultaneamente.

CamadaO que monitorarFerramenta principal
Cluster / Control PlaneAPI server latência, etcd, scheduler queuekube-apiserver metrics
Nó (Node)CPU, memória, disco, rede do nó físico/VMnode-exporter
Kubernetes objectsPod status, deployment replicas, HPA, PVCkube-state-metrics
Container runtimeCPU/memória por container, restartscAdvisor (via Kubelet)
AplicaçãoLogs, métricas, traces de negócioOTel SDK + Fluent Bit

A complexidade central é que uma latência alta em um endpoint pode ter origem em qualquer camada: a aplicação pode estar com GC pausas longas (camada app), o container pode estar sendo throttled por CPU limit (cAdvisor), o nó pode estar com CPU steal alto por vizinhos barulhentos (node-exporter), ou o Kubernetes scheduler pode estar remanejando pods (KSM + events). Correlacionar todas as camadas ao mesmo timestamp é o que diferencia debugging eficaz de tentativa e erro.

node-exporter — Métricas do Nó

O node-exporter (Prometheus) é um DaemonSet que coleta métricas do sistema operacional de cada nó: CPU (por modo: user, system, iowait, steal), memória (disponível, buffers, cache), disco (read/write ops, latência, saturation), rede (bytes in/out, erros, drops), e métricas de filesystem.

Métricas Críticas do node-exporter

# CPU por modo — "steal" alto indica problema na VM compartilhada
100 - (avg by (instance) (
  rate(node_cpu_seconds_total{mode="idle"}[5m])
) * 100)

# Memória disponível real (free + buffers + cache)
node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes

# I/O wait de disco — indica saturação de disco
rate(node_cpu_seconds_total{mode="iowait"}[5m])

# Disk saturation — tempo médio de uma operação de I/O (latência)
rate(node_disk_io_time_seconds_total[5m])  # 1.0 = 100% saturado

# Espaço em disco restante por mountpoint
(node_filesystem_avail_bytes / node_filesystem_size_bytes) * 100

# Conexões de rede — detectar SYN flood ou CLOSE_WAIT acumulando
node_netstat_Tcp_CurrEstab  # conexões TCP estabelecidas
node_netstat_Tcp_InSegs     # segmentos TCP recebidos (rate)

CPU Steal — O Risco das VMs Compartilhadas

Em ambientes de cloud com VMs shared (t3.medium da AWS, n1-standard da GCP), o CPU steal indica que a VM precisava de CPU mas o hypervisor estava servindo outra VM. Alta steal (> 5-10%) causa latência imprevisível — a aplicação parece saudável nas métricas próprias, mas latência P99 sobe sem causa aparente. Solução: migrar para instâncias dedicadas ou instâncias com CPU credits garantidos.

Container CPU Throttling

Uma métrica complementar ao node-exporter é o throttling de containers via cAdvisor: container_cpu_cfs_throttled_seconds_total. Um container pode ser throttled mesmo quando o nó tem CPU disponível — isso acontece quando o container excede seu cpu limit no intervalo CFS (100ms por padrão). Alta taxa de throttling com P99 de latência elevada, mas CPU do nó saudável, aponta para cpu limits muito restritivos no deployment. A solução é aumentar o limite ou remover o cpu limit (deixando apenas requests) para workloads com picos de CPU previsíveis.

kube-state-metrics — Estado dos Objetos Kubernetes

kube-state-metrics (KSM) expõe métricas sobre o estado dos objetos Kubernetes: quantos pods estão Running vs Pending vs Failed, quantas réplicas um Deployment tem vs o desejado, status de PersistentVolumeClaims, condições de Nodes, etc. É a principal ferramenta para saber "o cluster está saudável?".

Métricas KSM Essenciais

# Pods que não estão Running — alerta se qualquer pod não-running por > 5min
count by (namespace, pod, reason) (
  kube_pod_container_status_waiting_reason > 0
)
# Reasons comuns: CrashLoopBackOff, ImagePullBackoff, OOMKilled, Pending

# Deployment: réplicas disponíveis vs desejadas
kube_deployment_status_replicas_available
/ kube_deployment_status_replicas
# ratio < 1 indica degradação — alertar quando < 0.5 por > 5min

# HPA: está o autoscaler no limite?
kube_horizontalpodautoscaler_status_current_replicas
== kube_horizontalpodautoscaler_spec_max_replicas
# se verdadeiro, HPA atingiu o limite — pode haver problema de capacidade

# PVC não bound — volumes não disponíveis
kube_persistentvolumeclaim_status_phase{phase!="Bound"}

# Node conditions — nó com problema (DiskPressure, MemoryPressure)
kube_node_status_condition{condition!="Ready", status="true"}

# Job falhos — CronJobs com falha
kube_job_failed > 0

# Container OOMKilled — container encerrado por falta de memória
kube_pod_container_status_last_terminated_reason{reason="OOMKilled"}

# Detecção antecipada de OOMKill iminente (cAdvisor)
container_memory_working_set_bytes
/ container_spec_memory_limit_bytes > 0.80
# Alerta quando working set > 80% do limit por > 10min
KSM vs cAdvisor KSM reporta o estado declarado dos objetos Kubernetes (quantas réplicas o Deployment quer, qual o status do Pod). cAdvisor (integrado ao Kubelet) reporta o consumo real de recursos (CPU e memória reais de cada container). Para alertas de "pod com problema": KSM. Para alertas de "container consumindo memória demais": cAdvisor via container_memory_working_set_bytes. Use os dois em conjunto — KSM alerta o sintoma, cAdvisor ajuda a diagnosticar a causa.

Kubernetes Events

Kubernetes Events são o log de atividade do cluster — registram eventos como: pod scheduled, pod started, container killed (OOM), image pull failed, liveness probe failed, HPA scale event. Por padrão, Events são mantidos por 1 hora — essenciais para debugging mas perdidos rapidamente.

Exportar Events para Loki/Elasticsearch

Para manter histórico de Events além de 1 hora, exporte-os para o sistema de log aggregation. Ferramentas: kube-event-exporter (exporta Events como logs estruturados para Elasticsearch, Loki, Slack), kubernetes-event-exporter (CNCF), ou via Fluent Bit com plugin de Events.

# kubectl — filtrar events de um namespace ou pod específico
kubectl get events -n production --sort-by=.lastTimestamp

# Filtrar apenas Warnings (erros)
kubectl get events -n production --field-selector type=Warning

# Events de um pod específico
kubectl describe pod <pod-name> -n production
# A seção "Events" mostra: FailedScheduling, BackOff, Killing, etc.

# kube-event-exporter — values.yaml (Helm)
config:
  logLevel: error
  logFormat: json
  route:
    routes:
      - match:
          - receiver: loki
  receivers:
    - name: loki
      loki:
        streamLabels:
          cluster: production
          app: kube-event-exporter
        url: http://loki.monitoring.svc:3100/loki/api/v1/push
        # Events chegam no Loki como logs estruturados
        # busca: {app="kube-event-exporter"} | json | reason="OOMKilled"

# LogQL — encontrar todos OOMKilled nas últimas 24h
{app="kube-event-exporter"} | json
  | reason="OOMKilled"
  | line_format "{{.involvedObject_namespace}}/{{.involvedObject_name}}"

Events Importantes para Monitorar

OTel Operator para Kubernetes

O OpenTelemetry Operator é um Kubernetes Operator que gerencia a configuração de instrumentação OTel no cluster. Ele resolve dois problemas: (1) implantar e manter o OTel Collector de forma declarativa via CRDs; (2) auto-instrumentar pods sem modificar os deployments das aplicações (via instrumentação por mutating webhook).

OpenTelemetryCollector CRD

apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
  name: otel-collector
  namespace: monitoring
spec:
  mode: DaemonSet  # um Collector por nó (agent mode)
  config: |
    receivers:
      otlp:
        protocols:
          grpc:
            endpoint: 0.0.0.0:4317
          http:
            endpoint: 0.0.0.0:4318
      # Scraping de métricas Prometheus dos pods
      prometheus:
        config:
          scrape_configs:
            - job_name: pod-scraping
              kubernetes_sd_configs:
                - role: pod
              relabel_configs:
                - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
                  action: keep
                  regex: "true"

    processors:
      # Adicionar metadados Kubernetes ao resource dos spans
      k8sattributes:
        auth_type: serviceAccount
        extract:
          metadata:
            - k8s.pod.name
            - k8s.namespace.name
            - k8s.node.name
            - k8s.deployment.name
          labels:
            - tag_name: app.version
              key: app.kubernetes.io/version
              from: pod
      batch:
        timeout: 5s
      # Limitar memória do Collector em ambientes constrained
      memory_limiter:
        limit_mib: 400
        spike_limit_mib: 100
        check_interval: 5s

    exporters:
      otlp/tempo:
        endpoint: tempo.monitoring.svc:4317
        tls: { insecure: true }
      prometheusremotewrite:
        endpoint: http://mimir.monitoring.svc:9009/api/v1/push

    service:
      pipelines:
        traces:
          receivers: [otlp]
          processors: [memory_limiter, k8sattributes, batch]
          exporters: [otlp/tempo]
        metrics:
          receivers: [otlp, prometheus]
          processors: [memory_limiter, k8sattributes, batch]
          exporters: [prometheusremotewrite]

Auto-Instrumentation via Instrumentation CRD

apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: otel-instrumentation
  namespace: default
spec:
  exporter:
    endpoint: http://otel-collector.monitoring.svc:4318
  propagators:
    - tracecontext
    - baggage
  sampler:
    type: parentbased_traceidratio
    argument: "0.1"  # 10% sampling
  # Configuração por linguagem
  java:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:latest
  python:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-python:latest
  dotnet:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-dotnet:latest
  nodejs:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-nodejs:latest

---
# Pod: anotar para auto-instrumentação
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  template:
    metadata:
      annotations:
        # O Operator injeta automaticamente o agente OTel no container
        instrumentation.opentelemetry.io/inject-dotnet: "default/otel-instrumentation"
        # Para Python:
        # instrumentation.opentelemetry.io/inject-python: "default/otel-instrumentation"

A auto-instrumentação via Operator usa um mutating admission webhook: quando um pod é criado com a annotation correta, o webhook intercepta o request de criação antes de ser persistido no etcd, injeta um init container que prepara o agente OTel, e adiciona variáveis de ambiente ao container da aplicação (OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME, NODE_IP para resolver o collector DaemonSet). A imagem Docker original não é alterada — o agente vive apenas no ambiente de execução do pod.

Stack Completo de Observabilidade em Kubernetes

Uma stack bem integrada cobre todas as camadas do cluster com ferramentas que se correlacionam. A stack LGTM (Loki, Grafana, Tempo, Mimir) é o padrão open-source mais adotado para clusters Kubernetes de médio a grande porte.

# kube-prometheus-stack (Helm) — instala o conjunto completo
# Inclui: Prometheus Operator, Grafana, AlertManager,
#         kube-state-metrics, node-exporter, ServiceMonitor CRDs

helm install kube-prometheus-stack \
  prometheus-community/kube-prometheus-stack \
  --namespace monitoring \
  --create-namespace \
  --set prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.resources.requests.storage=50Gi \
  --set grafana.persistence.enabled=true \
  --set alertmanager.alertmanagerSpec.storage.volumeClaimTemplate.spec.resources.requests.storage=10Gi

# Adicionar Loki stack (logs)
helm install loki grafana/loki-stack \
  --namespace monitoring \
  -f loki-values.yaml  # configuração S3, retenção, etc.

# Adicionar Tempo (traces)
helm install tempo grafana/tempo-distributed \
  --namespace monitoring \
  -f tempo-values.yaml

# Adicionar OTel Operator
helm install opentelemetry-operator \
  open-telemetry/opentelemetry-operator \
  --namespace monitoring \
  --set manager.featureGates=EnableMultiInstrumentation

# Resultado: LGTM stack completo
# L = Loki (logs)     → Grafana datasource: Loki
# G = Grafana (UI)    → dashboards + alerts
# T = Tempo (traces)  → Grafana datasource: Tempo (trace-to-logs via Loki)
# M = Mimir/Prometheus → Grafana datasource: Prometheus-compatible

ServiceMonitor — Scraping Automático de Aplicações

O Prometheus Operator usa ServiceMonitor CRDs para configurar scraping de métricas. Um ServiceMonitor seleciona Services via labels e configura o endpoint de scraping — sem editar o ConfigMap do Prometheus manualmente. O Operator reconcilia continuamente: qualquer ServiceMonitor novo é automaticamente descoberto e adicionado à configuração do Prometheus.

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: order-service
  namespace: default
  labels:
    # Este label deve matchear o seletor do Prometheus
    release: kube-prometheus-stack
spec:
  selector:
    matchLabels:
      app: order-service  # seleciona Services com este label
  endpoints:
    - port: http         # nome da porta no Service
      path: /metrics     # endpoint de métricas
      interval: 30s
      # Adicionar labels ao scrape para enriquecimento
      relabelings:
        - sourceLabels: [__meta_kubernetes_pod_name]
          targetLabel: pod
        - sourceLabels: [__meta_kubernetes_namespace]
          targetLabel: namespace

---
# PrometheusRule — alertas declarativos via CRD (em vez de editar config do Alertmanager)
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: order-service-alerts
  labels:
    release: kube-prometheus-stack
spec:
  groups:
    - name: order-service
      rules:
        - alert: OrderServiceHighErrorRate
          expr: |
            rate(http_requests_total{service="order-service", code=~"5.."}[5m])
            / rate(http_requests_total{service="order-service"}[5m]) > 0.01
          for: 5m
          labels:
            severity: page
          annotations:
            summary: "Order service error rate > 1%"

Debugging de Pods em Produção

kubectl debug — Container Efêmero

Containers efêmeros permitem adicionar um container de debugging a um pod em execução sem reiniciá-lo — útil quando a imagem do pod não tem ferramentas de debugging (distroless, scratch). O container efêmero compartilha o namespace de rede e processo do pod.

# Adicionar container de debugging ao pod em execução
kubectl debug -it pod/order-service-abc123 \
  --image=nicolaka/netshoot \
  --target=order-service  # compartilha namespace de processo

# Dentro do netshoot: inspecionar conexões de rede do pod
# ss -tlnp    → sockets TCP abertos
# tcpdump -i eth0 port 80 -A   → capturar tráfego HTTP
# dig order-service.default.svc.cluster.local → DNS resolution
# curl -v http://payment-service:8080/health  → testar conectividade

# Debug de imagem distroless — criar pod cópia com shell
kubectl debug pod/order-service-abc123 \
  -it --copy-to=debug-pod \
  --set-image=order-service=busybox \
  --share-processes

# Port-forward para acessar pprof endpoint (Go)
kubectl port-forward pod/order-service-abc123 6060:6060
# go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30

# Acessar endpoint de profiling Pyroscope de uma aplicação (.NET)
kubectl port-forward pod/order-service-abc123 4040:4040

Checklist de Debugging de Pod

# 1. Status geral
kubectl get pod <pod> -o wide  # status, nó, IP, restarts

# 2. Eventos do pod — causa de problemas de scheduling ou crash
kubectl describe pod <pod> | grep -A 20 Events

# 3. Logs atuais e anteriores (se o container foi reiniciado)
kubectl logs <pod> --tail=100
kubectl logs <pod> --previous  # logs do container anterior

# 4. Recursos consumidos vs limits
kubectl top pod <pod> --containers

# 5. Variáveis de ambiente e configuração
kubectl exec <pod> -- env | sort
kubectl exec <pod> -- cat /etc/config/app.yaml  # se ConfigMap montado

# 6. Conectividade de rede
kubectl exec <pod> -- curl -v http://<service>:<port>/health
kubectl exec <pod> -- nslookup <service-name>  # DNS resolution

# 7. Checar se o problema é o nó (comparar com outros pods no mesmo nó)
kubectl get pods -o wide | grep <node-name>

# Diagnóstico por exit code:
# 0   → processo saiu normalmente (liveness probe mal configurado?)
# 1   → erro genérico na aplicação
# 137 → OOMKilled (SIGKILL, sinal 9)
# 143 → SIGTERM — graceful termination (rolling update, eviction)

Decisões de Engenharia

kube-prometheus-stack vs componentes individuais?

kube-prometheus-stack é quase sempre a escolha certa — inclui Prometheus Operator, Grafana pré-configurado com dashboards para Kubernetes, AlertManager, kube-state-metrics e node-exporter, com CRDs para ServiceMonitor e PrometheusRule. Instalar individualmente faz sentido apenas quando você tem requisitos específicos que o stack não suporta (ex: integração com Datadog ou New Relic como backend). O tempo economizado na configuração inicial é significativo — dashboards de K8s pré-criados, alertas padrão incluídos.

OTel Operator auto-instrumentation vs SDK manual?

Auto-instrumentation é ótima para adoção rápida em times grandes — você instrumenta todos os services de uma linguagem sem alterar os deployments. A limitação: cobertura de frameworks é mais limitada que o SDK completo, e você não tem controle sobre spans de lógica de negócio. SDK manual: mais trabalho, mais controle, melhor para customização de atributos e spans específicos. Estratégia mista: auto-instrumentation para cobertura baseline de HTTP/DB, SDK manual para spans de negócio (checkout, payment) onde os atributos importam.

Prometheus local vs Thanos/Mimir para retenção longa?

Prometheus local: 15-30 dias máximo (TSDB não é otimizado para longo prazo, memória cresce linearmente com séries ativas). Para retenção longa: Thanos ou Mimir como remote storage (escalam para anos com S3, query deduplication entre réplicas). Regra prática: dashboards e alertas (30 dias) ficam no Prometheus local; análise de capacidade e tendências históricas ficam no Thanos/Mimir. kube-prometheus-stack facilita configurar remote_write para Mimir com poucas linhas no values.yaml.

Como detectar OOMKilled antes que vire incidente?

Acompanhar working set memory vs limit via cAdvisor: container_memory_working_set_bytes / container_spec_memory_limit_bytes. Alertar quando > 80% por > 10 minutos — o container está prestes a ser OOMKilled. Também monitorar a tendência: se a memória está crescendo linearmente há horas, é um leak que vai causar OOMKill eventualmente. Use predict_linear(container_memory_working_set_bytes[1h], 3600) para prever quando o limite será atingido. Correlacione com profiling de memória (Pyroscope ou dotnet-dump) para identificar o alocador responsável antes do crash.

Perguntas de Entrevista

    Qual a diferença entre kube-state-metrics e node-exporter? Quando você usaria cada um?

    kube-state-metrics expõe métricas sobre o estado lógico dos objetos Kubernetes: quantas réplicas um Deployment deseja vs tem disponíveis, qual o status de um Pod (Running/Pending/Failed), se um Node tem a condição Ready=True, etc. É a API do Kubernetes refletida como métricas Prometheus. Use para alertas de "algum pod está em CrashLoopBackOff?" ou "o HPA atingiu o limite de réplicas?".

    node-exporter expõe métricas do sistema operacional de cada nó: CPU por modo (user, system, iowait, steal), memória disponível vs total, I/O de disco, latência de disco, tráfego de rede. Não conhece conceitos Kubernetes — é agnóstico ao orquestrador. Use para alertas de "o nó está com CPU a 95%" ou "o disco está quase cheio" ou "há CPU steal alto indicando vizinhos barulhentos".

    Os dois são complementares e necessários: kube-state-metrics para o que o Kubernetes está tentando fazer, node-exporter para o que o hardware está suportando. Em um incidente de latência, você consulta ambos: KSM para ver se pods foram evicted ou reiniciados, node-exporter para ver se o nó estava sob pressão de CPU ou memória no momento do incidente. cAdvisor (via Kubelet) completa o quadro com consumo real por container.

    O que é um container efêmero no Kubernetes e como você o usaria para debugging?

    Um container efêmero (kubectl debug) é um container adicionado a um pod já em execução, sem reiniciá-lo. Ele compartilha o namespace de rede e pode compartilhar o namespace de processo com os containers existentes (flag --target). É usado quando a imagem do pod não tem ferramentas de debugging — ex: imagens distroless baseadas em scratch, sem shell, sem curl, sem netstat — mas você precisa investigar um problema em produção sem alterar o deployment.

    Uso prático: kubectl debug -it pod/order-service-xyz --image=nicolaka/netshoot --target=order-service. O container netshoot tem ferramentas de rede (tcpdump, curl, ss, nslookup, dig, iperf3) e você pode inspecionar as conexões de rede do pod, fazer DNS resolution, capturar tráfego HTTP, ou até conectar ao processo via strace se o namespace de processo for compartilhado. Após terminar o debug, o container efêmero é descartado automaticamente quando a sessão termina — não persiste no pod.

    Limitação: containers efêmeros não podem ser removidos uma vez adicionados (são imutáveis), e não têm volumes do pod a não ser que você monte explicitamente. Para investigar o sistema de arquivos da aplicação, use --copy-to para criar um pod cópia com uma imagem diferente.

    Como você investigaria por que um pod está em CrashLoopBackOff?

    Em ordem de diagnóstico: (1) kubectl describe pod <pod> — seção Events mostra o motivo do crash (OOMKilled? Exit code específico? Liveness probe failed? FailedScheduling?); (2) kubectl logs <pod> --previous — logs do container antes do crash atual; sem --previous, você vê logs do container atual que pode ter acabado de iniciar e ainda não logou o erro; (3) o Exit code revela a causa: 137 = OOMKilled (SIGKILL), 143 = SIGTERM (graceful termination), 1 = erro na aplicação, 0 = processo saiu normalmente (possível liveness probe mal configurado); (4) kubectl top pod <pod> --containers — se memory working set estava perto do limit antes do crash, é OOMKill iminente; (5) se o crash é rápido demais para capturar logs, aumente o initialDelaySeconds do liveness probe temporariamente, ou use startupProbe para dar mais tempo de inicialização antes das probes começarem; (6) verificar se há limite de CPU muito restritivo causando timeout nas probes — container_cpu_cfs_throttled_seconds_total.

    Como você desenharia uma estratégia de observabilidade para um cluster Kubernetes com 50 teams e 300 microservices?

    O desafio nessa escala é isolamento de tenants com acesso self-service. Estratégia em camadas: (1) Métricas — Mimir multi-tenant com tenant ID por namespace ou time; Prometheus por namespace ou Prometheus federado com remote_write para Mimir; ServiceMonitor com label de ownership; Grafana com datasource por tenant usando variável de tenant ID. (2) Logs — Loki com multi-tenancy habilitado; Fluent Bit DaemonSet injetando label de namespace/team em todo log; LogQL com mandatory label filter por tenant. (3) Traces — Tempo com multi-tenancy; OTel Collector com routing por tenant baseado em resource attribute k8s.namespace.name; Instrumentation CRD por namespace. (4) Dashboards — Grafana Org por large team ou folder por namespace; provisioning via GitOps (Grafonnet/Terraform); dashboards padrão de golden signals pré-criados para qualquer ServiceMonitor. (5) Alertas — PrometheusRule CRDs em namespace do team; Alertmanager com routing por label de team para o canal Slack do time; alertas de infra centralizados no time de platform.

    A chave é self-service com guardrails: os teams criam seus próprios ServiceMonitors e PrometheusRules via GitOps, mas o platform team define os limites de cardinality (via relabeling drop em KSM) e as cotas de storage por tenant no Mimir.

    Explique como funciona tecnicamente a auto-instrumentação via OTel Operator e mutating webhook.

    O OTel Operator registra um MutatingAdmissionWebhook no Kubernetes. Quando um pod é criado em um namespace onde um Instrumentation CRD está configurado, o API server envia o request de criação do pod para o webhook antes de persistir no etcd. O webhook inspeciona as annotations do pod: se encontrar instrumentation.opentelemetry.io/inject-dotnet: "default/otel-instrumentation", ele modifica o spec do pod antes de retornar ao API server.

    As modificações injetadas: (1) um initContainer que copia os binários do agente (ex: OpenTelemetry.AutoInstrumentation para .NET) para um volume compartilhado via EmptyDir; (2) variáveis de ambiente no container da aplicação: OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME (derivado do label app), CORECLR_ENABLE_PROFILING=1, CORECLR_PROFILER e CORECLR_PROFILER_PATH apontando para o agente copiado pelo initContainer; (3) o volume EmptyDir montado no container. A imagem Docker original não é alterada — toda a instrumentação vive no ambiente de execução do pod criado pelo Kubernetes.

    Limitações: a auto-instrumentação não cobre spans de lógica de negócio customizada, e frameworks não suportados pela biblioteca de auto-instrumentação ficam sem cobertura. Para Go, não há suporte de auto-instrumentation (Go requer compilation-time instrumentation) — o SDK manual é obrigatório.

Como Praticar

  1. Instale kube-prometheus-stack em um cluster local (kind ou k3d) e verifique que kube-state-metrics e node-exporter já estão disponíveis. Abra o Grafana, explore os dashboards pré-criados de Kubernetes. Crie um Deployment de uma aplicação simples expondo /metrics, escreva um ServiceMonitor e confirme que as métricas aparecem como alvo no Prometheus (/targets).
    Critério: ServiceMonitor visible no Prometheus targets como UP; métrica customizada da aplicação consultável no Grafana; pelo menos um alerta disparado via PrometheusRule CRD.
  2. Configure kube-event-exporter para exportar Events do Kubernetes para Loki. Depois, force um OOMKilled criando um container com memory limit de 32Mi que aloca memória além do limite. Escreva uma LogQL query que encontre todos os OOMKilled events nas últimas 24h, agrupados por namespace.
    Critério: Events chegando no Loki com campos JSON parsed; query {app="kube-event-exporter"} | json | reason="OOMKilled" retornando o evento correto; correlação do timestamp do event com a métrica container_memory_working_set_bytes no Grafana.
  3. Simule um CrashLoopBackOff criando um pod que falha na inicialização (exit code 1 após 2 segundos). Siga o checklist de debugging: events, logs --previous, exit code, resources. Depois force um OOMKill (exit code 137) com outro pod. Documente a diferença no diagnóstico entre os dois cenários.
    Critério: Root cause identificado corretamente em cada cenário usando apenas kubectl (sem olhar o código-fonte do pod); diferença entre exit code 1 e 137 explicada com evidências dos logs/events.
  4. Instale o OTel Operator e configure um Instrumentation CRD para uma aplicação em .NET ou Python. Anote o Deployment com a annotation de auto-instrumentation. Confirme que traces aparecem no Tempo/Jaeger com os resource attributes k8s.pod.name, k8s.namespace.name e k8s.deployment.name enriquecidos pelo k8sattributes processor.
    Critério: Traces visíveis com atributos K8s corretos; link trace-to-logs funcionando no Grafana (clicar no trace abre os logs Loki do pod correspondente pelo timestamp); nenhuma modificação na imagem Docker da aplicação.
  5. Configure um alerta de OOMKill iminente usando container_memory_working_set_bytes / container_spec_memory_limit_bytes > 0.80 como PrometheusRule, com um for de 10 minutos. Valide que o alerta dispara antes do OOMKill acontecer. Como bônus, adicione um segundo alerta usando predict_linear para prever quando o limite será atingido.
    Critério: PrometheusRule criada via CRD (sem editar configmap do Prometheus); alerta aparece no Alertmanager como firing; alerta dispara pelo menos 5 minutos antes do OOMKill real ocorrer.

Referências

  1. docs kube-state-metrics — Exposed Metrics Documentação oficial listando todas as métricas expostas por KSM, agrupadas por objeto Kubernetes (pod, deployment, node, HPA, PVC). Referência essencial para construir alertas.
  2. docs Prometheus node_exporter — Collectors Lista de todos os collectors do node-exporter com as métricas expostas por cada um: CPU, memory, filesystem, network, diskstats. Inclui flags para habilitar collectors opcionais.
  3. docs OpenTelemetry Operator — Getting Started Documentação do OTel Operator: instalação, OpenTelemetryCollector CRD, Instrumentation CRD por linguagem, configuração de auto-instrumentation e modos de deployment (DaemonSet, Deployment, Sidecar).
  4. docs kube-prometheus-stack — Helm Chart README Chart da prometheus-community: valores configuráveis, integração com Grafana, configuração de storage, alertas padrão incluídos, e como customizar ServiceMonitor selectors.
  5. docs Kubernetes — Ephemeral Containers Documentação oficial do kubectl debug com containers efêmeros: uso com imagens distroless, compartilhamento de namespace de processo, limitações de volumes e mutability.
  6. docs Prometheus Operator — Design & CRDs Arquitetura do Prometheus Operator, especificação dos CRDs ServiceMonitor, PodMonitor, PrometheusRule e Alertmanager. Inclui exemplos de relabeling e seleção de targets.
  7. docs OTel — k8sattributes Processor Documentação do processor que enriquece spans/metrics/logs com metadados Kubernetes via API do cluster: pod name, namespace, node, deployment, labels e annotations customizadas.
  8. article Grafana — LGTM Stack: Observability for Kubernetes Guia da Grafana Labs sobre a stack LGTM completa (Loki, Grafana, Tempo, Mimir) para Kubernetes: arquitetura de referência, integração entre componentes, correlação entre sinais.
  9. blog Brendan Gregg — Linux Performance Blog de Brendan Gregg com análise profunda de métricas de performance Linux — CPU steal, iowait, disk I/O — base conceitual para interpretar as métricas do node-exporter em contextos de cloud.
  10. article CNCF — Observability Whitepaper Whitepaper do CNCF TAG Observability cobrindo as três dimensões (logs, métricas, traces) aplicadas a sistemas cloud-native e Kubernetes: maturidade, padrões, ferramentas recomendadas.
  11. docs Kubernetes — Events API Reference Referência da API de Events do Kubernetes: campos disponíveis (reason, message, involvedObject, type), retenção padrão de 1 hora, e como usar field selectors para filtrar eventos.
  12. book Hightower, Burns, Beda — Kubernetes: Up and Running (3ª ed.) O'Reilly. Capítulos de debugging e observability cobrem containers efêmeros, logs, resource limits e estratégias de monitoramento para clusters em produção. Base para as perguntas de entrevista.