Grafana Loki в Docker Compose: готовый файл
Разворачивать 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 ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →