MAATRIX / Блог / Grafana Loki в Docker Compose: готовый файл

Grafana Loki в Docker Compose: готовый файл

MAATRIX

Разворачивать Loki с нуля обычно означает: поднять контейнер, зайти в Grafana, вручную добавить источник данных, повторить то же самое на следующем сервере — и так каждый раз заново. Ниже — готовый docker-compose.yml, где Loki, Promtail, Prometheus и Grafana поднимаются одной командой, а датасорсы в Grafana прописываются автоматически через провижининг-файлы, без единого клика в интерфейсе. Именно так Loki и задумывался разработчиками Grafana Labs — как второй столб рядом с Prometheus, работающий в той же панели по тому же принципу меток.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Почему Loki и Prometheus — одна связка, а не два отдельных инструмента

Loki спроектирован по образу Prometheus: тот индексирует временные ряды метрик по меткам (labels), Loki — точно так же индексирует логи, но только по меткам (сервис, хост, уровень), а не по полному тексту. Содержимое строки лежит рядом сжатым, без построения полнотекстового индекса. Отсюда и экономия: Loki требует ресурсов в разы меньше, чем Elasticsearch/OpenSearch, за счёт более простой (и более узкой по возможностям) модели поиска.

Практическая польза от такой архитектуры — не абстрактная экономия диска, а конкретная штука в интерфейсе: Grafana умеет показывать логи и метрики на одной временной шкале, и если у обоих источников совпадают метки (например, service или instance), можно кликнуть по всплеску на графике Prometheus и тут же перейти к логам того же сервиса за тот же интервал. Отдельно от Prometheus Loki тоже работает, но часть смысла связки при этом теряется.

Если Grafana и Prometheus у вас уже развёрнуты отдельно и нужен только Loki с Promtail без переустановки остального, короче будет статья про настройку сбора логов Loki и Promtail — там компоновка минимальнее. Здесь же — цельный файл под ситуацию «поднимаю мониторинг и логи с нуля на новом сервере».

Что подготовить на хосте

Docker и Docker Compose plugin должны быть установлены заранее — если ещё нет, разверните по инструкции про установку Docker Compose для продакшена на VPS. Дальше нужна только структура каталогов под конфиги и провижининг:

mkdir -p ~/observability-stack/{loki,promtail,prometheus,grafana/provisioning/datasources}
cd ~/observability-stack

Loki-контейнер официально запускается от непривилегированного пользователя с UID 10001 — если смонтировать том без прав на запись для этого UID, контейнер падает на старте с mkdir /loki/chunks: permission denied. Проще всего довериться named volume, как в примере ниже, — Docker создаёт его с нужными правами сам; при bind-mount с хостовой директории права придётся выставлять вручную.

Промтейлу для чтения логов контейнеров нужен доступ к /var/lib/docker/containers и сокету Docker в режиме read-only — держите это в уме при аудите безопасности сервера.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Готовый docker-compose.yml

Полный стек: Loki (хранение логов), Promtail (сборщик), Prometheus (метрики), node-exporter (метрики самого хоста) и Grafana поверх всего этого:

services:
  loki:
    image: grafana/loki:3.2.0
    container_name: loki
    restart: unless-stopped
    user: "10001:10001"
    volumes:
      - ./loki/loki-config.yml:/etc/loki/loki-config.yml
      - loki-data:/loki
    command: -config.file=/etc/loki/loki-config.yml
    networks:
      - observability

  promtail:
    image: grafana/promtail:3.2.0
    container_name: promtail
    restart: unless-stopped
    volumes:
      - ./promtail/promtail-config.yml:/etc/promtail/promtail-config.yml
      - /var/log:/var/log:ro
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
    command: -config.file=/etc/promtail/promtail-config.yml
    depends_on:
      - loki
    networks:
      - observability

  prometheus:
    image: prom/prometheus:v2.54.1
    container_name: prometheus
    restart: unless-stopped
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus-data:/prometheus
    command:
      - --config.file=/etc/prometheus/prometheus.yml
      - --storage.tsdb.retention.time=30d
    networks:
      - observability

  node-exporter:
    image: prom/node-exporter:v1.8.2
    container_name: node-exporter
    restart: unless-stopped
    pid: host
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - --path.procfs=/host/proc
      - --path.sysfs=/host/sys
      - --collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)
    networks:
      - observability

  grafana:
    image: grafana/grafana:11.2.0
    container_name: grafana
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD}
    volumes:
      - grafana-data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning
    depends_on:
      - loki
      - prometheus
    networks:
      - observability

volumes:
  loki-data:
  prometheus-data:
  grafana-data:

networks:
  observability:
    driver: bridge

Рядом файл .env:

GRAFANA_ADMIN_PASSWORD=замените-на-свой-пароль

Версии образов зафиксированы явно — так вы не поймаете внезапный breaking change в схеме хранения Loki посреди ночи из-за тега latest. Перед боевым использованием сверьте актуальные версии в Docker Hub и обновляйте их сознательно, тестируя на некритичном окружении.

Конфиги Loki, Promtail и Prometheus

loki/loki-config.yml — минимальная рабочая конфигурация с хранением на локальном диске и retention на 30 дней:

auth_enabled: false
server:
  http_listen_port: 3100
common:
  path_prefix: /loki
  storage:
    filesystem:
      chunks_directory: /loki/chunks
      rules_directory: /loki/rules
  replication_factor: 1
  ring:
    kvstore:
      store: inmemory
schema_config:
  configs:
    - from: 2024-01-01
      store: tsdb
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h
limits_config:
  retention_period: 720h
compactor:
  retention_enabled: true
  delete_request_store: filesystem

Блок compactor.retention_enabled: true обязателен — без него параметр retention_period тихо игнорируется, и это самая частая причина, почему диск под логами растёт без остановки, хотя retention вроде бы настроен.

promtail/promtail-config.yml — собирает логи всех Docker-контейнеров и системные логи хоста:

server:
  http_listen_port: 9080
positions:
  filename: /tmp/positions.yaml
clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
  - job_name: docker
    docker_sd_configs:
      - host: unix:///var/run/docker.sock
        refresh_interval: 15s
    relabel_configs:
      - source_labels: ['__meta_docker_container_name']
        target_label: container

  - job_name: system
    static_configs:
      - targets: [localhost]
        labels:
          job: varlogs
          __path__: /var/log/*.log

prometheus/prometheus.yml — минимальный конфиг, который уже опрашивает сам себя и node-exporter:

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: prometheus
    static_configs:
      - targets: ['localhost:9090']

  - job_name: node
    static_configs:
      - targets: ['node-exporter:9100']

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

Автопровижининг: датасорсы без единого клика в UI

Главное отличие этого файла от классической установки руками — Grafana подхватывает оба источника данных сама при старте, без Connections → Add data source. Файл grafana/provisioning/datasources/datasources.yml:

apiVersion: 1

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

  - name: Loki
    type: loki
    access: proxy
    url: http://loki:3100
    editable: true
    jsonData:
      derivedFields:
        - datasourceUid: prometheus
          matcherRegex: "traceID=(\\w+)"
          name: TraceID
          url: "$${__value.raw}"

Блок derivedFields — необязательная деталь: если приложения пишут в лог traceID=..., Grafana превратит его в кликабельную ссылку прямо в просмотре логов. Если трассировки нет — удалите блок, датасорсы заработают и без него.

Grafana читает файлы из /etc/grafana/provisioning при каждом старте контейнера — провижининг идемпотентен: пересоздали контейнер, датасорсы снова на месте без ручной настройки, что особенно ценно для шаблона под несколько серверов.

Ресурсы, retention и типичные ошибки при первом запуске

Ориентир по ресурсам для всего стека целиком (это именно ориентир — реальные цифры зависят от числа контейнеров-источников и объёма метрик):

СценарийRAM всегоДиск под логиДиск под метрики
Один сервер, десяток контейнеров2–3 ГБ10–20 ГБ5–10 ГБ
Несколько серверов, логи с 3–5 машин3–4 ГБ30–50 ГБ10–20 ГБ
Активная инфраструктура, retention 30 дней+4–6 ГБот 50 ГБ, растётот 20 ГБ

Диск под логи растёт быстрее, чем кажется на глаз, если контейнеры пишут в stdout много отладочной информации — стоит свериться с общими принципами из статьи про ротацию логов, чтобы не забивался диск: идея с ограничением горизонта хранения там та же, что и в retention_period у Loki.

Частые проблемы при первом docker compose up -d:

  • Loki падает с permission denied на /loki/chunks — том смонтирован без прав для UID 10001, под которым бежит процесс Loki. С именованным volume из примера это решается само; при bind-mount права нужно выставить вручную.
  • Grafana поднялась, но датасорсы не появились — проверьте, что каталог grafana/provisioning смонтирован именно в /etc/grafana/provisioning, а не лежит рядом с compose-файлом; ошибки провижининга видно в docker compose logs grafana.
  • Promtail не видит логи контейнеров — не подключен сокет /var/run/docker.sock или в конфиге нет docker_sd_configs: без service discovery Promtail не знает, какие контейнеры существуют.
  • В Explore пусто, хотя Promtail жив — частая причина в неверном URL источника Loki или в рассинхроне времени между контейнерами; разбор типовых ошибок вроде «entry out of order» — в статье про частые ошибки логов в Grafana Loki на сервере.
  • Диск под /loki растёт без остановки — забыт или выключен compactor.retention_enabled: true в конфиге Loki.

Быстрая диагностика всего стека:

docker compose ps
curl -s http://localhost:3100/ready        # Loki готов
curl -s http://localhost:9090/-/healthy    # Prometheus готов
docker compose logs --tail 100 grafana

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Можно ли использовать файл без Prometheus, только ради логов?

Да, уберите сервисы prometheus и node-exporter из compose и второй датасорс из провижининга — Loki с Promtail и Grafana работают самостоятельно, только exemplars между метриками и логами будут недоступны.

Grafana точно не потребует пароль admin/admin по умолчанию?

Нет, при первом запуске возьмётся значение из GRAFANA_ADMIN_PASSWORD в .env — замените плейсхолдер на реальный пароль до старта, иначе получите ошибку из-за пустой переменной.

А если у меня уже есть отдельно поднятая Grafana и её не хочется переставлять?

Уберите сервис grafana из compose, оставьте Loki и Promtail, а датасорс в существующей Grafana добавьте вручную по URL http://IP_СЕРВЕРА:3100 — либо через её собственный провижининг, если она тоже в Docker.

Чем этот стек отличается от полноценного ELK?

Loki не строит полнотекстовый индекс по содержимому логов, а индексирует только метки — легче по ресурсам, слабее по возможностям поиска. Для фильтрации по сервису, хосту и уровню достаточно. Для сложных полнотекстовых запросов и агрегаций по произвольным полям нужен Elasticsearch/OpenSearch.

Нужно ли открывать порты Loki и Prometheus наружу?

Нет — наружу пробрасывается только порт Grafana (3000), Loki и Prometheus доступны внутри сети observability по именам сервисов. Если Grafana нужна снаружи, ставьте её за реверс-прокси с TLS.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →