KubernetesSeniorSystem design

Как ты делаешь production-grade observability в Kubernetes: метрики, логи, трейсы? Какой стек?

Стек: kube-prometheus-stack (Prometheus + Grafana + Alertmanager) для метрик, Loki + Promtail для логов, Tempo + OpenTelemetry Collector для трейсов. Всё интегрируется в Grafana с cross-linking трейсов и логов через trace_id.

Production-grade observability в Kubernetes: метрики, логи, трейсы

Observability строится на трёх столпах: метрики (Prometheus + Grafana), логи (Loki или Elasticsearch) и распределённые трейсы (Tempo или Jaeger). Все три компонента разворачиваются через Helm и интегрируются в единый дашборд Grafana.

Метрики: kube-prometheus-stack

# Устанавливаем kube-prometheus-stack (Prometheus Operator + Grafana + alertmanager)
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \
  --namespace monitoring --create-namespace \
  --set grafana.adminPassword=secretpassword \
  --set prometheus.prometheusSpec.retention=30d \
  --set prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.resources.requests.storage=50Gi
# ServiceMonitor для своего приложения
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: my-app
  namespace: monitoring
  labels:
    release: kube-prometheus-stack   # должен совпадать с selector в Prometheus CR
spec:
  selector:
    matchLabels:
      app: my-app
  namespaceSelector:
    matchNames:
      - production
  endpoints:
    - port: http
      path: /metrics
      interval: 30s

Логи: Grafana Loki + Promtail

# Loki в режиме single binary (для кластеров до ~50 ГБ/сут)
helm install loki grafana/loki \
  --namespace monitoring \
  --set loki.commonConfig.replication_factor=1 \
  --set loki.storage.type=filesystem

# Promtail как DaemonSet — собирает логи со всех нод
helm install promtail grafana/promtail \
  --namespace monitoring \
  --set config.lokiAddress=http://loki:3100/loki/api/v1/push
# Promtail автоматически добавляет k8s-метки через pipeline_stages
# Структурированные JSON-логи парсятся через stage.json:
pipelineStages:
  - json:
      expressions:
        level: level
        msg: message
        trace_id: trace_id
  - labels:
      level:
      trace_id:

Трейсы: Grafana Tempo + OpenTelemetry

# Tempo
helm install tempo grafana/tempo \
  --namespace monitoring \
  --set tempo.storage.trace.backend=local

# OpenTelemetry Collector как DaemonSet
helm install otel-collector open-telemetry/opentelemetry-collector \
  --namespace monitoring \
  --values otel-values.yaml
# otel-values.yaml — collector получает трейсы от приложений и шлёт в Tempo
mode: daemonset
config:
  receivers:
    otlp:
      protocols:
        grpc:
          endpoint: 0.0.0.0:4317
        http:
          endpoint: 0.0.0.0:4318
  exporters:
    otlp:
      endpoint: tempo.monitoring.svc.cluster.local:4317
      tls:
        insecure: true
  service:
    pipelines:
      traces:
        receivers: [otlp]
        exporters: [otlp]

Инструментирование приложения (Python пример)

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace.export import BatchSpanProcessor

provider = TracerProvider()
exporter = OTLPSpanExporter(endpoint="http://otel-collector:4317")
provider.add_span_processor(BatchSpanProcessor(exporter))
trace.set_tracer_provider(provider)

tracer = trace.get_tracer(__name__)

with tracer.start_as_current_span("process-order") as span:
    span.set_attribute("order.id", order_id)
    span.set_attribute("user.id", user_id)
    # бизнес-логика...

Alerting

# PrometheusRule — alertmanager правило
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: my-app-alerts
  namespace: monitoring
spec:
  groups:
    - name: my-app
      rules:
        - alert: HighErrorRate
          expr: |
            sum(rate(http_requests_total{status=~"5.."}[5m]))
            /
            sum(rate(http_requests_total[5m])) > 0.01
          for: 2m
          labels:
            severity: critical
          annotations:
            summary: "Error rate > 1% for 2 minutes"

Связь логов и трейсов через Grafana

В Grafana источник данных Loki настраивается с derivedFields, которые превращают trace_id в кликабельную ссылку в Tempo. Источник Tempo настраивается с traceToLogs, чтобы из трейса открывался связанный лог. Это даёт сквозную навигацию: метрика → лог → трейс.

Подводные камни

  • Cardinality взрывается — метки с высокой уникальностью (user_id, request_id в label) уничтожают Prometheus; такие значения должны быть в exemplars или логах, не в метках.
  • Promtail пропускает логи при ротации файлов — нужно настроить fileGlobbing и убедиться, что hostPath монтирует /var/log/pods.
  • Tempo без объектного хранилища не масштабируется — для production нужен S3/GCS бэкенд, иначе при перезапуске трейсы теряются.
  • kube-state-metrics и node-exporter уже включены в kube-prometheus-stack — не надо их ставить отдельно, иначе будет дублирование метрик и конфликты портов.
  • Retention Prometheus vs. Thanos/Cortex — нативный Prometheus не масштабируется горизонтально; для долгосрочного хранения нужен Thanos sidecar или VictoriaMetrics.
  • OTel Collector bottleneck — при высокой нагрузке один DaemonSet-экземпляр может стать узким местом; надо настраивать batch processor и limits по памяти.
  • Алерты без runbook бесполезны — каждый alert должен иметь аннотацию runbook_url, иначе on-call инженер не знает что делать.

Common mistakes

  • Сводить observability к logs
  • Не упоминать SLO
  • Игнорировать cardinality и retention

What the interviewer is testing

  • Покрывает metrics/logs/traces
  • Знает Kubernetes sources
  • Говорит про alerts и runbooks

Sources

Related topics