Минимальный observability-стек на open-source за день: logs, metrics, traces
В каждый новый кластер k8s через две недели после старта приходит вопрос: «а почему этот сервис тормозит ночью?». И ты понимаешь, что ничего не видишь — ни логов, ни метрик, ни трейсов. Можно подписаться на платный SaaS за $5 на хост в месяц, а можно за день поднять open-source стек, который покрывает 95% потребностей. Покажу, что я ставлю и как это конфигурирую.
Версия — k8s 1.30, Helm-чарты на момент середины 2026.
Что в стеке
Три столпа observability — logs, metrics, traces. Под каждый — отдельная база и отдельный сборщик. Я беру:
- Метрики: Prometheus + node-exporter + kube-state-metrics. Хранение — Mimir или просто локальный TSDB в Prometheus с retention.
- Логи: Loki + Promtail (или Vector / Alloy). Хранение — S3-совместимый object storage.
- Трейсы: Tempo + OpenTelemetry Collector. Хранение — тоже S3.
- Визуализация: Grafana (одна на всех).
Это всё от Grafana Labs, оно отлично интегрировано между собой и не требует extra-обвязки. Альтернатива — связка Prometheus + Elastic + Jaeger, но там больше движущихся частей.
Поднимаем за день
Шаг 1: object storage
Loki и Tempo пишут чанки в S3. В облаке берёшь S3, в on-prem — MinIO. Поднимаешь bucket, лучше два — отдельный для логов и для трейсов.
aws s3 mb s3://company-loki-chunks --region eu-central-1
aws s3 mb s3://company-tempo-blocks --region eu-central-1Если есть отдельный bucket для метрик через Mimir — ещё один. Для маленького кластера хватит и одного на всё, разделять по prefix.
Шаг 2: Helm и kube-prometheus-stack
Базу metric-stack ставим одним чартом:
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install monitoring \
prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace \
--values monitoring-values.yamlВ monitoring-values.yaml я ставлю минимум:
prometheus:
prometheusSpec:
retention: 30d
retentionSize: 50GB
storageSpec:
volumeClaimTemplate:
spec:
storageClassName: gp3
resources:
requests:
storage: 100Gi
resources:
requests:
cpu: 500m
memory: 2Gi
limits:
memory: 4Gi
grafana:
adminPassword:
persistence:
enabled: true
size: 10Gi
ingress:
enabled: true
hosts:
- grafana.example.comПолучаешь Prometheus (с node-exporter, kube-state-metrics, операторами) и Grafana с готовыми дашбордами.
Шаг 3: Loki
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
helm install loki grafana/loki \
--namespace monitoring \
--values loki-values.yamlВ loki-values.yaml:
loki:
schemaConfig:
configs:
- from: "2024-04-01"
store: tsdb
object_store: s3
schema: v13
index:
prefix: loki_index_
period: 24h
storage:
type: s3
s3:
region: eu-central-1
bucketnames: company-loki-chunks
limits_config:
retention_period: 30d
deploymentMode: SimpleScalable
backend:
replicas: 2
read:
replicas: 2
write:
replicas: 3SimpleScalable — это middle ground между monolithic (для совсем маленьких) и distributed (для крупных). Хорошо подходит для кластеров до пары десятков нод.
Дальше — сборщик логов. Я беру Promtail или Alloy (новый Grafana-агент).
# promtail-values.yaml
config:
clients:
- url: http://loki-gateway/loki/api/v1/push
snippets:
extraScrapeConfigs: |
- job_name: kubernetes-pods
kubernetes_sd_configs:
- role: pod
# ... стандартные relabels из чартаШаг 4: Tempo
helm install tempo grafana/tempo-distributed \
--namespace monitoring \
--values tempo-values.yamltempo-values.yaml:
storage:
trace:
backend: s3
s3:
bucket: company-tempo-blocks
endpoint: s3.eu-central-1.amazonaws.com
region: eu-central-1
retention: 168h # 7 дней
ingester:
replicas: 3
distributor:
replicas: 2
querier:
replicas: 2Перед Tempo стоит OpenTelemetry Collector, в который льют трейсы все приложения по OTLP-протоколу.
helm install otel-collector \
open-telemetry/opentelemetry-collector \
--namespace monitoring \
--values otel-values.yamlotel-values.yaml:
mode: deployment
config:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
exporters:
otlp:
endpoint: tempo-distributor:4317
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
exporters: [otlp]Шаг 5: подключаем datasources в Grafana
В values kube-prometheus-stack:
grafana:
additionalDataSources:
- name: Loki
type: loki
url: http://loki-gateway.monitoring.svc:80
access: proxy
- name: Tempo
type: tempo
url: http://tempo-query-frontend.monitoring.svc:3100
access: proxyПосле apply Grafana показывает все три источника, можно перепрыгивать с метрики на логи и на трейс одним кликом — это и есть «correlated observability», ради которого всё это собирается.
Инструментация приложений
Стек подняли, но пока пустой. Дальше — научить приложения отдавать данные.
Метрики Prometheus
В Go это prometheus/client_golang, эндпоинт /metrics:
import "github.com/prometheus/client_golang/prometheus/promhttp"
mux.Handle("/metrics", promhttp.Handler())А в k8s deployment:
spec:
template:
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/metrics"Лучше использовать ServiceMonitor (CRD из prometheus-operator) — это типизированный вариант, без аннотаций.
Структурированные логи
JSON-логи в stdout, дальше Promtail/Alloy подхватит. В Go это slog:
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
logger.Info("request handled",
"method", r.Method,
"path", r.URL.Path,
"duration_ms", time.Since(start).Milliseconds(),
)Loki хорошо парсит JSON и даёт LogQL для фильтрации:
{namespace="prod", app="api"} | json | duration_ms > 500Трейсы через OTLP
В Go — go.opentelemetry.io/otel:
import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/sdk/trace"
)
exporter, _ := otlptracegrpc.New(ctx,
otlptracegrpc.WithEndpoint("otel-collector.monitoring.svc:4317"),
otlptracegrpc.WithInsecure())
tp := trace.NewTracerProvider(trace.WithBatcher(exporter))
otel.SetTracerProvider(tp)Дальше в обработчиках оборачиваешь критичные участки в spans, и они появляются в Tempo.
Что я ставлю в обязательном минимуме
Дашборды:
- Из kube-prometheus-stack — Cluster, Nodes, Pods, Workloads. Это уже идёт коробочно.
- Kubernetes Views: Pods (15760) — детальный по поду.
- Дашборд per-сервис — копируешь шаблон, меняешь labels.
Алёрты:
node memory > 90%— кончается RAM на ноде.pod restart count > 5 per hour— что-то постоянно падает.http_5xx_rate > 1%— конкретный сервис ломается.certmanager_certificate_expiry_seconds < 7d— сертификат не обновляется.
Сноска про затраты
На небольшом кластере (10 нод, 200 pods) этот стек съедает примерно:
- ~6 vCPU и ~16 ГБ RAM суммарно (Prometheus, Loki, Tempo, Grafana).
- ~50 ГБ S3 в месяц на логи/трейсы при retention 30/7 дней.
Это $30-50 в месяц в облаке, плюс компьют на нодах. Дешевле любого managed-сервиса того же охвата.
Что запомнить
Один день — реально, если без избыточной кастомизации. Берёшь kube-prometheus-stack, Loki, Tempo, OpenTelemetry Collector. Object storage в S3 для chunks. Один Grafana с тремя datasources. Дальше учишь приложения отдавать /metrics, JSON-логи и OTLP-трейсы. Через неделю поймёшь, что без этого жить нельзя.
Куда копать: prometheus-community helm charts, документация Grafana Labs по Loki и Tempo, OpenTelemetry. Если нужен long-term storage метрик — Mimir или Thanos.