Перейти к содержимому
Главная страница » Централизованное логирование Docker контейнеров с Loki: Полное руководство

Централизованное логирование Docker контейнеров с Loki: Полное руководство

Содержание

Введение в Loki

Grafana Loki — это система агрегации логов, разработанная специально для работы с метриками и логами в экосистеме Grafana. В отличие от традиционных систем логирования (ELK Stack), Loki не индексирует содержимое логов, а только метки (labels), что делает его значительно легче и быстрее.

Ключевые особенности Loki:

  • ✅ Легковесность — потребляет в 10-100 раз меньше ресурсов, чем ELK
  • ✅ Интеграция с Grafana — единый интерфейс для логов и метрик
  • ✅ Простота — минимальная конфигурация
  • ✅ Масштабируемость — горизонтальное масштабирование
  • ✅ Экономичность — низкие требования к ресурсам

Когда использовать Loki:

  • Сбор логов из Docker контейнеров
  • Централизованное логирование микросервисов
  • Интеграция с существующим стеком мониторинга Grafana
  • Необходимость быстрого поиска по логам
  • Ограниченные ресурсы сервера

Почему Loki для Docker?

Проблемы традиционного логирования Docker:

  1. Разрозненные логи — каждый контейнер пишет в свой файл
  2. Сложный поиск — нужно знать, на какой ноде находится контейнер
  3. Высокое потребление ресурсов — ELK Stack требует много памяти
  4. Сложная настройка — множество компонентов для конфигурации

Преимущества Loki для Docker:

  • ✅ Автоматический сбор — Promtail собирает логи всех контейнеров
  • ✅ Единый интерфейс — все логи в одном месте (Grafana)
  • ✅ Низкое потребление ресурсов — ~100-200MB RAM для небольших установок
  • ✅ Простая интеграция — работает «из коробки» с Docker

Архитектура Loki + Docker

┌─────────────────────────────────────────────────────────────┐
│                    DOCKER CONTAINERS                         │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐                 │
│  │  Bot 1   │  │  Bot 2   │  │  Bot N   │                 │
│  │ (stdout) │  │ (stdout) │  │ (stdout) │                 │
│  └────┬─────┘  └────┬─────┘  └────┬─────┘                 │
│       │            │              │                         │
│       └────────────┴──────────────┘                        │
│                    │                                        │
│                    ▼                                        │
│       ┌────────────────────────┐                            │
│       │   Docker Logs         │                            │
│       │   (/var/lib/docker/   │                            │
│       │    containers/)       │                            │
│       └───────────┬────────────┘                            │
└───────────────────┼────────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────────────────────────┐
│                    PROMTAIL (Log Shipper)                   │
│  • Собирает логи из Docker                                  │
│  • Добавляет метки (labels)                                 │
│  • Отправляет в Loki                                        │
└───────────────────┬────────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────────────────────────┐
│                    LOKI (Log Aggregator)                    │
│  • Хранит логи                                              │
│  • Индексирует по меткам                                    │
│  • Предоставляет API для запросов                           │
└───────────────────┬────────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────────────────────────┐
│                    GRAFANA (Visualization)                   │
│  • Отображение логов                                        │
│  • Поиск и фильтрация                                       │
│  • Интеграция с метриками                                    │
└─────────────────────────────────────────────────────────────┘

Установка и настройка

Шаг 1: Docker Compose конфигурация

Создайте файл docker-compose.loki.yml:

version: '3.8'

services:
  # Loki - агрегатор логов
  loki:
    image: grafana/loki:latest
    container_name: loki
    ports:
      - "3100:3100"
    command: -config.file=/etc/loki/local-config.yaml
    volumes:
      - loki-data:/loki
      - ./loki-config.yml:/etc/loki/local-config.yaml:ro
    networks:
      - monitoring
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3100/ready"]
      interval: 10s
      timeout: 5s
      retries: 3

  # Promtail - сборщик логов
  promtail:
    image: grafana/promtail:latest
    container_name: promtail
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
      - ./promtail-config.yml:/etc/promtail/config.yml:ro
    command: -config.file=/etc/promtail/config.yml
    networks:
      - monitoring
    restart: unless-stopped
    depends_on:
      - loki

  # Grafana - визуализация
  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
      - GF_USERS_ALLOW_SIGN_UP=false
    volumes:
      - grafana-data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning:ro
    networks:
      - monitoring
    restart: unless-stopped
    depends_on:
      - loki

