MAATRIX GAMES / Блог / Управление версиями конфигов через git — практика для админов

Управление версиями конфигов через git — практика для админов

MAATRIX GAMES

Ситуация знакома почти каждому, кто держит игровой сервер дольше пары месяцев: поправил server.properties, сервер не поднялся, а какая именно строчка сломала запуск — уже не вспомнить, потому что предыдущая версия файла нигде не сохранилась. Или наоборот — всё вроде работало, а через неделю выясняется, что кто-то из соадминов тихо переписал твои настройки лимита игроков поверх твоих. Git решает обе проблемы не как экзотика для программистов, а как рабочая привычка: каждое изменение конфига фиксируется отдельным коммитом, откат к рабочей версии — одна команда, а не раскопки в папке с файлами server.cfg.bak2_final.

Зачем это, если и так есть бэкапы

Автобэкапы (см. автобэкапы игрового сервера — настройка расписания) решают другую задачу — они снимают полный снимок мира и файлов по расписанию, на случай катастрофы: диск умер, хостер потерял VPS, сервер взломали. Git — про другое: про историю мелких ручных правок конфигурации, которые вносятся каждый день и которые бэкап по расписанию просто не успевает поймать между снимками.

Разница на практике: если ты в 15:00 поменял view-distance и max-players, а в 15:20 сервер лёг, ночной бэкап тебе не поможет — он снят вчера и не знает про сегодняшние правки. А git diff покажет ровно эти две изменённые строчки за десять секунд. Обе практики не конкурируют, а дополняют друг друга: бэкапы — от катастроф, git — от «сам себе прострелил ногу мелкой правкой».

Инициализация репозитория и первый коммит

Заводить git под конфиги можно прямо в рабочей директории сервера, без танцев с bare-репозиториями и хуками деплоя (это отдельная, более продвинутая тема — если дорастёшь до неё, она разобрана в статье про автоматизацию деплоя конфигов через git). Для практики версионирования достаточно локального репозитория:

cd /home/minecraft/survival-server
git init
git config user.name "Admin"
git config user.email "admin@example.com"

Дальше — первый коммит, фиксирующий текущее состояние как точку отсчёта:

git add server.properties bukkit.yml spigot.yml
git commit -m "Начальное состояние конфигов survival-сервера"

Важный момент: в первый коммит стоит добавлять только то, что реально нужно версионировать — сами конфиги, а не всю директорию сервера целиком. Мир, логи, jar-файлы, кэш плагинов — это гигабайты бинарных данных, которые git хранить не умеет эффективно и для которых у него просто нет смысла: диффа по бинарнику не построишь, а вес репозитория раздуется быстро. Их нужно сразу исключить — как раз через .gitignore.

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

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

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

.gitignore и секреты: что никогда не попадёт в коммит

Это ключевое правило всей практики: в репозиторий не должно попасть ничего, что является паролем, токеном или персональными данными игроков. RCON-пароль, токен Discord-бота, API-ключ хостинга, файл whitelist с реальными никами и датами — всё это либо секрет, либо персональные данные (о хранении вторых — отдельно в статье про приватность данных игроков). Причина простая: история git не забывает. Если пароль однажды попал в коммит, удаление файла в следующем коммите его не стирает — он останется доступен через git log и старые ревизии, пока ты не перепишешь историю целиком, а это уже отдельная головная боль.

Создай .gitignore в корне репозитория конфигов ещё до первого коммита:

# логи и рантайм-данные
*.log
logs/
world/
worlds/
cache/

# секреты
secrets.env
*rcon*password*
.env
*token*
*api*key*

# персональные данные игроков
whitelist.json
banned-players.json
ops.json
usercache.json

Если пароль или токен уже случайно закоммитили — недостаточно просто добавить файл в .gitignore задним числом, он всё ещё будет в истории. Правильная последовательность:

git rm --cached secrets.env
echo "secrets.env" >> .gitignore
git commit -m "Убрать secrets.env из репозитория"

Это уберёт файл из будущих коммитов, но не из истории — если пароль реально утёк в публичный или общий репозиторий, его нужно считать скомпрометированным и просто сменить (RCON-пароль, токен бота, ключ API), а не полагаться на переписывание истории (git filter-repo или BFG Repo-Cleaner — рабочие инструменты для этого, но проще и надёжнее сразу ротировать секрет).

Сами секреты при этом никуда не деваются — они живут вне репозитория, в отдельном файле вроде /etc/gameserver/secrets.env, который читается сервисом при старте, но в git status никогда не показывается как отслеживаемый.

Рабочий цикл: commit, diff, log так, чтобы это реально помогало

Голая механика «git add, git commit» без дисциплины даёт мало пользы — история из полусотни коммитов с сообщением fix бесполезна ровно так же, как папка с файлами _final2. Три привычки, которые делают историю рабочим инструментом, а не формальностью:

Смотри диф перед коммитом, а не после — так видно ровно то, что уйдёт в историю:

git diff server.properties
-view-distance=10
+view-distance=16
-max-players=20
+max-players=40

Коммить осмысленно, по одной логической правке за раз, а не «накопил за неделю — закоммитил всё разом»:

git commit -m "Увеличить view-distance до 16, поднять лимит игроков до 40 под ивент субботы"

Такое сообщение через месяц ответит сразу на два вопроса: что изменилось и зачем. Голое update config — ни на один. Если в команде несколько админов, привычка писать причину правки, а не только факт, экономит часы на будущих разборах инцидентов.

