MAATRIX GAMES / Блог / Логротация: почему логи игрового сервера нельзя игнорировать

Логротация: почему логи игрового сервера нельзя игнорировать

MAATRIX GAMES

Классическая история: сервер работал месяцами без единой перезагрузки, все довольны, а потом внезапно падает запись мира, RCON перестаёт отвечать, а df -h показывает 0% свободного места. Виновник — не хак и не баг движка, а забытая папка logs/, которая тихо росла все эти месяцы и однажды съела весь диск. Логротация — это не про порядок ради порядка, а про то, чтобы такого вечера у вас никогда не было. Разберём, как настроить logrotate под игровой сервер, сколько логов реально нужно хранить и почему сжатие старых файлов — не опция, а обязательный пункт чек-листа.

Почему логи вообще становятся проблемой

Игровой сервер пишет в лог гораздо больше, чем кажется на первый взгляд. Каждый коннект и дисконнект игрока, каждая ошибка плагина, каждый чат-месседж (если включено логирование чата), каждая RCON-команда админа — всё это строки в файле, который растёт без остановки, если никто не следит.

На спокойном сервере на 10-20 игроков лог может расти на десятки мегабайт в день. На загруженной сборке с активным античитом, подробным логированием плагинов (Oxide/uMod на Rust, Paper с включёнными таймингами) или RP-сервером FiveM, где логируется каждое действие в тикет-системе, счёт может идти на сотни мегабайт в сутки. Без ротации файл latest.log или server.log просто копится месяцами.

Проблема не только в занятом месте:

  • Диск переполняется незаметно — сервер продолжает работать, пока не кончится место, и тогда падает уже не только логирование, а запись сохранений мира, что куда хуже.
  • Большой лог тормозит диагностику — открыть файл на 5 ГБ текстовым редактором или прогнать через grep заметно дольше, чем читать ротированный кусок за один день.
  • Некоторые процессы пишут в лог синхронно — на медленном диске (особенно на бюджетных VPS с сетевым хранилищем) это может просаживать тикрейт при активном логировании.
  • История инцидентов теряется — если файл не архивируется, а перезаписывается при рестарте, вы теряете улики ровно тогда, когда они нужнее всего — сразу после краша.

Если сервер уже упал и вы разбираетесь, что произошло, — сначала прочитайте как читать логи и искать причину краша, а логротацию настройте сразу после, чтобы в следующий раз было что читать.

Как это устроено у разных игр по умолчанию

Прежде чем городить свою ротацию, стоит понять, что уже делает сама игра — часто дублировать не нужно, а нужно только дополнить.

Игра/движокВстроенная ротацияЧто нужно добавить
Minecraft (Java, Forge/Fabric/Paper)Да, latest.log архивируется в logs/YYYY-MM-DD-N.log.gz при каждом рестартеНичего критичного, но старые .gz тоже стоит чистить по возрасту
RustНет, пишет в stdoutОбязательно — либо logrotate, либо перенаправление с ротацией на уровне systemd
CS2, TF2, Left 4 Dead 2 (Source)Нет, файлы L<дата>.log копятся бесконечноОбязательно чистить/архивировать по возрасту
ARK: Survival (Ascended)Частично, но ShooterGame.log может расти без ограничения при долгом аптаймеЖелательно
Project ZomboidНет отдельного встроенного лимита на размерОбязательно
FiveM (GTA V)Зависит от ресурсов сервера, ESX/QBCore обычно пишут свои файлы без лимитаОбязательно, особенно логи тикет-систем и античита
Discord-бот, Telegram-бот управления серверомОбычно нет, если это ваш собственный скриптОбязательно, если бот активно логирует

Даже там, где игра сама архивирует старые сессии (Minecraft), она обычно не удаляет их совсем — просто оставляет расти папку logs/ в виде десятков .gz-файлов за месяцы. Это уже не критично для диска, но за годы набегает объём, так что возрастной лимит не помешает и там.

Нужен игровой сервер?

MAATRIX GAMES — 45+ игр, деплой за минуты, DDoS-защита, автобэкапы. Тарифы от 399 ₽/мес.

Создать сервер

