Настройка выделенного сервера Source под Linux, часть 3
- Установка MetaMod и SourceMod
- Установка MetaMod: Source
- Установка SourceMod
- Автозапуск игровых серверов
- Логи
- Логи cron
- Логи клиента Steam
- Логи веб-сервера
- Логи SourceMod
- Логи игровых серверов
- Логи сервера статистики
- Привязка к Steam и QuickPlay
- Серверная учётная запись
- QuickPlay
Установка MetaMod и SourceMod
Пришла пора внести свежую струю в процесс настройки совместного сосуществования наших серверов, а именно — добавить MetaMod и SourceMod.
Установка MetaMod: Source
Сначала установим плагин MetaMod: Source, который никаких игровых функций не добавляет, а лишь обеспечивает интерфейс между движком Source, игрой и другими плагинами.
Домашняя страница — http://www.metamodsource.net/
Скачиваем последнюю версию и распаковываем архив.
$ wget http://cdn.probablyaserver.com/sourcemod/mmsource-1.10.6-linux.tar.gz
$ tar -xvzf mmsource-1.10.6-linux.tar.gz -C ~/tf2/tf
В каталоге ~/tf2/tf будет создан подкаталог addons (в него мы будем устанавливать и другие моды), а в нём каталог metamod с рядом файлов для различных Source игр, в том числе и для Team Fortress 2:
addons/
addons/metamod.vdf
...
addons/metamod/bin/metamod.2.tf2.so
addons/metamod/bin/server.so
...
addons/metamod/metaplugins.ini
Игровой сервер srcds при запуске проверяет наличие каталога ~/tf2/tf/addons, ищёт в нём *.vdf файлы, в которых в параметре «file» должны быть указаны пути к библиотекам плагинов. В нашем случае, как раз в таком файле ~/tf2/tf/addons/metamod.vdf подключен плагин MetaMod: Source как «addons/metamod/bin/server» (путь указывается относительно ~/tf2/tf/).
Теперь мы установим SourceMod и подключим его к MetaMod.
Установка SourceMod
Домашняя страница — http://www.sourcemod.net/
Переходим на http://www.sourcemod.net/downloads.php? branch=stable, скачиваем последнюю версию под Linux и распаковываем архив
$ wget https://sm.alliedmods.net/smdrop/1.8/sourcemod-1.8.0-git5947-linux.tar.gz
$ tar -xvzf sourcemod-1.8.0-git5947-linux.tar.gz -C ~/tf2/tf
В ~/tf2/tf/addons появился каталог sourcemod, в ~/tf2/tf/cfg/ — каталог sourcemod с файлами конфигурации, а в
~/tf2/tf/addons/metamod — файлик sourcemod.vdf.
addons/metamod/sourcemod.vdf
addons/sourcemod/
addons/sourcemod/scripting/
addons/sourcemod/configs/core.cfg
...
cfg/sourcemod/sm_warmode_off.cfg
cfg/sourcemod/sm_warmode_on.cfg
cfg/sourcemod/sourcemod.cfg
Так как одна инсталляция SourceMod не может одновременно обслуживать несколько серверов, то нам необходимо растиражировать по количеству серверов. В каталоге addons переименовываем sourcemod в sourcemod1 и копируем со всем содержимым в sourcemod2, предварительно для удобства выполнив dos2unix для файлов настроек:
$ mv ~/tf2/tf/addons/sourcemod ~/tf2/tf/addons/sourcemod1
$ cd ~/tf2/tf/addons/sourcemod1/configs
$ dos2unix *.cfg *.ini *.txt
$ cp -r ~/tf2/tf/addons/sourcemod1 ~/tf2/tf/addons/sourcemod2
MetaMod находит и подключает SourceMod благодаря файлу addons/metamod/sourcemod.vdf. Но этот файл один, а нам надо два разных, для каждого сервера. У MetaMod есть параметр, указывающий на каталог (не файл!) с конфигурацией, поэтому создадим два каталога cfg1 и cfg2 и поместим туда sourcemod.vdf, который и будем править.
$ mkdir ~/tf2/tf/addons/metamod/cfg{1,2}
$ dos2unix ~/tf2/tf/addons/metamod/sourcemod.vdf
$ mv ~/tf2/tf/addons/metamod/sourcemod.vdf ~/tf2/tf/addons/metamod/cfg1/sourcemod.vdf
$ cp ~/tf2/tf/addons/metamod/cfg1/sourcemod.vdf ~/tf2/tf/addons/metamod/cfg2/sourcemod.vdf
Редактируем эти файлы, исправляя пути к инсталляциям SourceMod (так же, относительно ~/tf2/tf):
~/tf2/tf/addons/metamod/cfg1/sourcemod.vdf:
"Metamod Plugin"
{
"alias" "sourcemod"
"file" "addons/sourcemod1/bin/sourcemod_mm"
}
~/tf2/tf/addons/metamod/cfg2/sourcemod.vdf (исправляем 1 → 2):
"Metamod Plugin"
{
"alias" "sourcemod"
"file" "addons/sourcemod2/bin/sourcemod_mm"
}
Для удобства делаем символьные ссылки на каталоги с настройками и логами:
$ ln -s ~/tf2/tf/addons/metamod/cfg1 ~/cfg/mm1
$ ln -s ~/tf2/tf/addons/metamod/cfg2 ~/cfg/mm2
$ ln -s ~/tf2/tf/addons/sourcemod1/configs ~/cfg/sm1
$ ln -s ~/tf2/tf/addons/sourcemod2/configs ~/cfg/sm2
$ ln -s ~/tf2/tf/addons/sourcemod1/logs ~/log/sm1
$ ln -s ~/tf2/tf/addons/sourcemod2/logs ~/log/sm2
Каких-то отдельных команд для запуска MetaMod прописывать не надо — игровой сервер запустит его автоматически, но нам надо указать различные каталоги для загрузки SourceMod.
В файле ~/cfg/autoexec1.cfg для первого сервера дописываем в конец:
//...
// Load SourceMod instance No 1
mm_basedir addons/metamod/cfg1
Для второго сервера в ~/cfg/autoexec2.cfg аналогично, только вместо «cfg1» → «cfg2»:
//...
// Load SourceMod instance No 2
mm_basedir addons/metamod/cfg2
Теперь MetaMod знает откуда загружать SourceMod, но последний не знает откуда ему брать свои настройки. Пропишем пути для каждого SourceMod. В отличие от MetaMod, параметры нам придётся указывать аж в командной строке запуска сервера. Ни в autoexec.cfg, ни в server.cfg они не срабатывают.
В скриптах запусках start1.sh и start2.sh, в CMDLINE дописываем:
CMDLINE="...
+sm_basepath addons/sourcemod1 +sm_corecfgfile addons/sourcemod1/configs/core.cfg \
...
Это для первого сервера. Для второго сервера аналогично, только вместо «sourcemod1» → «sourcemod2»:
CMDLINE="...
+sm_basepath addons/sourcemod2 +sm_corecfgfile addons/sourcemod2/configs/core.cfg \
...
Параметр sm_corecfgfile необходим, так как путь к core.cfg не берётся из sm_basepath, что логично было бы предположить.
Для проверки можно запустить первый игровой сервер и когда он полностью запустится, в его консоли ввести (команды выделены угловыми скобками):
>>> meta version
Metamod:Source version 1.10.6
Built from: https://github.com/alliedmodders/metamod-source/commit/9fed12f
Build ID: 946:9fed12f
Loaded As: Valve Server Plugin
Compiled on: Sep 12 2015
Plugin interface version: 15:14
SourceHook version: 5:5
http://www.metamodsource.net/
>>> mm_basedir
"mm_basedir" = "addons/metamod/cfg1" ( def. "addons/metamod" )
singleplayer
- Metamod:Source Base Folder
>>> meta list
Listing 4 plugins:
[01] SourceMod (1.8.0.5907) by AlliedModders LLC
[02] TF2 Tools (1.8.0.5907) by AlliedModders LLC
[03] SDK Hooks (1.8.0.5907) by AlliedModders LLC
[04] SDK Tools (1.8.0.5907) by AlliedModders LLC
>>> meta info 1
Plugin 1 is running.
Name: "SourceMod" by AlliedModders LLC
Version: 1.8.0.5907
Description: Extensible administration and scripting system
License: See LICENSE.txt
URL: http://www.sourcemod.net/
Details: API 015, Date: Apr 26 2016
File: /home/game/tf2/tf/addons/sourcemod1/bin/sourcemod_mm_i486.so
>>> sm_basepath
"sm_basepath" = "addons/sourcemod1" ( def. "addons/sourcemod" )
- SourceMod base path (set via command line)
>>> sm_corecfgfile
"sm_corecfgfile" = "addons/sourcemod1/configs/core.cfg" ( def. "addons/sourcemod/configs/core.cfg" )
- SourceMod core configuration file
>>> sm plugins list
[SM] Listing 17 plugins:
01 "Admin Menu" (1.8.0.5907) by AlliedModders LLC
...
17 "Player Commands" (1.8.0.5907) by AlliedModders LLC
>>> sm exts list
[SM] Displaying 9 extensions:
[01] Automatic Updater (1.8.0.5907): Updates SourceMod gamedata files
...
[09] SQLite (1.8.0.5907): SQLite Driver
Ну что же, все пути к каталогам и файлам верны, семнадцать стандартных плагинов и девять расширений запущены. Позже мы разберём кто что делает, а пока продолжим.
Если же при вводе команд выдаются ошибки вида:
>>> meta list
Listing 1 plugin:
[01]
>>> sm_basepath
Unknown command "sm_basepath"
То значит MetaMod не смог загрузить SourceMod. Надо искать в чём дело.
Перечень консольных команд MetaMod: Source — Console Commands (SourceMM)
Расширения находятся в каталоге ~/tf2/tf/addons/sourcemod{1,2}/extensions — для всех поддерживаемых игр на движке Source, не только TF2.
Скомпилированные плагины находятся в каталоге ~/tf2/tf/addons/sourcemod{1,2}/plugins. Если требуется отключить какой-нибудь плагин, то его можно просто перенести в подкаталог disabled. Сами исходные тексты плагинов находятся каталоге ~/tf2/tf/addons/sourcemod{1,2}/scripting. Там же есть компилятор и скрипт compile.sh, который компилирует исходники и помещает скомпилированные плагины в подкаталог compiled, откуда их можно перенести в ~/tf2/tf/addons/sourcemod{1.2}/plugins — и они станут доступны для использования SourceMod.
Теперь можно настроить SourceMod для каждого сервера, редактируя файлы core.cfg в ~/tf2/tf/addons/sourcemod{1,2}/configs. В нашем случае удовлетворимся параметрами по умолчанию. Обратите внимание, что при этом параметром «DisableAutoUpdate No» включено автообновление игровых данных SourceMod в каталогах ~/tf2/tf/addons/sourcemod{1,2}/gamedata.
Так же можно в core.cfg установить параметру «LogMode» значени «game» — тогда логи станут записываться не в отдельные файлы, а в логи игрового сервера. Подробнее в разделе «Логи»
Прочие файлы конфигурации разберём позже.
Однако есть ещё один файл конфигурации — ~/tf2/tf/cfg/sourcemod/sourcemod.cfg — он выполняется при каждой смене карт следом за server.cfg и является общим для всех инсталляций SourceMod. Поэтому оставляем в sourcemod.cfg лишь команды, общие для обоих серверов, а индивидуальные переносим в отдельные файлы для каждого сервера.
$ dos2unix ~/tf2/tf/cfg/sourcemod/sourcemod.cfg
$ cp ~/tf2/tf/cfg/sourcemod/sourcemod.cfg ~/cfg/sourcemod_default.cfg
$ ln -s ~/tf2/tf/cfg/sourcemod ~/cfg/sm
Редактируем ~/tf2/tf/cfg/sourcemod/sourcemod.cfg, традиционно добавляем вывод «echo», исправляем «sm_show_activity» и устанавливаем в «sm_datetime_format» привычный формат вывода даты/времени.
echo "*** ~/tf2/tf/cfg/sourcemod/sourcemod.cfg"
// SourceMod Configuration File
// This file is automatically executed by SourceMod every mapchange.
// Specifies how admin activity should be relayed to users.
sm_show_activity 28
// Default datetime formatting rules when displaying to clients.
sm_datetime_format "%d %b %Y - %H:%M:%S"
Всё. Остальное удаляем. Позже, когда захочется установить для серверов свои параметры, то можно взять их из сохранённого ~/cfg/sourcemod_default.cfg и указать в каком-либо файле конфигурации сервера. Почему нельзя оставить всё как есть — в оригинальном sourcemod.cfg? А потому что он выполняется при каждой смене карт следом за server{1,2}.cfg — то есть любые установки, например, касающиеся резервных слотов, прописанные в server{1,2}.cfg будут перезаписаны значениями по умолчанию из этого файла. А те же резервные слоты мы будем настраивать для наших серверов по-разному.
При установке новых плагинов SourceMod, их файлы конфигурации могут автоматически создаваться в ~/tf2/tf/cfg/sourcemod/ и становиться общими для обеих инсталляций SourceMod. Собственно, там уже лежит файл с настройками для плагина funcommands.smx
Так же SourceMod по умолчанию периодически скачивает со своего сервера (update.sourcemod.net) обновления файлов с игровыми данными, что найдёт своё отражение в логах. На примере первого сервера (~/tf2/tf/addons/sourcemod1/logs/), строчки 3,4,5:
L 03/21/2014 - 14:52:41: SourceMod log file session started (file "L20140321.log") (Version "1.5.3")
L 03/21/2014 - 14:52:41: -------- Mapchange to cp_well --------
L 03/21/2014 - 14:52:56: [UPDATER] Successfully updated gamedata file "sdkhooks.games/engine.csgo.txt"
L 03/21/2014 - 14:52:56: [UPDATER] Successfully updated gamedata file "sdktools.games/engine.csgo.txt"
L 03/21/2014 - 14:52:56: [UPDATER] Successfully updated gamedata file "sm-cstrike.games/game.csgo.txt"
L 03/21/2014 - 14:52:56: [UPDATER] SourceMod has been updated, please reload it or restart your server.
L 03/21/2014 - 14:55:26: SourceMod log file session started (file "L20140321.log") (Version "1.5.3")
L 03/21/2014 - 14:55:26: -------- Mapchange to cp_well --------
Документация по SourceMod — настройка, управление, небольшой FAQ (eng).
Так как очередные обновления Team Fortress 2 могут нарушать хрупкое взаимодействие игры и SourceMod, то имеет смысл отслеживать новые версии и билды и регулярно обновляться.
Продолжение с описанием настройки плагинов и интерактивным взаимодействием игроков с сервером описано в разделе «Плагины SourceMod», а пока двинемся дальше — к настройке запуска серверов.
Автозапуск игровых серверов
Наши игровые сервера уже достаточно взрослые для того, чтобы можно было их включать в автозапуск. Для систем с System V init можно переделать ранее созданные скрипты запуска, либо использовать примеры из TF2 Wiki. Для систем с systemd, как у нас, делаем два файла служб, незатейливо именуемых srcds1.service и srcds2.service.
Справочно, для освежения материала: После всех добавлений (Записи, SourceMod), скрипт запуска того же первого сервера ~/start1.sh в полной комплектации должен был выглядеть как-то так:
#!/bin/sh
#
# Запуск первого сервера.
# Путь к каталогу с игрой, где лежит файл srcds_run
GAMEFOLDER=/home/game/tf2
CMDLINE="+sv_pure 2 -game tf +maxplayers 24 \
-pidfile ${GAMEFOLDER}/tf/srcds1.pid \
-ugcpath ${GAMEFOLDER}/steamapps/workshop1 \
-replay reply1.cfg -replayserverdir server1 \
+exec autoexec1.cfg +servercfgfile server1.cfg \
-port 27015 -steamport 26900 +clientport 27005 +tv_port 27020 -strictportbind \
+sm_basepath addons/sourcemod1 +sm_corecfgfile addons/sourcemod1/configs/core.cfg"
# Запускаем игровой сервер
${GAMEFOLDER}/srcds_run ${CMDLINE}
Мы уже привыкли к скрипту srcds_run, поэтому будем и дальше использовать его. Надо только озаботиться автоматическим перезапуском игрового серевера при выполнении команд quit
, _restart
. Сейчас скрипт перезапускает сервер только если он закончился аварийно с ненулевым кодом выхода, либо с нулевым при включенном автообновлении.
Вариантов несколько. Первый — можно просто включить стандартное автообновление (секция «Автоматическое» в разделе «Обновление серверов»). Второй — создать копию скрипта с другим именем и поправить его на предмет вечного перезапуска сервера (исходный скрипт править не стоит — он может замениться при обновлении) и использовать его в дальнейшем. Третий — если планируется обновлять сервера самостоятельно, то можно включить автообновление, добавив -autoupdate и два его параметра-сателлита в командную строку запуска, но в качестве скрипта автообновления использовать файлик типа ~/cfg/tf2_quit, состоящий лишь из одной строчки с командой quit, то есть фактически, обновления не будут проверяться.
Знания умножают печали. Но выбор за вами. В нашем же случае, для первого сервера мы в файлах конфигурации ещё не прописывали критерии окончания раундов, а второй сервер вообще будет с нестандартными картами, вполне возможно, что и с бесконечными раундами, поэтому в автозапуске будем использовать третий вариант — с фиктивным автообновлением, а настоящее обновление — через регулярный запуск update.sh скрипта в crontab. Опять же, так интереснее.
Итак, файлы служб. Для удобства мы будем использовать терминальный мультиплексор tmux (желающие могут использовать вместо него screen), который будет запускать скрипт srcds_run. Параметры командной строки перешли практически неизменными из скриптов запуска, которые мы использовали до этого.
Так как игровые сервера запускаются от имени пользователя game, то по-хорошему, можно было бы создавать файлы запуска в пользовательском окружении, в ~/.config/systemd/user/. Но данный раздел документации тестировался на centos 7, где запуск systemd в пользовательском режиме не функционирует, поэтому файлы служб мы создаём от имени root.
Заходим как root, создаём файл /etc/systemd/system/srcds1.service для первого сервера.
# /etc/systemd/system/srcds1.service
#
# Team Fortress 2 Source Dedicated Server
[Unit]
Description=Team Fortress 2 Source Dedicated Server No 1
After=network.target
[Service]
User=game
Group=game
WorkingDirectory=/home/game/Steam
Type=oneshot
RemainAfterExit=yes
PIDFile=/home/game/tf2/tf/srcds1.pid
ExecStart=/usr/bin/tmux -L socket1 new-session -d '/home/game/tf2/srcds_run \
+sv_pure 2 -game tf +maxplayers 24 \
-pidfile /home/game/tf2/tf/srcds1.pid \
-ugcpath /home/game/tf2/steamapps/workshop1 \
-replay replay1.cfg -replayserverdir server1 \
+exec autoexec1.cfg +servercfgfile server1.cfg \
-port 27015 -steamport 26900 +clientport 27005 +tv_port 27020 -strictportbind \
-autoupdate -steam_dir /home/game/Steam -steamcmd_script /home/game/cfg/tf2_quit \
+sm_basepath addons/sourcemod1 +sm_corecfgfile addons/sourcemod1/configs/core.cfg'
ExecStop=/usr/bin/tmux -L socket1 send-keys "quit" Enter ; /usr/bin/sleep 5s ; /usr/bin/tmux -L socket1 kill-session
ExecReload=/usr/bin/tmux -L socket1 send-keys "_restart" Enter
ExecStopPost=/usr/bin/rm -f /home/game/tf2/tf/srcds1.pid
[Install]
WantedBy=multi-user.target
Небольшое пояснение по параметрам ExecStop и ExecReload. Для завершения работы сервера мы посылаем ему лучи любви и команду quit
в консоль, ждём несколько секунд для завершения работы, и, пока всё не запустилось заново, прибиваем сессию. Схожий механизм для рестарта сервера. При желании можно добавить вывод предупреждения игрокам о грядущем рестарте — как это было в примере одного из скриптов для обновления, в соответствующем разделе.
Копируем srcds1.service в srcds2.service, корректируем пути, порты и имя tmux сокета. Сохраняем. /etc/systemd/system/srcds2.service:
# /etc/systemd/system/srcds2.service
#
# Team Fortress 2 Source Dedicated Server
[Unit]
Description=Team Fortress 2 Source Dedicated Server No 2
After=network.target
[Service]
User=game
Group=game
WorkingDirectory=/home/game/Steam
Type=oneshot
RemainAfterExit=yes
PIDFile=/home/game/tf2/tf/srcds2.pid
ExecStart=/usr/bin/tmux -L socket2 new-session -d '/home/game/tf2/srcds_run \
+sv_pure 2 -game tf +maxplayers 24 \
-pidfile /home/game/tf2/tf/srcds2.pid \
-ugcpath /home/game/tf2/steamapps/workshop2 \
-replay replay2.cfg -replayserverdir server2 \
+exec autoexec2.cfg +servercfgfile server2.cfg \
-port 27016 -steamport 26901 +clientport 27006 +tv_port 27021 -strictportbind \
-autoupdate -steam_dir /home/game/Steam -steamcmd_script /home/game/cfg/tf2_quit \
+sm_basepath addons/sourcemod2 +sm_corecfgfile addons/sourcemod2/configs/core.cfg'
ExecStop=/usr/bin/tmux -L socket2 send-keys "quit" Enter ; /usr/bin/sleep 5s ; /usr/bin/tmux -L socket2 kill-session
ExecReload=/usr/bin/tmux -L socket2 send-keys "_restart" Enter
ExecStopPost=/usr/bin/rm -f /home/game/tf2/tf/srcds2.pid
[Install]
WantedBy=multi-user.target
Здесь мы специально запускаем две отдельные копии tmux, каждую для своего игрового сервера. Можно было бы, конечно, обойтись сессиями внутри одного tmux (tmux new-session -d -s tf1 и tmux new-session -d -s tf2), но тогда это стало бы дополнительной точкой для epic fail — когда нечаянный Ctrl-C прибил бы все наши сервера.
Не забываем создать файлик ~/cfg/tf2_quit с содержимым:
quit
Проверяем, что оба .service файла доступны на запись только для root, затем запускаем, включаем:
# systemctl start srcds1
# systemctl start srcds2
# systemctl enable srcds1
# systemctl enable srcds2
Но сейчас у нас глупая ситуация — игровые сервера стартуют при запуске нашего сервера, но пользователь game не может ими управлять стандартными способами с помощью systemctl (start, stop, reload), хотя вполне может ими «управлять» при помощи Ctrl+C в tmux сессии, с последующим запуском вручную. Исправляем несправедливость.
Пока мы ещё root, запускаем visudo
, корректируем файл sudoers, дописав в конец:
Defaults:game !requiretty
game ALL= NOPASSWD: /usr/bin/systemctl start srcds1.service, /usr/bin/systemctl start srcds2.service
game ALL= NOPASSWD: /usr/bin/systemctl stop srcds1.service, /usr/bin/systemctl stop srcds2.service
game ALL= NOPASSWD: /usr/bin/systemctl reload srcds1.service, /usr/bin/systemctl reload srcds2.service
game ALL= NOPASSWD: /usr/bin/systemctl status srcds1.service, /usr/bin/systemctl status srcds2.service
game ALL= NOPASSWD: /usr/bin/systemctl enable srcds1.service, /usr/bin/systemctl enable srcds2.service
game ALL= NOPASSWD: /usr/bin/systemctl disable srcds1.service, /usr/bin/systemctl disable srcds2.service
Здесь мы пользователю game предоставляем возможность посредством sudo, без запроса пароля (которого у него и нет) выполнять команды start, stop, reload, status и для кучи enable с disable для первого и второго сервера. А параметр «Defaults: game! requiretty» позволит нам запускать sudo из crontab файла. По желанию можно добавить разрешение на использование иных команд типа is-* и прочих.
Разрешения на команды вида systemctl edit [--full]
мы предоставлять не будем. Всё-таки безопасность должна быть… безопасной.
Выходим из-под root, проверяем из-под game:
$ sudo -l
Matching Defaults entries for game on this host:
...
..., !requiretty
User game may run the following commands on this host:
(root) NOPASSWD: /usr/bin/systemctl start srcds1.service, (root) /usr/bin/systemctl start srcds2.service
(root) NOPASSWD: /usr/bin/systemctl stop srcds1.service, (root) /usr/bin/systemctl stop srcds2.service
(root) NOPASSWD: /usr/bin/systemctl reload srcds1.service, (root) /usr/bin/systemctl reload srcds2.service
(root) NOPASSWD: /usr/bin/systemctl status srcds1.service, (root) /usr/bin/systemctl status srcds2.service
(root) NOPASSWD: /usr/bin/systemctl enable srcds1.service, (root) /usr/bin/systemctl enable srcds2.service
(root) NOPASSWD: /usr/bin/systemctl disable srcds1.service, (root) /usr/bin/systemctl disable srcds2.service
Можно тут же запустить sudo systemctl status srcds1.service
, потестировать.
Команды необходимо будет вводить без сокращения параметров. То есть sudo systemctl status srcds1.service — норм, а вот sudo systemctl status srcds1 — уже не торт.
Если при манипуляциях с игровыми серверами с помощью systemctl выдаётся ошибка вида «Failed to stop srcds1.service: Interactive authentication required.» — то значит вы забыли про sudo:-)
Когда всё заработает, можно будет работать с консолями серверов уже как пользователь game, подключаясь через tmux:
$ tmux -L socket1 attach
$ tmux -L socket2 attach
Отключаться от tmux сессии — Ctrl+b, d.
Можно прописать алиасы в ~/.bash_profile:
alias tf1="tmux -L socket1 attach"
alias tf2="tmux -L socket2 attach"
По умолчанию, размер хранимой истории для tmux составляет 2000 строк, но его можно увеличить, например до 10000 строк, создав файл ~/.tmux.conf вида:
set-option -g history-limit 10000
set-option -g set-remain-on-exit on
Вторая команда не закрывает tmux сессию при завершении основной программы, что даёт возможность почитать её последнее прости в консоли.
Перемещаться по истории — Ctrl+b, [, а далее обычными клавишами — Up/Down, PgUp/PgDown. Выход из этого режима — q.
Логи
Логов у нас будет много и разных. А именно:
Логи cron
У нас через cron запускаются скрипты проверки обновлений, сервера статистики HLstatsX, удаления устаревших Записей, а их вывод отражается в /var/log/cron в виде
Jun 15 12:55:01 server CROND[584]: (game) CMD (cd $HOME/stat/scripts && ./run_hlstats start 2 27500 1)
Jun 15 12:55:01 server CROND[583]: (game) CMDOUT (HLstatsX:CE daemon control)
Jun 15 12:55:01 server CROND[583]: (game) CMDOUT (http://www.hlxce.com)
Jun 15 12:55:01 server CROND[583]: (game) CMDOUT (---------------------------)
Jun 15 12:55:01 server CROND[583]: (game) CMDOUT (Daemon is already running on port 27500)
Jun 15 12:55:01 server CROND[583]: (game) CMDOUT (Daemon is already running on port 27501)
...
Так как /var/log/cron никто прочитать не сможет кроме пользователя root, а ему это не интересно, то хотелось бы дать доступ пользователю game к логам его crontab файла. Конечно, можно для каждого скрипта в конце строки запуска дописать что-то вида »… >> $HOME/log/cron.log», но эти «логи» будут без меток времени, да и вообще, кустарщина. Более культурный варинт »… | /usr/bin/logger --tag srcds» ничего не меняет — ну будут сообщения попадать не в /var/log/cron, а в /var/log/messages, разницы-то.
Поэтому мы в настройках демона rsyslog сделаем так, что сообщения от cron, начинающиеся с имени пользователя »(game)», перенаправляются в файл /home/game/log/cron.log, владельцем которого является пользователь game, и не попадают в дальнейшую обработку (в «cron.* /var/log/cron»)
У нас установлен rsyslogd версии 8.19, и в /etc/rsyslog.conf директива $IncludeConfig /etc/rsyslog.d/*.conf
находится до правила cron.* /var/log/cron
, поэтому перехватывать сообщения от наших скриптов будем, создав как пользователь root файл /etc/rsyslog.d/srcds-10-cron.conf с содержимым:
# /etc/rsyslog.d/srcds-10-cron.conf
if $syslogfacility-text == "cron" and $msg startswith " (game)" then {
action(
type = "omfile"
fileOwner = "game"
fileGroup = "game"
file = "/home/game/log/cron.log"
)
stop
}
Подробнее об использованных командах — omfile, properties.
Проверяем синтаксис, если всё ok, то перезапускаем демона и наслаждаемся свежесозданным логом.
# rsyslogd -N 2
# systemctl restart rsyslog
Пока мы ещё root, настраиваем ротацию логов, создав файл /etc/logrotate.d/srcds-cron:
#/etc/logrotate.d/srcds-cron
/home/game/log/cron.log {
su game game
daily
dateext
maxage 3
missingok
copytruncate
compress
notifempty
}
Проверяем:
# logrotate --debug --force /etc/logrotate.d/srcds-cron
Логи клиента Steam
Эти логи находятся в ~/Steam/logs/. Ротация происходит автоматически, силами самого клиента. Текущий лог переименовывается в *.previous.txt
, предыдущий *.previous.txt
предварительно удаляется. Специально мы их обрабатывать не будем, какой-то необходимости хранить исторические данные нет — текущие проблемы с подключением можно посмотреть в активных логах, а что было с коннектом к серверам Valve девятого термидора второго года — nobody cares…
Логи веб-сервера
У нас с игровыми серверами сотрудничают три виртуальных веб-сервера, обслуживающих:
- Fast Download, логи в /var/www/fastdl.example.org/log/
- Записи (Replay), логи в /var/www/replay.example.org/log/
- HLstatsX, логи в /var/www/stat.example.org/log/
Ротация этих логов у нас настроена в /etc/logrotate.d/srcds-nginx, предполагается, что логи с ошибками будем просматривать самостоятельно, а логи доступа можно скармливать хоть Elasticsearch, Kibana и Logstash —, но описание ELK стека вне объёма данного проекта.
Логи SourceMod
Эти логи находятся в каталогах ~/tf2/tf/addons/sourcemod{1,2}/logs. Их настройки прописаны в соответствующих ~/tf2/tf/addons/sourcemod{1,2}/configs/core.cfg:
Logging on
логи вкл/выкл
LogMode daily
ротация логов — ежедневная (daily), при смене карты (map), и третий режим (game) — писать в логи игрового сервера.
DebugSpew no
детализировать ли в логах обновление игровых данных
Логи SourceMod ведутся довольно скромно, поэтому достаточно настроить их регулярную ротацию, либо не заморачиваться и просто в core.cfg прописать LogMode game
, тогда они гармонично впишутся в логи игровых серверов, как-то так:
L 06/20/2016 - 04:53:24: Executing dedicated server config file server1.cfg
L 06/20/2016 - 04:53:32: server_cvar: "sv_tags" "HLstatsX:CE,cp,increased_maxplayers"
L 06/20/2016 - 04:53:32: tf_server_identity_account_id not set; not logging into registered account
L 06/20/2016 - 04:53:32: server_cvar: "sv_contact" "game@example.org"
L 06/20/2016 - 04:53:33: [UPDATER] Successfully updated gamedata file "core.games/common.games.txt"
L 06/20/2016 - 04:53:33: [UPDATER] Successfully updated gamedata file "sdkhooks.games/game.doi.txt"
L 06/20/2016 - 04:53:33: [UPDATER] Successfully updated gamedata file "sdkhooks.games/master.games.txt"
L 06/20/2016 - 04:53:33: [UPDATER] Successfully updated gamedata file "sdktools.games/game.doi.txt"
L 06/20/2016 - 04:53:33: [UPDATER] Successfully updated gamedata file "sdktools.games/master.games.txt"
L 06/20/2016 - 04:53:33: [UPDATER] SourceMod has been updated, please reload it or restart your server.
L 06/20/2016 - 04:53:33: Connection to Steam servers successful.
L 06/20/2016 - 04:53:33: Public IP is 192.0.2.0.
L 06/20/2016 - 04:53:33: Assigned anonymous gameserver Steam ID [A:1:123456789:1234].
L 06/20/2016 - 04:53:33: VAC secure mode is activated.
L 06/20/2016 - 04:53:34: server_cvar: "sm_nextmap" "cp_well"
Логи игровых серверов
Логи игровых серверов находятся в ~/log/server{1,2}/. Минимальную настройку логов мы провели при написании конфигурационных файлов, но сейчас рассмотрим значение некоторых переменных поподробнее:
log off
глобально включает (on) ведение логов данного игрового сервера. Логи могут направляться в файл (sv_logfile), на консоль сервера (sv_logecho), транслироваться по UDP (logaddress_add).
sv_logfile 1
включает запись логов в файл (1), при этом требуется включение предыдущей командой. Каталог для логов и формат имени файлов задаются параметрами sv_logsdir и sv_logfilename_format.
sv_logsdir logs
каталог для логов. По умолчанию, был бы ~/tf2/tf/logs, но так как мы логи наших двух серверов разводим по разным каталогам, то каталоги /home/game/log/server1 и /home/game/log/server2 соответственно. Хотя, конечно, можно было бы писать в один каталог, но с разным именованием файлов.
sv_log_onefile 0
вести запись логов в один файл (1), либо при смене карты создавать новый (0), например: l0618000.log → l0618001.log → l0618002.log… l0619000.log → l0619001.log и так далее. Отметим две особенности при включении данного параметра: во-первых, при наступлении новых суток автоматической ротации лога l0618000.log → l0619000.log не произойдёт; во-вторых, если в консоли или посредством какого-нибудь файла конфигурации сделать log off и тут же log on, то запись начнётся в новый файл, с инкрементированным номером.
sv_logfilecompress 0
при начале записи в новый файл логов, старый будет сжиматься gzip и переименовываться в
. Задумка хорошая, реализация под Linux хромает. Так, при установке этой переменной в »1» и смене карты (что, при нашем sv_log_onefile = »0» вызывает запись логов в новый файл), на консоли сервера появляется ругань, создаётся пустой .gz файл, а старый файл лога не удаляется. А всё из-за регистра первого символа в имени файла — «l» vs «L», что принципиально в Linux:
---- Host_Changelevel ----
Compressing /home/game/log/server2/L0713019.log to /home/game/log/server2/L0713019000.log.gz...
Success. Removing /home/game/log/server2/L0713019.log.
Unable to remove /home/game/log/server2/L0713019.log!
Unable to remove /home/game/log/server2/L0713019.log!
Server logging data to file /home/game/log/server2/L0713020.log
Applying new item schema, version 5C0BC93D
sv_logfilename_format ""
формат именования файлов с логами. Должен указываться до параметра «log on». Если специально не указывать, то файлы с логами сейчас именуются как l<Месяц><День><000...999>.log
, где первый символ —
, а последнее трёхзначное число в имени инкрементируется для каждого нового лога, в пределах текущих суток. Можно задавать свой формат, с использованием параметров функции strftime. То есть, при указании «sv_logfilename_format %Y%m%d» файл будет выглядеть как »20160714.log». Автонумерация в этом случае будет вида 20160714.log → 20160714_000.log → 20160714_001.log и так далее. Забавно, но при явной установке формата, начинает работать параметр sv_logfilecompress — предыдущий файл лога действительно архивируется.
log_verbose_enable 0
включает (1) подробное ведение логов. Так, каждые несколько секунд (определяется log_verbose_interval) туда пишутся текущие трёхмерные координаты игроков. Лог разрастается неимоверно. Включать, пожалуй, имеет смысл разве что для построения тепловых карт движения игроков, с селекцией по классам. Что-то вроде http://geit.uk/blog/player-flow/.
log_verbose_interval 3.0
задаёт интервал записи подробного лога. По умолчанию — каждые 3 секунды.
sv_rcon_log 1
при отключении (0), не выводит на консоль и в лог поступающие rcon команды (результаты выполнения команд выводятся на консоль по-прежнему). Так как в нашей конфигурации rcon использует исключительно HLstatsX, то экономия в части размеров логов невелика, но всё же спама на консоли сервера будет поменьше, без вот этого:
rcon from "192.0.2.0:60533": command "status"
rcon from "192.0.2.0:60533": command "hlx_sm_psay "30" 2 "Round Over - All actions/frags are ignored <...>
rcon from "192.0.2.0:60533": command "hlx_sm_psay "30" 1 "Red got 10 points for Round Win""
rcon from "192.0.2.0:60533": command "hlx_sm_psay "30" 1 "Asuka got 2 points (1,025) for Kill Assist""
sv_logecho 1
дублирует вывод лога на консоль. Естественно, отключаем (0).
sv_logbans 0
при включении (1) фиксирует информацию о бане игрока в лог:
L 07/13/2016 - 23:09:31: Addip: "<><><>" was banned by IP "for 10.00 minutes" by "Console" (IP "10.1.1.1")
logaddress_add
указывает адрес: порт, куда будут транслироваться логи по UDP, в нашем случае для HLstatsX. Первому серверу мы устанавливали 192.0.2.0:27500, второму — 192.0.2.0:27501
logaddress_del
удаляет адрес: порт из трансляции.
logaddress_delall
удаляет все адреса из трансляции
developer 0
обеспечивает более подробный вывод информации на консоль сервера. Варианты значений — 0 (по умолчанию), 1, 2, 3…
Вот логи игровых серверов имеет смысл хранить некоторое время, на случай жалоб на того или иного игрока — мат в чате, оскорбление других игроков, двести восемьдесят вторая статья и так далее.
Обычно логи сервера при полной загрузке и интенсивной движухе растут на 3.5 — 4.0 тыс. строк в час, около 0.5 — 0.6 Мб.
Запись логов ведётся таким способом, что если запускать ротацию логов с помощью logrotate без параметра copytruncate в файле настроек, то после неё новых логов мы не увидим до смены карты (и то, при sv_log_onefile 0). Запуск же с параметром copytruncate приводит к обрезанию старого лога посередине строки (как повезёт — так как игровые сервера, если не включать sv_logflush 1, буферизируют запись логов в файл, причём не построчно, а покилобайтно) и бинарному мусору в начале нового файла, да так, что при уже его ротации, архив состоит из одной строки «Binary file (standard input) matches».
Учитывая это, работу с логами серверов можно организовать следующими способами:
Очень ленивый. Ничего не делать. При наличии инстинкта самосохранения всё же обеспечить удаление старых логов. Ну или просто отключить их ведение.
Ленивый. Для обеспечения ротации логов и экономии места на диске включить комбинацию «sv_log_onefile 0», «sv_logfilecompress 1» и «sv_logfilename_format %Y%m%d», добавить в crontab
find $HOME/log/server{1,2}/*.gz -type f -mtime +30 -delete
, что позволит обойтись без logrotate. Хороший, годный вариант. В случае «бесконечных» карт можно либо уповать на время от времени случающиеся обновления серверов, при перезапуске гарантирующие ротацию логов, либо принудительно перезапускать карту хотя бы раз в неделю — добавить в crontab запуск командыtmux -L socket1 send-keys "changelevel_next" Enter
в часы минимальной загрузки.- Активный. Если есть необходимость мониторить логи в реальном режиме времени, как-то их анализировать и обрабатывать, то можно использовать возможности rsyslog демона. Плагин omfile — File Output Module мы использовали выше в обработке лога cron, а здесь можно использовать imfile — Text File Input Module.
Попробуем настроить сохранение чата игроков первого сервера в отдельном логе. Cоздаём (из-под root) файл /etc/rsyslog.d/srcds-20-chat.conf:
#/etc/rsyslog.d/srcds-20-chat.conf
module(load="imfile" mode="inotify")
# Мониторит все *.log файлы первого сервера
input(
type = "imfile"
tag = "srcds1:"
file = "/home/game/log/server1/*.log"
ruleset = "chat-1"
)
# $now - текущая дата, на момент записи лога, в формате YYYY-MM-DD
template (name="chat1-log-name" type="string" string="/home/game/log/chat1-%$now%.log")
# Мониторим общий чат (say), командный (say_team), и steam id и ip игроков, входящих на сервер
ruleset(name = "chat-1") {
if ($msg contains [" say ", " say_team ", " connected, address "]) then {
action(
type = "omfile"
fileOwner = "game"
fileGroup = "game"
dynafile = "chat1-log-name"
)
}
}
При таких настройках, лог чата будет выглядеть как:
Jul 23 10:40:46 server srcds1: L 07/23/2016 - 10:40:27: "Asuka<3><[U:1:12345678]>" say_team "Valar morghūlis"
Jul 23 10:40:46 server srcds1: L 07/23/2016 - 10:40:34: "Rei<4><[U:1:09876543]>" say "Valar dohaeris, desu"
Тут стандартный формат rsyslogd — дата+время, имя сервера (железного), метка из input, и сама строчка лога. Отличие в секундах вызвано буферизацией записи игровых логов. При желании можно настроить дополнительную проверку текста сообщений на ключевые слова с немедленным извещением нас по электронной почте (модуль ommail в rsyslog)
Проверяем синтаксис, если всё ok, то перезапускаем демона.
# rsyslogd -N 2
# systemctl restart rsyslog
Если новый лог будете создавать в том же каталоге, откуда считываются исходные (параметр file в секции input), то убедитесь, что его имя не попадает под маску файлов из секции input, которые мониторит rsyslogd. А то получится очень, гм… рекурсивненько.
Возвращаясь к настройкам ведения логов, стоит отметить ещё два параметра:
con_logfile ""
создаёт отдельный файл лога, куда записывается всё что выводится на консоль сервера. Имя файла указывается либо с полным путём, либо относительно каталога ~/tf2/tf/ (не logs!). Если не указывать расширение ».log», то оно добавится автоматически. Имеет аналогичный параметр командной строки -consolelog
— только там лучше уже указывать расширение, а то получится забавно — при старте сервера создадутся три консольных лога: сначала запись пойдёт в ~/tf2/bin/
, затем в ~/tf2/tf/
, ну и наконец в ~/tf2/tf/
.
con_timestamp 0
при включении (1), весь вывод в консольный лог будет предваряться метками времени, как в примере ниже. Если указывать в командной строке запуска вместе с предыдущим параметром, как +con_timestamp 1 -consolelog
, то метки начнутся лишь с третьего файла.
В отличие от серверных логов, запись в консольный лог идёт построчно, и вполне корректно работает logrotate с параметром copytruncate.
Следует быть осторожным с включением записи этого, безусловно полезного лога. Иногда на игровой сервер что-то находит, и он начинает мощно спамить в консоль сообщениями вида:
07/15/2016 - 14:59:59: DataTable warning: player: Out-of-range value (72483.898438/65536.000000) in SendPropFloat 'm_flLastDamageTime', clamping.
07/15/2016 - 14:59:59: DataTable warning: player: Out-of-range value (72492.539062/65536.000000) in SendPropFloat 'm_flLastDamageTime', clamping.
07/15/2016 - 14:59:59: DataTable warning: player: Out-of-range value (72479.742188/65536.000000) in SendPropFloat 'm_flLastDamageTime', clamping.
При скорости потока, как правило ~ 125 сообщений в секунду, такими темпами файл с консольным логом прирастает на ~ 64 Мб в час, 1.5 Гб в сутки, что особенно неприятно в случае «долгоиграющих» карт, так как при смене карты эта ошибка обычно исчезает. Лечится либо прописыванием критериев, обеспечивающих регулярную ротацию карты, либо смирением с полуторагигабайтным логом в сутки, тем более, что сжимаются они неплохо, а хранить их можно недолго.
Настраиваем ротацию и этих логов, создав из-под root файл /etc/logrotate.d/srcds-server:
#/etc/logrotate.d/srcds-server
/home/game/log/console*.log
{
su game game
daily
dateext
rotate 7
copytruncate
notifempty
missingok
compress
# compresscmd /home/game/logrotate-filter.sh
}
Проверяем логику работы:
# logrotate --debug --force /etc/logrotate.d/srcds-server
Можно туда добавить костыль — строчку «compresscmd /home/game/logrotate-filter.sh», а сам ~/logrotate-filter.sh вида:
#!/bin/sh
/bin/grep -v ": DataTable warning:" | /bin/gzip -6
то есть в качестве архиватора будет вызываться не gzip, а этот скрипт, который входной поток пропустит через grep, вырезая DataTable warning, а остальное передавая gzip, as planned.
Кстати, как вариант — можно включить дублирование лога в консоль (sv_logecho 1), выключить ведение основного лога (sv_logfile 0) и активировать