Смотри историю файла, когда параметр давно живёт своей жизнью и непонятно, кто и когда его трогал:

git log -p --follow server.properties
git log --oneline -- bukkit.yml

git blame server.properties покажет построчно, каким коммитом внесена каждая строка — полезно, когда параметр выглядит странно и хочется понять контекст правки, а не просто значение.

Ветка на рискованную правку

Не каждую правку стоит сразу коммитить в основную линию истории. Если меняешь что-то с неочевидным эффектом — тикрейт, distance-настройки, память под JVM, — удобнее вынести это в отдельную ветку, проверить на практике и либо влить, либо выбросить:

git checkout -b tune-tickrate
# правишь конфиг, перезапускаешь сервер, смотришь на результат
git add server.properties
git commit -m "Эксперимент: снизить simulation-distance для теста нагрузки"

Если правка себя оправдала:

git checkout main
git merge tune-tickrate
git branch -d tune-tickrate

Если нет — ветку просто выбрасывают без merge, и основная линия истории остаётся чистой от неудачных экспериментов:

git checkout main
git branch -D tune-tickrate

Это особенно полезно перед крупными изменениями конфига, когда новую версию нужно подготовить заранее и проверить, не трогая рабочую версию до момента реального переключения.

Откат к рабочей версии после неудачного изменения

Это тот самый сценарий, ради которого вся эта практика в первую очередь и заводится: конфиг поменяли, сервер не стартует или ведёт себя не так, и нужно быстро вернуться к последнему состоянию, которое точно работало.

Первый шаг всегда одинаковый — посмотреть, что вообще менялось в последнее время:

git log --oneline -5
a3f21c9 Увеличить view-distance до 16, поднять лимит игроков до 40
7c88e0d Обновить EssentialsX config.yml под новую версию плагина
e91d4a0 Начальное состояние конфигов survival-сервера

Дальше — три инструмента отката, и у каждого своя ситуация:

КомандаКогда использоватьЧто делает
git checkout <commit> -- файлНужно вернуть только один файл, остальное трогать не хочетсяПодтягивает версию файла из указанного коммита в рабочую копию
git revert <commit>Правка уже запушена/расшарена другим админам, историю переписывать нельзяСоздаёт новый коммит, отменяющий изменения старого — история остаётся честной
git reset --hard <commit>Локальный репозиторий, правка ещё не расшарена, нужно откатить сразу всёПеремещает ветку и рабочую копию к указанному коммиту, более поздние изменения теряются

Пример точечного отката одного файла, когда сломался только server.properties, а остальные конфиги трогать не нужно:

git checkout 7c88e0d -- server.properties
systemctl restart minecraft-survival

Пример отмены последнего коммита с сохранением истории (подходит, когда конфиги общие для команды и лежат в общем репозитории):

git revert a3f21c9
systemctl restart minecraft-survival

Если нужно отбросить всё вплоть до заведомо рабочей точки локально — например, эксперимент разросся на несколько коммитов и разбираться, какой из них виноват, дольше, чем просто откатиться:

git reset --hard e91d4a0
systemctl restart minecraft-survival

reset --hard стирает незакоммиченные изменения безвозвратно — перед ним стоит убедиться, что действительно ничего ценного не потеряется, либо сначала сохранить текущее состояние отдельным коммитом или веткой на всякий случай.

Если непонятно, какой именно из десятка коммитов внёс проблему (например, сервер начал падать не сразу, а через несколько правок), git bisect находит виноватый коммит бинарным поиском — быстрее, чем откатывать по одному и перезапускать сервер после каждой попытки.

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

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

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

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

Нужен ли git для одного небольшого сервера, где всё и так помню наизусть?

Практика окупается уже на втором-третьем месяце — конфиг обрастает нюансами (почему тут именно такое значение, а не дефолтное), и без истории объяснение теряется. Плюс это дешёвая страховка на случай, если правки будет вносить кто-то ещё, кроме тебя.

А что если конфиг менял игровой движок сам, не через git — например, плагин переписал YAML при старте?

git status сразу покажет файл как изменённый. Если правка ожидаема (плагин нормализовал формат), закоммить её с понятным сообщением; если нет — git diff покажет ровно то, что изменил движок, и можно решить, откатывать или нет.

Можно ли хранить в репозитории сразу несколько серверов — Minecraft, Rust, CS2?

Да, по папке на инстанс в одном репозитории — это упрощает общий .gitignore и историю команды. Единственное ограничение: .gitignore-паттерны вроде whitelist.json сработают на всех папках сразу, так что для специфичных для игры секретов лучше явно указывать путь, например rust-eu-1/secrets.env.

Что делать с конфликтами, если два админа правят один файл одновременно?

Git честно покажет конфликт при попытке слияния — конфликтующие строки нужно разрешить руками, выбрав правильную версию или скомбинировав обе. Это неприятно, но лучше, чем молчаливая перезапись чужой правки, которая случается без версионирования.

Как понять, что коммит вообще безопасно катить на прод, а не просто «выглядит нормально»?

Синтаксис YAML/properties можно проверить локально перед коммитом (yamllint, обычный парсер properties-файла), но логику игрового движка снаружи не проверишь — тестовый прогон на staging-сервере остаётся обязательным шагом, особенно перед серьёзными правками. Подробнее о такой практике — в статье про тестовый staging-сервер перед обновлением прода.