Устанавливаем и настраиваем logrotate

logrotate — штатная утилита почти всех Linux-дистрибутивов (Debian/Ubuntu, CentOS/Alma, Rocky), обычно уже установлена и запускается по крону раз в сутки. Проверить:

which logrotate
cat /etc/cron.daily/logrotate

Если утилиты нет:

apt install logrotate      # Debian/Ubuntu
dnf install logrotate      # CentOS/AlmaLinux/Rocky

Конфиги хранятся в /etc/logrotate.d/ — каждый сервис получает свой файл. Создаём отдельный для игрового сервера, например /etc/logrotate.d/rust-server:

/home/rustserver/server/my-server/RustDedicated_Data/... /home/rustserver/logs/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    copytruncate
}

Разбор ключевых директив:

  • daily — ротация раз в сутки (есть также weekly, monthly, либо size 100M — ротация по достижении размера, что полезнее для очень активных серверов).
  • rotate 14 — хранить 14 архивных копий (то есть две недели истории при дневной ротации).
  • compress — сжимать старые файлы в .gz, экономит до 90% места на текстовых логах.
  • delaycompress — не сжимать файл сразу, а только следующий цикл (полезно, если какой-то процесс ещё дописывает в него данные после ротации).
  • missingok — не ругаться ошибкой, если лог-файла временно нет (сервер не запущен).
  • notifempty — не ротировать пустой файл, незачем плодить пустые архивы.
  • copytruncate — критически важная директива для процессов, которые держат файл лога открытым постоянно (большинство игровых серверов). Вместо переименования файла logrotate копирует его содержимое и обрезает оригинал до нуля — так процесс продолжает писать в тот же файл-дескриптор без перезапуска.

Применить и проверить конфиг без ожидания суток можно принудительно:

logrotate -f /etc/logrotate.d/rust-server
logrotate -d /etc/logrotate.d/rust-server   # dry-run, покажет что будет сделано без реальных действий

Ключевая грабля: copytruncate или postrotate

Здесь чаще всего ошибаются новички. Есть два принципиально разных подхода к ротации файла, который процесс держит открытым:

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

Вариант 2 — postrotate с сигналом процессу. Логротация переименовывает старый файл, а затем сигналом (SIGHUP/SIGUSR1) просит процесс переоткрыть лог — работает только если сам процесс это умеет:

postrotate
    systemctl kill -s HUP gameserver.service > /dev/null 2>&1 || true
endscript

Проблема в том, что большинство игровых серверов (Rust, ARK, Source-движок, ваш собственный Discord-бот на Python/Node) не умеют переоткрывать лог по сигналу — если использовать postrotate без copytruncate для такого процесса, файл переименуется, а сервер продолжит писать в уже переименованный (и невидимый в списке файлов) дескриптор — новый server.log не появится вообще, пока процесс не перезапустится.

Практический вывод: для 9 из 10 игровых серверов используйте copytruncate. postrotate со штатным переоткрытием логов имеет смысл только для процессов, которые сами это умеют — например, отдельный сервис веб-панели рядом на той же машине, который работает по HTTPS.

Если игра не пишет в файл: логируем stdout через systemd

Rust, Valheim и многие другие сервера по умолчанию пишут вывод только в stdout, а не в файл. Если сервер запущен через systemd-юнит (что правильнее, чем голый screen/tmux — подробнее в статье про systemd, screen и tmux для игровых процессов), самый чистый способ получить файл лога — journald с последующей выгрузкой, либо явный редирект в юните:

[Service]
ExecStart=/home/rust/server/RustDedicated -batchmode +server.port 28015
StandardOutput=append:/home/rust/logs/rust-server.log
StandardError=append:/home/rust/logs/rust-server.log

Такой файл logrotate ротирует так же, как любой другой — copytruncate работает и здесь, поскольку systemd просто дописывает в открытый дескриптор.