volumes:
  loki-data:
    driver: local
  grafana-data:
    driver: local

networks:
  monitoring:
    driver: bridge

Шаг 2: Конфигурация Loki

Создайте файл loki-config.yml:

auth_enabled: false

server:
  http_listen_port: 3100
  grpc_listen_port: 9096

common:
  path_prefix: /loki
  storage:
    filesystem:
      chunks_directory: /loki/chunks
      rules_directory: /loki/rules
  replication_factor: 1
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory

schema_config:
  configs:
    - from: 2020-10-24
      store: boltdb-shipper
      object_store: filesystem
      schema: v11
      index:
        prefix: index_
        period: 24h

ruler:
  alertmanager_url: http://localhost:9093

# Ограничения для оптимизации
limits_config:
  reject_old_samples: true
  reject_old_samples_max_age: 168h  # 7 дней
  ingestion_rate_mb: 16
  ingestion_burst_size_mb: 32
  max_query_length: 721h
  max_query_parallelism: 32
  max_streams_per_user: 10000
  max_line_size: 256KB

Шаг 3: Конфигурация Promtail

Создайте файл promtail-config.yml:

server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /tmp/positions.yaml

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

scrape_configs:
  # Сбор логов из Docker контейнеров
  - job_name: docker
    docker_sd_configs:
      - host: unix:///var/run/docker.sock
        refresh_interval: 5s
        filters:
          - name: status
            values: ["running"]
    
    relabel_configs:
      # Имя контейнера
      - source_labels: ['__meta_docker_container_name']
        regex: '/(.*)'
        target_label: 'container_name'
      
      # ID контейнера
      - source_labels: ['__meta_docker_container_id']
        target_label: 'container_id'
      
      # Образ контейнера
      - source_labels: ['__meta_docker_container_label_com_docker_compose_service']
        target_label: 'service'
      
      # Проект Docker Compose
      - source_labels: ['__meta_docker_container_label_com_docker_compose_project']
        target_label: 'project'
      
      # Имя образа
      - source_labels: ['__meta_docker_container_label_com_docker_compose_image']
        target_label: 'image'
      
      # Путь к логам
      - source_labels: ['__meta_docker_container_log_path']
        target_label: 'log_path'
        replacement: '/var/lib/docker/containers/*/${__meta_docker_container_id}*.log'
      
      # Job name
      - target_label: 'job'
        replacement: 'docker'

    pipeline_stages:
      # Парсинг JSON логов (если приложение пишет JSON)
      - json:
          expressions:
            output: log
            stream: stream
            attrs: attrs
      
      # Извлечение timestamp
      - timestamp:
          source: time
          format: RFC3339Nano
      
      # Добавление меток из JSON
      - labels:
          stream:
          level:
          service:
      
      # Вывод лога
      - output:
          source: output

Шаг 4: Запуск

# Запустить стек Loki
docker-compose -f docker-compose.loki.yml up -d

# Проверить статус
docker-compose -f docker-compose.loki.yml ps

# Просмотр логов
docker-compose -f docker-compose.loki.yml logs -f loki

Сбор логов с Docker контейнеров

Автоматический сбор

Promtail автоматически обнаруживает все запущенные Docker контейнеры и собирает их логи. Никакой дополнительной настройки не требуется!

Метки (Labels) в Loki

Loki использует метки для индексации логов. Основные метки:

  • container_name — имя контейнера
  • container_id — ID контейнера
  • service — сервис из Docker Compose
  • project — проект Docker Compose
  • image — образ контейнера
  • stream — stdout или stderr

Пример запроса в Grafana

{container_name="bot-1"} |= "error"

Этот запрос найдет все логи контейнера bot-1, содержащие слово «error».

Интеграция с Grafana

Шаг 1: Добавление Loki как источника данных

  1. Откройте Grafana: http://localhost:3000
  2. Войдите (admin/admin)
  3. Перейдите в Configuration → Data Sources
  4. Нажмите Add data source
  5. Выберите Loki
  6. Укажите URL: http://loki:3100
  7. Нажмите Save & Test

Шаг 2: Создание дашборда

Создайте новый дашборд и добавьте панель с логами:

Query:

{container_name=~".+"} |= "error" | json

Visualization: Logs

Шаг 3: Полезные запросы LogQL

Поиск ошибок в конкретном контейнере:

{container_name="my-bot"} |= "error"

Поиск по уровню логирования:

{container_name="my-bot"} | json | level="ERROR"

Поиск за последний час:

{container_name="my-bot"} |= "error" [1h]

Подсчет ошибок:

sum(count_over_time({container_name="my-bot"} |= "error" [5m]))

Топ контейнеров по количеству логов:

topk(10, sum by (container_name) (count_over_time({container_name=~".+"}[5m])))

Продвинутые настройки

Фильтрация логов

Исключите ненужные контейнеры из сбора:

# promtail-config.yml
scrape_configs:
  - job_name: docker
    docker_sd_configs:
      - host: unix:///var/run/docker.sock
        refresh_interval: 5s
        filters:
          - name: status
            values: ["running"]
          # Исключить системные контейнеры
          - name: label
            values: ["com.docker.compose.service!=loki", "com.docker.compose.service!=promtail"]

Парсинг структурированных логов

Если ваше приложение пишет JSON логи:

pipeline_stages:
  - json:
      expressions:
        level: level
        message: message
        timestamp: timestamp
        user_id: user_id
  
  - labels:
      level:
      user_id:
  
  - timestamp:
      source: timestamp
      format: RFC3339

Мультистрочная обработка

Для логов, которые занимают несколько строк:

pipeline_stages:
  - multiline:
      firstline: '^\d{4}-\d{2}-\d{2}'
      max_wait_time: 3s

Мониторинг и алерты

Метрики Loki

Loki предоставляет метрики Prometheus:

  • loki_distributor_lines_received_total — количество полученных строк
  • loki_ingester_chunks_created_total — количество созданных чанков
  • loki_ingester_chunks_stored_bytes_total — размер хранимых данных
  • loki_request_duration_seconds — время обработки запросов

Создание алертов в Grafana

Алерт: слишком много ошибок

# Alert rule
- alert: HighErrorRate
  expr: |
    sum(rate({container_name=~".+"} |= "error" [5m])) > 10
  for: 5m
  annotations:
    summary: "Высокий уровень ошибок в логах"
    description: "Обнаружено {{ $value }} ошибок в секунду"

Дашборд для мониторинга Loki

Создайте дашборд с метриками:

  1. RPS (Requests Per Second) — количество запросов к Loki
  2. Ingestion Rate — скорость приема логов
  3. Storage Usage — использование хранилища
  4. Query Performance — производительность запросов

Оптимизация производительности

Ограничение объема логов

# loki-config.yml
limits_config:
  # Максимальный размер строки
  max_line_size: 256KB
  
  # Максимальное количество потоков на пользователя
  max_streams_per_user: 10000
  
  # Максимальный возраст логов
  reject_old_samples_max_age: 168h  # 7 дней
  
  # Лимит скорости приема
  ingestion_rate_mb: 16
  ingestion_burst_size_mb: 32

Retention политика

Автоматическое удаление старых логов:

# loki-config.yml
compactor:
  retention_enabled: true
  retention_delete_delay: 2h
  retention_delete_worker_count: 150

Оптимизация запросов

Используйте метки для фильтрации:

❌ Плохо:

{container_name=~".+"} |= "error" | json | user_id="123"

✅ Хорошо:

{container_name="my-bot", user_id="123"} |= "error"

Сжатие данных

Включите сжатие для экономии места:

# promtail-config.yml
clients:
  - url: http://loki:3100/loki/api/v1/push
    headers:
      Content-Type: application/json
    tenant_id: "1"
    # Сжатие
    compression: gzip

Решение проблем

Loki не принимает логи

Проблема: Promtail не может отправить логи в Loki

Решение:

# Проверить доступность Loki
curl http://localhost:3100/ready

# Проверить логи Promtail
docker logs promtail

# Проверить конфигурацию
docker exec promtail promtail -config.file=/etc/promtail/config.yml -dry-run

Высокое потребление памяти

Проблема: Loki потребляет много памяти

Решение:

# Уменьшить retention
limits_config:
  reject_old_samples_max_age: 24h  # 1 день вместо 7

# Ограничить количество потоков
limits_config:
  max_streams_per_user: 1000

Медленные запросы

Проблема: Запросы в Grafana выполняются медленно

Решение:

  • Используйте метки для фильтрации
  • Ограничьте временной диапазон запроса
  • Используйте topk() для агрегации

Пропуск логов

Проблема: Некоторые логи не попадают в Loki

Решение:

# Увеличить буфер Promtail
clients:
  - url: http://loki:3100/loki/api/v1/push
    batch_wait: 1s
    batch_size: 1048576  # 1MB
    timeout: 10s

Лучшие практики

1. Структурированное логирование

Используйте JSON формат для логов:

import json
import logging

class JSONFormatter(logging.Formatter):
    def format(self, record):
        log_data = {
            'timestamp': self.formatTime(record),
            'level': record.levelname,
            'message': record.getMessage(),
            'module': record.module,
            'function': record.funcName,
        }
        return json.dumps(log_data)

logger = logging.getLogger()
handler = logging.StreamHandler()
handler.setFormatter(JSONFormatter())
logger.addHandler(handler)

2. Правильные метки

Используйте метки для важных атрибутов:

✅ Хорошо:

  • container_name
  • service
  • environment (dev, staging, prod)
  • level (ERROR, WARN, INFO)

❌ Плохо:

  • user_id (слишком много уникальных значений)
  • request_id (слишком много уникальных значений)
  • message (содержимое лога)

3. Retention политика

Настройте автоматическое удаление старых логов:

# Удалять логи старше 7 дней
compactor:
  retention_enabled: true
  retention_delete_delay: 2h
  retention_delete_worker_count: 150

4. Мониторинг Loki

Следите за метриками Loki:

  • Использование памяти
  • Скорость приема логов
  • Производительность запросов
  • Размер хранилища

5. Безопасность

В продакшене:

  • Включите аутентификацию
  • Используйте TLS для соединений
  • Ограничьте доступ к API
  • Настройте rate limiting

Практический пример: Логирование Docker ботов

Конфигурация для хостинга ботов

# docker-compose.yml
version: '3.8'

services:
  bot-1:
    image: my-bot:latest
    container_name: bot-1
    environment:
      - LOG_FORMAT=json
      - LOG_LEVEL=INFO
    labels:
      - "logging=promtail"
      - "service=bot"
      - "environment=production"
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  bot-2:
    image: my-bot-2:latest
    container_name: bot-2
    environment:
      - LOG_FORMAT=json
      - LOG_LEVEL=INFO
    labels:
      - "logging=promtail"
      - "service=bot"
      - "environment=production"
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

Promtail конфигурация для ботов

scrape_configs:
  - job_name: bots
    docker_sd_configs:
      - host: unix:///var/run/docker.sock
        refresh_interval: 5s
        filters:
          - name: label
            values: ["logging=promtail"]
    
    relabel_configs:
      - source_labels: ['__meta_docker_container_name']
        regex: '/(.*)'
        target_label: 'bot_name'
      
      - source_labels: ['__meta_docker_container_label_environment']
        target_label: 'environment'
      
      - source_labels: ['__meta_docker_container_label_service']
        target_label: 'service'
    
    pipeline_stages:
      - json:
          expressions:
            level: level
            message: message
            timestamp: timestamp
            user_id: user_id
            bot_id: bot_id
      
      - labels:
          level:
          bot_id:
          environment:
      
      - timestamp:
          source: timestamp
          format: RFC3339

Запросы для анализа ботов

Ошибки конкретного бота:

{bot_name="bot-1", level="ERROR"}

Статистика по ботам:

sum by (bot_name) (count_over_time({service="bot"}[5m]))

Топ ошибок:

topk(10, sum by (message) (count_over_time({level="ERROR"}[1h])))

Заключение

Grafana Loki — это мощный инструмент для централизованного логирования Docker контейнеров. Он сочетает простоту настройки, низкое потребление ресурсов и отличную интеграцию с Grafana.

Ключевые преимущества:

  • ✅ Простота — минимальная конфигурация
  • ✅ Производительность — низкое потребление ресурсов
  • ✅ Интеграция — единый интерфейс с метриками
  • ✅ Масштабируемость — горизонтальное масштабирование
  • ✅ Экономичность — подходит для небольших проектов

Следующие шаги:

  1. Установите Loki + Promtail + Grafana
  2. Настройте сбор логов из ваших контейнеров
  3. Создайте дашборды в Grafana
  4. Настройте алерты для критических ошибок
  5. Оптимизируйте retention политику

Полезные ресурсы:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *