Counter Strike: Global Offensive и Docker

Захотелось немного описать процесс, который прошел я с самого начала, до текущего момента. Процесс именно усовершенствования выделенного сервера, от запуска его в ручную до практически полного автоматизма. Все начиналось банально, я случайно наткнулся на статью о установке сервера CS:GO, тут я понял, что сервера в этой игре может поставить любой желающий, честно говоря после Battlefield, где в открытом доступе ничего нет я уже и забыл, что так бывает. Оказалось, что у стима есть инструмент называемый SteamCMD, это по сути консольный клиент, но из него можно скачивать только серверные части игр. Некоторые с авторизацией, некоторые без. Процесс установки и настройки любой игры есть на сайте Valve, описывать его не буду, он тривиален. Дальше еще немного интересней, для некоторых игр, есть скрипт, ссылка на который опять же, есть на сайте Valve. Этот скрипт используя SteamCMD, устанавливает, апдейтит, следит за аптаймом и т.п.

Я очень долго им пользовался, но у него есть минус, если он работает внутри докера, то все нужно делать в ручную, т.к. весь скрипт построен на tmux и отсутствие интерактивной консоли у докера убивает почти весь функционал. То есть если вы хотите просто на одном хосте рядом поднять 10 серверов кс, то это делается элементарным копированием этого скрипта и изменением его внутренних параметров. В моем же случае, идея была, запускать каждый сервер как отдельный контейнер. Я достаточно долго ничего не менял, просто запускал сервера в ручную, если к примеру перезапускал железо.

В отпуске у меня было время подумать, я набросал небольшой план по автоматизации всего этого дела. У меня было несколько проблем. Первая и одна из самых главных, это размер. Первые пару серверов, которые я поднял делались в лоб, я просто копировал целиком папку, клал в нее скрип для запуска и запускал контейнер.

docker run -i -t —rm —cpuset=0,1 -v /etc/localtime:/etc/localtime:ro -h hsdm.local -c 800 -p 26901:26901/udp -p 27015:27015 -p 27015:27015/udp -p 27005:27005/udp -p 27020:27020/udp —link mysql_inot.pro:db001.local -v /opt/csgo:/home/csgoserver —name csgo inotkaa/csgo bash

Если вкратце. Контейнер создается в интерактивном режиме и удаляется после остановки использует только 2 ядра имеет 800 акций из 1000(приоритет машины в системе) и монтируется из папки на хосте, в папку в контейнере. Остальное это часовой пояс как на хосте, имя хоста, открытые порты, линк к БД(изначально этого не было, когда серверов было два) и собственно интерактивный запуск в bash. В консоли я просто запускал сервер

# su - csgoserver
> ./csgoserver start

и он висел у меня открытый в screen’e.

Пара слов про приоритет, когда я не указывал акции, сложно было работать даже с консолью, при этом загрузки прям огромной не было, видимо CS, берет все ресурсы и не отдает. То же самое касается и ядер, изначально я ничего не указывал, уже в дальнейшем начал разбрасывать сервера по ядрам.

Все тут хорошо, все работает, но как я уже говорил, все руками. Т.к. каждый сервер это примерно 15гиг, а на сервере там еще и моя файловая помойка, то тратить место на диске, под одинаковые к тому же файлы как-то не интересно. Я выделил, только те, папки в которых я производил изменения, это cfg и addons и положил для каждого сервера их отдельно. Строчка запуска немного изменилась.

#!/bin/bash
ADDONS=’/opt/csgo_conf/retakes/addons:/home/csgoserver/serverfiles/csgo/addons’
CFG=’/opt/csgo_conf/retakes/cfg:/home/csgoserver/serverfiles/csgo/cfg’
LOGS=’/opt/csgo_conf/logs:/home/csgoserver/serverfiles/csgo/logs’
EXE=’/opt/csgo_conf/retakes/csgoserver:/home/csgoserver/csgoserver’
MAPLIST=’/opt/csgo_conf/retakes/maplist.txt:/home/csgoserver/serverfiles/csgo/maplist.txt’
MAPCYCLE=’/opt/csgo_conf/retakes/mapcycle.txt:/home/csgoserver/serverfiles/csgo/mapcycle.txt’
NAME=’csgo_retakes’
HOSTNAME=’retakes.local’
PORTS=’-p 27017:27017 -p 27017:27017/udp -p 27007:27007/udp -p 27022:27022/udp’

cd /opt/docker/csgo ; docker build -t inotkaa/csgo . ; docker run -i -t —rm —cpuset-cpus=0,1 $PORTS -v /etc/localtime:/etc/localtime:ro -h $HOSTNAME -c 800 —link mysql_inot.pro:db001.local -v /opt/csgo_ref:/home/csgoserver -v $ADDONS -v $CFG -v $LOGS -v $EXE -v $MAPLIST -v $MAPCYCLE —name $NAME inotkaa/csgo bash

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

С такой системой увеличилось количество серверов, но увеличение количества, увеличило время их обслуживания. При выходе апдейта на серверную часть, все сервера нужно было погасить по очереди, запустить один любой, чтобы он обновил файлы и запустить все остальные. Раз в неделю стабильно приходилось это делать.

