Централизованное логирование через Loki — зачем это игровому серверу
Если у вас один сервер Minecraft, tail -f logs/latest.log и grep по факту решают любую задачу — не усложняйте. Но как только серверов становится два-три (например, основной Minecraft плюс Rust-филиал плюс тестовый CS2 для разработки конфигов), начинается беготня: зашёл по ssh на один хост, посмотрел лог, вышел, зашёл на второй, забыл, где именно была та строчка с ошибкой. Grafana Loki решает именно это — все логи стекаются в одну систему, ищутся одним запросом сразу по всем серверам, и для этого не нужно поднимать тяжеленный Elasticsearch. Разберём, как это развернуть на практике, без лишней теории.
Содержание
Зачем игровому серверу вообще Loki
Loki — это система агрегации логов от Grafana Labs, спроектированная по принципу «индексируем только метки, а не полный текст». В отличие от Elasticsearch (ядро классического ELK-стека), который строит полнотекстовый индекс по каждому слову в каждой строке лога, Loki хранит логи почти как есть, а индексирует лишь небольшой набор меток — например, job, game, server, host. Из-за этого Loki заметно легче по ресурсам: instance на 1-2 ГБ RAM спокойно тянет логи с десятка небольших игровых серверов, тогда как аналогичный Elasticsearch-кластер потребует на порядок больше памяти.
Практическая польза для игрового хостинга:
- Поиск без ssh. Открыли Grafana в браузере, ввели запрос — увидели ошибку сразу со всех серверов, а не переключались по вкладкам терминала.
- Корреляция во времени. Если Rust и Minecraft легли одновременно — скорее всего, дело не в игре, а в хосте (диск, сеть, OOM на уровне ОС). Одна временная шкала для всех логов такое видно сразу.
- История без раздувания диска. Логи хранятся сжатыми чанками с настраиваемым сроком хранения — не нужно вручную чистить
logs/на каждой машине. - Готовая связка с алертами. Если вы уже мониторите TPS и аптайм сервера через Grafana, Loki органично встаёт рядом — метрики и логи в одном интерфейсе, без переключения между инструментами.
Если у вас единственный сервер и вы редко в него заглядываете — разница будет не так заметна, и приёмы из статьи про чтение логов и краш-репортов вам вполне хватит. Loki окупается именно на масштабе — от двух-трёх серверов и выше, или когда логи нужно смотреть команде, а не только вам одному с root-доступом.
Архитектура: Loki, агент сбора и Grafana
Система строится из трёх частей:
- Loki — хранилище и движок запросов. Принимает поток логов через HTTP API, хранит их в чанках (локально на диске или в S3-совместимом хранилище), отвечает на запросы на языке LogQL.
- Агент сбора — читает логи на каждом сервере и отправляет их в Loki. Классический вариант — Promtail (родной агент Loki, работает как systemd-юнит или контейнер). С 2024 года Grafana продвигает более универсальный Grafana Alloy как замену Promtail, но Promtail пока прекрасно работает, проще в конфигурации для одной задачи и это разумный выбор для небольшой инфраструктуры игровых серверов.
- Grafana — веб-интерфейс для запросов (раздел Explore) и дашбордов. Один и тот же Grafana-инстанс можно использовать и для логов, и для метрик TPS/RAM, если они у вас уже собираются.
LogQL — язык запросов Loki — синтаксически похож на PromQL (если вы уже настраивали Prometheus-алерты для хостинга, освоитесь за пять минут). Ключевое отличие от полнотекстового поиска: сначала вы фильтруете по меткам ({job="minecraft", server="survival-1"}), а уже внутри этого потока ищете текст (|= "OutOfMemory"). Это и даёт скорость — Loki не перебирает вообще все логи, а сразу сужается до нужного потока по индексу меток.
Нужен игровой сервер?
MAATRIX GAMES — 45+ игр, деплой за минуты, DDoS-защита, автобэкапы. Тарифы от 399 ₽/мес.
Создать серверРазворачиваем Loki и Grafana в Docker Compose
Самый быстрый способ поднять стек — Docker Compose на отдельной небольшой VM (не обязательно на той же машине, где крутится игра — логично вынести логирование на отдельный хост или хотя бы отдельный диск).
Минимальный docker-compose.yml:
version: "3.8"
services:
loki:
image: grafana/loki:3.1.0
container_name: loki
ports:
- "3100:3100"
volumes:
- ./loki-config.yaml:/etc/loki/local-config.yaml
- loki-data:/loki
command: -config.file=/etc/loki/local-config.yaml
restart: unless-stopped
grafana:
image: grafana/grafana:11.2.0
container_name: grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=смените_меня
volumes:
- grafana-data:/var/lib/grafana
restart: unless-stopped
volumes:
loki-data:
grafana-data:
Базовый loki-config.yaml (файловое хранилище, без внешних зависимостей — для старта на 5-15 игровых серверов этого достаточно):
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: 336h
ingestion_rate_mb: 8
ingestion_burst_size_mb: 16
retention_period: 336h — это 14 дней; для игровых логов обычно достаточно, крашрепорты и разбор конфликтов модов редко требуют смотреть глубже пары недель. После docker compose up -d Loki слушает на 3100, Grafana — на 3000. В Grafana добавьте data source типа Loki с адресом http://loki:3100 (если Grafana в том же docker-compose — по имени сервиса, если снаружи — по IP хоста).
Настройка Promtail: что и как собирать с игрового сервера
Promtail ставится на каждый сервер с игрой (можно и как systemd-сервис на голом железе, можно в контейнере). Он читает файлы логов и стримит их в центральный Loki.
Пример promtail-config.yaml для Minecraft-сервера с multiline-обработкой java-стектрейсов (без неё каждая строка исключения улетит отдельным событием, и связный стектрейс развалится на десятки логов):
server:
http_listen_port: 9080
positions:
filename: /var/lib/promtail/positions.yaml
clients:
- url: http://loki-host:3100/loki/api/v1/push
scrape_configs:
- job_name: minecraft
static_configs:
- targets: [localhost]
labels:
job: minecraft
server: survival-1
host: ${HOSTNAME}
__path__: /opt/minecraft/logs/latest.log
pipeline_stages:
- multiline:
firstline: '^\[\d{2}:\d{2}:\d{2}\]'
max_wait_time: 3s
Для игр, которые пишут в stdout и запущены как systemd-юниты (Rust, Valheim — см. статью про systemd, screen и tmux для игровых процессов), логи собираются не из файла, а прямо из journald:
- job_name: rust-server
journal:
max_age: 24h
labels:
job: rust
server: rust-main
relabel_configs:
- source_labels: ["__journal__systemd_unit"]
target_label: unit
Если процессы игр запущены в Docker-контейнерах (сравнение с классической установкой — в статье Docker-контейнер или классическая установка), проще всего использовать docker_sd_configs в Promtail — он сам находит контейнеры и подставляет их логи с метками по имени контейнера, без ручного указания путей:
- job_name: docker-containers
docker_sd_configs:
- host: unix:///var/run/docker.sock
refresh_interval: 5s
relabel_configs:
- source_labels: ["__meta_docker_container_name"]
target_label: container
Не забудьте добавить и системные логи хоста (/var/log/syslog или journald целиком) — именно там видно OOM-killer ОС и сетевые события, которые не попадают в лог самой игры.
Поиск логов через LogQL: примеры под игровые задачи
Открываем Grafana → Explore → выбираем data source Loki. Несколько практичных запросов:
Все ошибки на конкретном сервере за последний час:
{job="minecraft", server="survival-1"} |= "ERROR"
Поиск конкретного исключения сразу по всем Minecraft-серверам (то, что раньше требовало ssh на каждый по очереди):
{job="minecraft"} |= "OutOfMemoryError"
Регулярное выражение вместо точной подстроки — например, любые упоминания краша или фатальной ошибки:
{job=~"minecraft|rust|cs2"} |~ "(?i)(crash|fatal|exception)"
График количества ошибок в минуту по каждому серверу — удобно, чтобы увидеть, растёт ли частота проблемы, а не разово ли она возникла:
sum by (server) (count_over_time({job="minecraft"} |= "ERROR" [1m]))
Логи конкретного игрока (если ник попадает в лог подключений) сразу по нескольким серверам:
{job=~"minecraft|rust"} |= "PlayerName"
Такой запрос, который раньше означал открыть три ssh-сессии и три раза прогнать grep, здесь выполняется за секунды и сразу с подсветкой совпадений и временной шкалой.
Несколько серверов в одной Loki: метки, ретеншн, алерты
Ключевая идея централизованного логирования — правильные метки. Не полагайтесь только на имя job, добавляйте отдельные метки server, location (если у вас, скажем, сервера в UK и US), env (prod/test). Это позволяет одним запросом выбрать «все продакшн-сервера Minecraft в US» и не путать их с тестовым стендом.
По ретеншну — не храните логи вечно, это съедает диск и практической пользы почти не даёт после 2-4 недель. retention_period в limits_config (см. конфиг выше) — простой способ ограничить хранение на уровне всего Loki. Для более гибкой политики (разный срок для разных job) используется overrides в том же файле с отдельными лимитами на конкретный tenant или label.
Алерты имеет смысл вешать не на сами логи построчно, а на метрики, посчитанные из логов через LogQL — например, скачок числа строк с ERROR или OutOfMemory за 5 минут:
sum(count_over_time({job=~"minecraft|rust"} |= "OutOfMemory" [5m])) > 0
Такое правило в Grafana Alerting пришлёт уведомление (в тот же Discord-канал, куда обычно падают уведомления о статусе сервера) раньше, чем игроки напишут «сервер лежит» — вы узнаете о проблеме из лога, а не из чата.
Отдельно стоит закрыть порт 3100 (Loki push API) от внешнего мира файрволом или ограничить его VPN/приватной сетью между вашими серверами — это внутренний служебный трафик, наружу торчать ему незачем.
Нужен игровой сервер?
MAATRIX GAMES — 45+ игр, деплой за минуты, DDoS-защита, автобэкапы. Тарифы от 399 ₽/мес.
Создать серверЧастые вопросы
Нужен ли Loki, если у меня всего один игровой сервер?
Обычно нет — grep и tail -f по локальным логам решают задачу быстрее, чем разворачивать отдельный стек. Loki оправдан от двух-трёх серверов, когда время на ручной обход хостов становится ощутимо.
Чем Loki принципиально проще ELK-стека?
Loki не строит полнотекстовый индекс по каждому слову, а индексирует только метки (job, server и так далее) — это на порядок дешевле по RAM и диску, но и поиск по свободному тексту внутри лога чуть менее гибкий, чем в Elasticsearch. Для игровых логов, где основные запросы — «покажи ошибки на сервере X за последний час», этого более чем достаточно.
Сколько ресурсов реально нужно под Loki?
Зависит от объёма логов и числа серверов, но для десятка небольших игровых серверов небольшой VM с 1-2 ГБ RAM под сам Loki и минимальными накладными расходами Promtail на каждом хосте (там нагрузка минимальна, это просто tail файла) обычно достаточно с запасом — ориентируйтесь по факту через мониторинг самого Loki-хоста, а не берите цифру как гарантию.
Можно ли собирать логи с Windows-сервера (например, если игра крутится на Windows-хосте)?
Да, у Promtail есть сборка под Windows и он умеет читать Windows Event Log через отдельный windows_events scrape config — конфигурация немного другая, но принцип тот же: агент читает локальный источник и пушит в тот же центральный Loki.
Что делать, если Loki сам «падает» или не успевает принимать логи?
Обычно это упирается в ingestion_rate_mb в limits_config — при резком всплеске логов (например, спам ошибок от сломанного плагина) Loki начинает отбрасывать превышение с ошибкой 429. Увеличьте лимит под свой трафик или сначала разберитесь, почему один сервер вдруг начал писать логи в разы активнее обычного — это само по себе диагностический сигнал.