# Loki: label-based логи, LogQL, Promtail/Vector pipeline _Observability и мониторинг · LinuxLab Knowledge Base_ **TL;DR:** Loki - log aggregation с label-based индексом (не full-text как Elastic). Дёшево на S3-storage. Promtail/Vector как агенты. LogQL похож на PromQL: фильтр + parse + aggregation. Cardinality - враг. ## Зачем Loki Elasticsearch, full-text search всех полей log'а. Каждое слово индексируется. Стоимость: - 1 TB/день логов = ~30 GB/день RAM на heap (3-5× source size) - $30K/месяц ES cluster vs $5K/месяц для S3-Loki - Indexing latency, реальная боль на >100K events/s **Loki (Grafana Labs, 2018)** перевернул подход: - Индексируется **только labels** (как в [[prometheus-basics|Prometheus]]) - Сам log payload, compressed chunks на S3-compatible storage - Поиск = «выбор stream'ов по labels» + «grep по chunk'ам» - Дёшево, scale почти бесконечный Trade-off: **полно-текстовый поиск медленнее**, но 95% запросов в observability, `{service=X, level=error} |= "timeout"`, что Loki обрабатывает быстро. ## Архитектура ``` ┌─────────┐ push ┌──────────┐ write ┌─────────┐ │ Promtail│ ─────────► │ Loki │ ─────────► │ S3 │ │ (agent) │ │ (server) │ │ (chunks)│ └─────────┘ └──────────┘ └─────────┘ ┌─────────┐ push ▲ ▲ │ Vector │ ────────────────┘ │ └─────────┘ │ ▲ │ │ tail files / journald / docker read │ │ │ ┌────┴────┐ ┌──────┴───┐ │ /var/log│ ◄── │ Grafana │ └─────────┘ └──────────┘ LogQL query ``` Components Loki в кластере: - **distributor**, принимает push, hashing - **ingester**, буферизует chunks в RAM, flush на S3 каждые 10-30 мин - **querier**, читает chunks с S3 + ingester для recent - **query-frontend**, splits большие queries, кэширует - **compactor**, мерджит index'ы, retention enforcement ## Stream и labels **Stream** в Loki = уникальная комбинация labels: ``` {service="api", env="prod", host="node-12", level="info"} ``` Каждый stream, отдельный chunked-file на S3. Внутри stream, log lines в порядке времени. Подобно [[metric-types|Prometheus series]], кардинальность = product unique values каждого label. **>10K активных streams** в одном tenant → деградация. Опасные labels: - `request_id` (миллионы), **никогда** - `user_id`, **никогда**, в payload пиши - `pod_name` (k8s), может быть тысячи, OK с retention - `host`, десятки-сотни, OK - `level`, 4-5 значений, идеально Правило: labels = **низкая кардинальность**, остальное, в log line. ## LogQL, query language PromQL-like, но на логах. **Stream selector** + line-filter: ``` {service="api", env="prod"} |= "error" {service="api"} |~ "timeout|refused" # regex {service="api"} != "healthcheck" # exclude {service=~"api.*"} | json | level="error" # parse JSON ``` **Operators:** | Op | Что делает | |----|------------| | `\|=` | line contains substring | | `\!=` | line not contains | | `\|~` | line matches regex | | `\!~` | line not matches regex | **Parsers** (после `|`): - `json`, парсит JSON, поля доступны как `level`, `user_id`, etc - `logfmt`, для `key=value` логов - `regexp`, `| regexp "(?P\d+)"` - `pattern`, `| pattern "<_> [] "` - `unpack`, для Fluentbit-обёрнутых **Metrics из логов** (Loki как time-series): ``` rate({service="api"} |= "error" [5m]) # error rate в req/s sum by (status)(count_over_time({service="api"} | json [1m])) ``` Это **дёшево**, Loki считает на лету без индекса. Используется в alerting когда метрика отсутствует ([alerting-rules-alertmanager](/kb/alerting-rules-alertmanager.md)). ## Promtail, Loki-native agent Discovers log files, парсит, добавляет labels, пушит: ```yaml scrape_configs: - job_name: system static_configs: - targets: [localhost] labels: job: varlogs __path__: /var/log/*.log - job_name: containers docker_sd_configs: - host: unix:///var/run/docker.sock relabel_configs: - source_labels: ['__meta_docker_container_name'] regex: '/(.*)' target_label: container - source_labels: ['__meta_docker_container_log_stream'] target_label: stream pipeline_stages: - cri: {} - json: expressions: {level: level, msg: msg, trace_id: trace_id} - labels: level: - structured_metadata: trace_id: ``` `pipeline_stages`, преобразование log line. **structured_metadata** (Loki 2.9+), поля без cardinality cost (`trace_id`, `request_id`), searchable но не индексируется как label. Решение проблемы high-card identifiers. ## Vector, alternative agent Vector (Datadog, opensource), более мощный pipeline: ```toml [sources.in] type = "kubernetes_logs" [transforms.parse] type = "remap" inputs = ["in"] source = ''' . = parse_json!(.message) ?? . .level = downcase(string!(.level)) ''' [sinks.loki] type = "loki" inputs = ["parse"] endpoint = "http://loki:3100" labels = {service = "{{ kubernetes.container_name }}", level = "{{ level }}"} remove_label_fields = true ``` Vector умеет: - Multi-sink: одновременно Loki + S3 + Kafka - VRL (Vector Remap Language), JS-like для парсинга - Backpressure handling: disk-buffered queue - Sampling, filtering до отправки Используй Vector если pipeline сложный или нужны несколько backend'ов. ## Retention и storage Loki стоимость почти 100%, S3 storage. Расчёт: - 1 GB/день logs → compressed ~100-200 MB chunks - 90d retention → ~15 GB на S3 → $0.35/мес (S3 standard) - Index ~5% от chunks: $0.02/мес Total: **<$1/мес** для ~100 GB logs. Сравни с Datadog ($1.27/GB/мес). Конфиг retention: ```yaml limits_config: retention_period: 90d compactor: retention_enabled: true retention_delete_delay: 2h ``` ## Sizing rules of thumb - 1 TB/day ingest = 3 ingester + 2 querier + S3 - Ingester RAM ≈ chunks_in_flight × 1.5 MB - Compactor, 1-2 vCPU, не нагружен - Index lookup в querier, fast, бутылочное горлышко обычно chunk-fetch ## Loki vs Elastic vs ClickHouse | Критерий | Loki | Elastic | ClickHouse | |----------|------|---------|------------| | Index | label-only | full-text | columnar | | Storage | S3 (cheap) | local SSD | local/S3 | | Cost @ 1TB/day | ~$5K/мес | ~$30K/мес | ~$10K/мес | | Full-text speed | средне | very fast | fast (with skip-index) | | Aggregations | LogQL metrics | aggregations API | SQL | | Multi-tenancy | yes | через index | через DB | ClickHouse-based (SigNoz, Quickwit), компромисс: дешевле Elastic, быстрее Loki на full-text. Растёт в популярности. ## Когда что-то пошло не так - **Cardinality explosion**, десятки тысяч streams. `loki-canary` показывает active streams. Удали dynamic labels. См. [cardinality-explosion](/kb/cardinality-explosion.md). - **Logs не приходят**, Promtail logs (`journalctl -u promtail`): auth failure, network, disk full в /tmp. - **«too many outstanding requests»**, query frontend rate-limit. Сужай range, добавляй labels selector. - **`entry too far behind`**, log line > `max_line_size` (default 256 KB). Truncate в agent или подними limit. - **Search возвращает 0** хотя логи есть, wrong tenant header (`X-Scope-OrgID`), или label selector не матчится. Проверь `{__path__=~".+"}`. - **Loki OOM на ingester**, chunks_per_user_per_target превышен. Уменьшай interval flush'а или растяни retention в memory. - **Promtail отстаёт от логов**, disk-IO на read; кубер pod logs rotated. Используй Vector с persistent buffer. ## Команды ```bash logcli query '{service="api"} |= "error"' --limit 50 --since 1h ``` Loki CLI - 50 последних error-line за час из service=api ```bash logcli query 'sum by (level)(count_over_time({service="api"} | json [1m]))' --limit 5 ``` Metrics из логов - count by level за минуту, как в Prometheus ```bash curl -s 'http://loki:3100/loki/api/v1/labels' | jq ``` Все labels в системе - первый шаг диагностики cardinality ```bash curl -s 'http://loki:3100/loki/api/v1/label/service/values' | jq ``` Все значения label 'service' - сколько уникальных streams ```bash promtail -config.file=/etc/promtail/config.yaml -log.level=debug ``` Promtail в debug-режиме - видно какие файлы tail и куда push ```bash journalctl -u promtail -n 100 -f ``` Live логи самого Promtail - типичный source ошибок ingestion ```bash vector validate /etc/vector/vector.toml ``` Валидация Vector config до запуска - в CI ## См. также - [Prometheus: scrape, TSDB, PromQL и production-pitfalls](/kb/prometheus-basics.md) - [journalctl - журнал systemd](/kb/cmd-journalctl.md) - [auditd - syscall и file audit](/kb/auditd.md) - [Distributed tracing: span, context propagation, sampling](/kb/tracing-basics.md) - [Cardinality explosion: как убить Prometheus и как чинить](/kb/cardinality-explosion.md)