Теперь собственно итог. Я отказался от скрипта о котором говорил в самом начале, теперь я запускаю контейнеры без интерактивного режима. Выглядит это так.

#!/bin/bash
VIP=’/opt/csgo_conf/vip/addons/sourcemod/plugins/vip:/home/csgoserver/serverfiles/csgo/addons/sourcemod/plugins/vip’
VIP_CONF=’/opt/csgo_conf/vip/cfg/vip:/home/csgoserver/serverfiles/csgo/cfg/vip’
VIP_DATA=’/opt/csgo_conf/vip/addons/sourcemod/data/vip:/home/csgoserver/serverfiles/csgo/addons/sourcemod/data/vip’
VIP_TRANS1=’-v /opt/csgo_conf/vip/addons/sourcemod/translations/vip_core.phrases.txt:/home/csgoserver/serverfiles/csgo/addons/sourcemod/translations/vip_core.phrases.txt’
VIP_TRANS2=’-v /opt/csgo_conf/vip/addons/sourcemod/translations/vip_modules.phrases.txt:/home/csgoserver/serverfiles/csgo/addons/sourcemod/translations/vip_modules.phrases.txt’
VIP_TRANS3=’-v /opt/csgo_conf/vip/addons/sourcemod/translations/vip_test.phrases.txt:/home/csgoserver/serverfiles/csgo/addons/sourcemod/translations/vip_test.phrases.txt’

ADDONS=’/opt/csgo_conf/retakes/addons:/home/csgoserver/serverfiles/csgo/addons’
CFG=’/opt/csgo_conf/retakes/cfg:/home/csgoserver/serverfiles/csgo/cfg’
LOGS=’/opt/csgo_conf/logs:/home/csgoserver/serverfiles/csgo/logs’
EXE=’/opt/csgo_conf/retakes/csgoserver:/home/csgoserver/csgoserver’
MAPLIST=’/opt/csgo_conf/retakes/maplist.txt:/home/csgoserver/serverfiles/csgo/maplist.txt’
MAPCYCLE=’/opt/csgo_conf/retakes/mapcycle.txt:/home/csgoserver/serverfiles/csgo/mapcycle.txt’
NAME=’csgo_retakes’
HOSTNAME=’retakes.local’
PORTS=’-p 27017:27017 -p 27017:27017/udp -p 27007:27007/udp -p 27022:27022/udp’
RUN=’/home/csgoserver/retakes.sh’

cd /opt/docker/csgo ; docker build -t inotkaa/csgo . ; docker rm -f $NAME ; docker run -d —restart=always —cpuset-cpus=0,1 $PORTS -v /etc/localtime:/etc/localtime:ro -h $HOSTNAME -c 800 —link mysql_inot.pro:db001.local -v /opt/csgo_ref:/home/csgoserver -v $ADDONS -v $CFG -v $LOGS -v $EXE -v $MAPLIST -v $MAPCYCLE -v $VIP -v $VIP_CONF -v $VIP_DATA $VIP_TRANS1 $VIP_TRANS2 $VIP_TRANS3 —name $NAME inotkaa/csgo $RUN

Файл /home/csgoserver/retakes.sh выглядит вот так.

#!/bin/bash
cd /home/csgoserver/serverfiles
su -c «./srcds_run -game csgo -usercon -strictportbind -port 27017 +clientport 27007 +tv_port 27022 -tickrate 64 +map de_dust2 +servercfgfile csgo-server.cfg -maxplayers_override 7 +mapgroup mg_active +game_mode 1 +game_type 0 +host_workshop_collection +workshop_start_map -authkey» csgoserver

Тут добавилось несколько строчек про VIP, т.к. этот плагин пока не поддерживает SQL, я сделал так. В будущем, если мне это не надоест, вроде обещали поддержку.

Теперь все работает на автомате, остался только один «приватный» сервер старого формата, он нужен, чтобы остановив все контейнеры, запустить этот сервер и обновить файлы. Дальше просто одной командой запускаются контейнеры обратно. Из минусов, я лишился «консоли сервера» в консоли, именно интерактивной консоли, можно только посмотреть, что там выводится с помощью docker logs.

Это был достаточно интересный опыт и подводных камней было сильно больше, чем я могу описать, тут я написал только о стороне докера. Еще я написал некоторое количество скриптов, настроил анализаторы логов, трафика и т.п. Некоторые вещи я использовал много раз, некоторые вещи работают сами в кроне, но каждая фишечка была интересна. Сейчас посещаемость моей площадки серверов это около 2500 уникальных игроков в день. Более подробную статистику можно посмотреть тут. Денег никак это не приносит, хотя я пытался повесить банер гугла, но гугл забанил меня через пару недель. =) Как оказалось 10к-12к просмотров в день, это микроскопические деньги, даже если бы меня и не забанили. Я даже пережил один ddos, видимо обиженный школьник решил с друзьями поиграться.

Вот собственно и все, что я хотел сказать на данном этапе. Даже не надеюсь, что кто-то дочитал до этого места.