September 24

Пример настройки стека мониторинга: Prometheus, Loki, Grafana, Promtail и Tempo

Специально для телеграм-канала Мониторим ИТ. Это перевод оригинальной статьи.

Наблюдаемость — это уже не роскошь, а необходимость. В этой статье рассказано, как настроить полноценный стек мониторинга с открытым исходным кодом для приложения на базе Node.js (NestJS) с использованием Prometheus, Loki, Grafana, Promtail и Tempo. В итоге вы получите полную observability-модель: метрики, логи и трассировки — всё на одном дашборде.

👀 Почему мониторинг важен в любой системе

«Если вы не можете что-то измерить, вы не можете это улучшить». — Питер Друкер

Современные приложения сложны. Они работают в контейнерах, динамически масштабируются и часто зависят от множества внутренних сервисов. В таких средах наблюдаемость — не роскошь, а залог выживания. Вот почему мониторинг имеет решающее значение для каждой серьезной системы.

🧰 The Stack: что мы настраиваем

Мы настроим полностью открытый стек мониторинга, используя:

📁 Структура папки:

monitoring/
├── prometheus/prometheus.yml
├── grafana/provisioning/...
├── loki/config.yaml
├── tempo/tempo-config.yaml
├── promtail/promtail-config.yaml
└── docker-compose.yml
Готовый шаблон со всеми файлами конфигурации можно найти в репозитории GitHub:
👉 miladezzat/monitoring

🐳 Настройка Docker Compose

version: '3.7'

services:
  prometheus:
    image: prom/prometheus
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"

  grafana:
    image: grafana/grafana
    ports:
      - "3001:3000"
    volumes:
      - grafana-storage:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning

  loki:
    image: grafana/loki
    ports:
      - "3100:3100"
    volumes:
      - ./loki/config.yaml:/etc/loki/config.yaml

  promtail:
    image: grafana/promtail
    volumes:
      - ./promtail/promtail-config.yaml:/etc/promtail/config.yaml
      - /var/log/nestjs:/var/log/nestjs
    command: -config.file=/etc/promtail/config.yaml

  tempo:
    image: grafana/tempo
    ports:
      - "3200:3200"
    volumes:
      - ./tempo/tempo-config.yaml:/etc/tempo-config.yaml
    command: ["--config.file=/etc/tempo-config.yaml"]

volumes:
  grafana-storage:

📊 Конфигурация Prometheus

# prometheus/prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'nestjs-api'
    static_configs:
      - targets: ['host.docker.internal:3000']
Используйте его host.docker.internal, чтобы разрешить Docker подключаться к вашему локальному серверу NestJS.

📦 Loki для логов

# loki/config.yaml
auth_enabled: false
server:
  http_listen_port: 3100
ingester:
  lifecycler:
    ring:
      kvstore:
        store: inmemory
      replication_factor: 1
schema_config:
  configs:
    - from: 2020-10-24
      store: boltdb-shipper
      object_store: filesystem
      schema: v11
      index:
        prefix: index_
        period: 24h
storage_config:
  boltdb_shipper:
    active_index_directory: /loki/index
    cache_location: /loki/boltdb-cache
    shared_store: filesystem
  filesystem:
    directory: /loki/chunks

📄 Promtail для сбора логов

# promtail/promtail-config.yaml
server:
  http_listen_port: 9080

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
  - job_name: nestjs-logs
    static_configs:
      - targets:
          - localhost
        labels:
          job: nestjs
          __path__: /var/log/nestjs/*.log
Promtail просматривает логи, генерируемые NestJS, и пересылает их в Loki.

🧠 Tempo для трейсов

# tempo/tempo-config.yaml
server:
  http_listen_port: 3200

distributor:
  receivers:
    otlp:
      protocols:
        grpc:
        http:

ingester:
  trace_idle_period: 10s

compactor:
  compaction:
    block_retention: 1h

storage:
  trace:
    backend: local
    local:
      path: /tmp/tempo

📈 Подготовка источника данных Grafana

# grafana/provisioning/datasources/datasource.yml
apiVersion: 1

datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090

  - name: Loki
    type: loki
    access: proxy
    url: http://loki:3100

  - name: Tempo
    type: tempo
    access: proxy
    url: http://tempo:3200
Это позволит автоматически настроить все источники данных при загрузке Grafana.

🧪 Интеграция NestJS

1. Сбор логов с Promtail

// main.ts
import * as fs from 'fs';
import * as path from 'path';

const logDir = '/var/log/nestjs';
if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });

const logFile = fs.createWriteStream(path.join(logDir, 'app.log'), { flags: 'a' });

const originalLog = console.log;
console.log = (...args) => {
  originalLog(...args);
  logFile.write(args.join(' ') + '\n');
};  
Теперь все логи отправляются в файл, который отслеживает Promtail.

2. Сбор метрик с Prometheus

Установить:

npm install prom-client

Услуга:

// metrics.service.ts
import { Injectable } from '@nestjs/common';
import { collectDefaultMetrics, Registry, Gauge } from 'prom-client';

@Injectable()
export class MetricsService {
  private readonly registry = new Registry();
  private readonly requestGauge = new Gauge({
    name: 'http_requests_total',
    help: 'Total number of HTTP requests',
    labelNames: ['method', 'route', 'status'],
  });

  constructor() {
    collectDefaultMetrics({ register: this.registry });
  }

  recordRequest(method: string, route: string, status: number) {
    this.requestGauge.labels(method, route, String(status)).inc();
  }

  getMetrics(): string {
    return this.registry.metrics();
  }
}

Контроллер:

@Get('/metrics')
getMetrics(): string {
  return this.metricsService.getMetrics();
}

🚀 Управление

cd monitoring
docker-compose up -d

Доступ:

Все инструменты имеют открытый исходный код, готовы к использованию в боевой среде и легко масштабируются.

Подписывайтесь на телеграм-канал Мониторим ИТ, там еще больше полезной информации!