Альтернатива — оставить вывод в journald (поведение по умолчанию без StandardOutput) и настроить лимиты самого journald вместо logrotate, в /etc/systemd/journald.conf: SystemMaxUse=500M и MaxRetentionSec=14day, затем systemctl restart systemd-journald. Это не совсем логротация в классическом смысле, но решает ту же задачу. Какой вариант выбрать — дело вкуса: файл проще смотреть через grep/tail, journald удобнее фильтровать через journalctl -u rust-server --since "1 hour ago".

Сколько хранить: баланс между диском и расследованием инцидентов

Универсального числа нет, но есть рабочие ориентиры:

  • Спокойный сервер с редкими проблемами — 14-30 дней ротации достаточно, этого хватает разобраться в инциденте, даже если о нём сообщили не сразу.
  • Активный сервер с частыми жалобами на читеров/гриферов — храните дольше, 30-60 дней, особенно логи чата и RCON-команд админов.
  • Продакшн с юридическими рисками (персональные данные игроков, платёжные логи биллинга) — ротация должна сверяться с политикой хранения персональных данных: иногда логи нужно хранить дольше по закону, а иногда — наоборот, удалять быстрее по требованиям приватности.
  • Диагностические/дебаг-логи с verbose-уровнем — обычно достаточно 3-7 дней: они огромные по объёму и актуальны только для текущей проблемы.

Практический приём — не хранить всё на одном уровне детализации. Держите короткую ротацию (7 дней) на подробные debug-логи и длинную (30-60 дней) на сжатые архивы ключевых событий вроде банов, крашей и рестартов — так вы не теряете важную историю, но и не платите местом на диске за мегабайты рутинного шума.

Мониторинг места на диске как страховка

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

Простой крон-скрипт для алерта в Discord при нехватке места (запускать раз в 30 минут через crontab):

#!/bin/bash
THRESHOLD=85
USAGE=$(df -h / | awk 'NR==2 {print $5}' | tr -d '%')

if [ "$USAGE" -ge "$THRESHOLD" ]; then
    curl -s -X POST -H "Content-Type: application/json" \
        -d "{\"content\": \"⚠️ Диск занят на ${USAGE}%! Проверь логи и бэкапы на сервере.\"}" \
        "$DISCORD_WEBHOOK_URL"
fi

Это тот же принцип, что и уведомления о критичных событиях сервера через webhook — только конкретно про место на диске, которое логи занимают в первую очередь, если ротация не настроена или настроена неправильно (например, rotate 14 без compress на очень болтливом сервере всё равно может накопить внушительный объём).

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

Нужен игровой сервер?

MAATRIX GAMES — 45+ игр, деплой за минуты, DDoS-защита, автобэкапы. Тарифы от 399 ₽/мес.

Создать сервер

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

Что будет, если не настраивать logrotate вообще?

Ничего страшного не произойдёт, пока не кончится место на диске — а вот когда кончится, скорее всего пострадает не только логирование: перестанут сохраняться миры, упадёт база данных плагинов, может отвалиться сам процесс сервера. Лучше настроить один раз заранее, чем разбираться в 3 часа ночи.

Можно ли просто удалять старые логи скриптом через find -mtime +14 -delete вместо logrotate?

Можно, и для совсем простых случаев это рабочий вариант, но logrotate даёт сжатие, безопасную ротацию открытых файлов (copytruncate) и единый стандартный механизм — плюс не нужно писать и поддерживать свой скрипт с нуля.

Логи занимают место даже после сжатия — что не так?

Проверьте, не логируется ли что-то избыточное — например, полный чат сервера или подробный debug-режим плагина, который забыли выключить после отладки. Иногда решение — не хранить дольше, а логировать меньше.

Нужна ли логротация, если хостинг сам управляет дисками и мониторит место?

Мониторинг хостинга обычно предупредит о переполнении, но не решит проблему за вас — файлы логов всё равно растут внутри вашего дискового квота. На MAATRIX GAMES диск выделяется под конкретный тариф, и логротация — это ваша зона ответственности, как и на любом другом хостинге.

Как ротировать логи Docker-контейнера с игровым сервером?

logrotate хоста логи контейнера обычно не видит — настройте драйвер json-file с лимитами max-size/max-file в daemon.json, либо пробросьте лог-директорию наружу volume'ом и ротируйте её штатным logrotate хоста, как описано выше.