Facepunch буквально вынудили меня использовать Docker

от автора

У меня есть собственные Rust сервера на арендованной удаленной машине. Онлайн пока что крайне мал (в основном — никого, хотя бывает и 1-3 игроков), но мне нравится настройка и администрирование, поэтому в первую очередь мой сервер мне служит в образовательных целях.

Начинал я с малого: пытался писать небольшие плагины для OxideMod с помощью ChatGPT и, организовав git репозиторий прямо в папке oxide/plugins, сделал процесс обновления плагинов максимально удобным. А недавно мне досталась задача посложнее: в свете недавнего обновления RustDedicated Server (которое стало отправной точкой) я решил наконец по максимуму автоматизировать имеющиеся задачи — об этом далее в статье.


Все началось с того, что разработчики Rust Dedicated Server починили сломали функцию restart. Она и ранее не работала, поскольку выключала сервер вместо того, чтобы перезагружать его. А теперь она стала работать согласно названию, но параллельно с ней перезагружать почему-то начала и функция quit. Очевидно, что это два названия одного и того же, но разработчики не определились, чего же именно.

Как бы то ни было — данный фикс создал мне проблему: теперь я мог выключить запущенный сервер только нативными средствами Linux:

pkill RustDedicated

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

Docker

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

Сама по себе папка с сервером представляет собой развернутое с помощью steamcmd приложение, куда дополнительно устанавливается oxidemod. Когда я начинал использовать docker, я так же добавил в эту папку Dockerfile с настройками.

Типичная структура сервера примерно такая:

Rust_Server ├── Bundles (~ 6.5 ГБ) ├── HarmonyMods ├── RustDedicated_data (~ 700 МБ) │   ├── Managed │   ├── Mono │   ├── MonoBleedingEdge │   ├── Plugins │   ├── Resources │   └── StreamingAssets ├── oxide ├── scripts ├── server │   └── melee.mayhem │       ├── cfg │       ├── scripts │       └── serveremoji ├── steamapps └── Dockerfile

Когда мне понадобилось больше серверов, я просто копировал всю папку Rust_Server каждый раз, когда хотел добавить еще один. Очевидно, что подход избыточный, особенно в условиях ограниченного места на диске, ведь папка сама по себе весит более 7 ГБ, а вдобавок еще и каждый docker-образ занимает столько же.

Поэтому в конце-концов структура эволюционировала в единственную папку, в которой для каждого нового сервера (./server/$IDENTITY) я просто добавлял локальную подпапку oxide с отдельным набором плагинов и конфиг-файлами, что значительно экономило место на диске. Для запуска/сборки всех серверов было решено использовать docker-compose.

В итоге, преобразования произошли только в подпапке server, остальная структура осталась без изменений:

Rust_Server ├── Bundles ├── HarmonyMods ├── RustDedicated_data │   ├── Managed │   ├── Mono │   ├── MonoBleedingEdge │   ├── Plugins │   ├── Resources │   └── StreamingAssets ├── oxide ├── scripts ├── server │   ├── melee.mayhem │   │   ├── cfg │   │   ├── oxide │   │   │   ├── config │   │   │   ├── data │   │   │   └── plugins │   │   ├── scripts │   │   └── serveremoji │   └── oxid.vanguard │       ├── cfg │       ├── oxide │       │   ├── config │       │   ├── data │       │   └── plugins │       ├── scripts │       └── serveremoji ├── steamapps ├── Dockerfile └── docker-compose.yml

На этом этапе возникла проблема — корневая папка oxide (строка 11 выше) оказалась общей для всех контейнеров (поскольку она, как и остальные папки, примонтирована в volumes), тогда как мне было нужно, чтобы в каждом контейнере такая папка была изолированной (поскольку наборы плагинов разные).

И хотя в wiki Facepunch я не нашел, как я могу указать кастомный путь к папке oxide, решение, к счастью, было найдено.

TMPFS

В процессе изучения документации докера я наткнулся на этот тип монтирования, при котором создается временная папка, доступная только в рамках текущего контейнера и удаляемая при его остановке.

Итоговый конфиг docker-compose.yml при этом стал таким:

version: "3.8" services:   main_server:     build:       context: .       args:         - SERVER_ROOT=main_server     environment:       - IDENTITY=oxid.vanguard       - HOSTNAME=ZGR | Zombie Got Rust | PVP - Solo.Duo.Trio.Quad       - SERVER_URL=zgr.ddns.net       - SERVER_TAGS=monthly,vanilla,EU       - SERVER_PORT=28015       - QUERY_PORT=28016       - WORLDSIZE=3000       - MAXPLAYERS=100     container_name: main_server     ports:       - "28015:28015/udp"       - "28016:28016/udp"       - "25888:25888"       - "80:80"       - "443:443"     volumes:       - .:/main_server     tmpfs:       - /main_server/oxide     command: ./run-server.sh   mayhem_server:     build:       context: .       args:         - SERVER_ROOT=mayhem_server     environment:       - IDENTITY=melee.mayhem       - HOSTNAME=ZGR | Melee Mayhem       - SERVER_URL=zgr.ddns.net       - SERVER_TAGS=weekly,vanilla,EU       - SERVER_PORT=28017       - QUERY_PORT=28018       - WORLDSIZE=1200       - MAXPLAYERS=150     container_name: mayhem_server     ports:       - "28017:28017/udp"       - "28018:28018/udp"       - "25889:25888"     volumes:       - .:/mayhem_server     tmpfs:       - /mayhem_server/oxide     command: ./run-server.sh
Мысли насчет конфига

В конфиге выше меня смущает только необходимость дублировать SERVER_ROOT (см args, container_name, volumes, tmpfs), поэтому если не найду другого решения — просто создам bash-script для генерации docker-compose.yml, а дублирующееся значение перенесу в переменную.

А в файле для запуске сервера run-server.sh я просто выполняю копирование из текущей подпапки oxide в изолированную корневую tmpfs папку oxide:

cp -R ./server/$IDENTITY/oxide/* ./oxide

Однако, данный подход забрал у меня одну важную фичу — live reload папки oxide. Папка копируется всего один раз на старте и больше не обновляется. Т.е. если я залью новый плагин в эту папку (./server/$IDENTITY/oxide), мне придется подключаться к контейнеру:

docker exec -it mayhem_server /bin/bash

и выполнять вот это действие вручную:

cp -R ./server/$IDENTITY/oxide/* ./oxide

Поэтому было решено использовать watch на каждой папке ./server/$IDENTITY/oxide и выполнять обновление tmpfs oxide при изменениях. В самом docker уже имеется такая фича, но она помечена как experimental, что лично меня отпугивает, поэтому решил пока воспользоваться проверенными средствами линукс — inotify-tools.

Я сделал bash-скрипт watch-oxide-dir.sh, который будет отслеживать изменения в папках oxide каждого сервера и при необходимости обновлять содержимое tmpfs oxide:

#!/bin/bash  while inotifywait -r ./server/$IDENTITY/oxide -e modify,create,delete; do     cp ./server/$IDENTITY/oxide/discord.config.json ./oxide     cp ./server/$IDENTITY/oxide/oxide.config.json ./oxide      rsync -a --delete ./server/$IDENTITY/oxide/config ./oxide     rsync -a --delete ./server/$IDENTITY/oxide/plugins ./oxide     rsync -a --delete ./server/$IDENTITY/oxide/data ./oxide     rsync -a --delete ./server/$IDENTITY/oxide/lang ./oxide     rsync -a --delete ./server/$IDENTITY/oxide/logs ./oxide done

Итоговый run-server.sh стал таким:

#!/bin/bash  SEED=$(cat ./server/$IDENTITY/seed.txt)  cp -R ./server/$IDENTITY/oxide/* ./oxide  "./set-permissions.sh" & "./watch-oxide-dir.sh" &  ./RustDedicated -batchmode \     +server.hostname "$HOSTNAME" \     +server.identity "$IDENTITY" \     +server.maxplayers $MAXPLAYERS \     +server.worldsize $WORLDSIZE \     +server.seed "$SEED" \     +server.tags "$SERVER_TAGS" \     +server.url "$SERVER_URL" \     +server.port $SERVER_PORT \     +server.queryport $QUERY_PORT;

Здесь добавление амперсанда (&) на строке 7 и 8 делает скрипт выполняемым в фоне.

Итоговый Dockerfile с необходимыми зависимостями, устанавливаемыми при сборке образа:

# syntax=docker/dockerfile:1 FROM ubuntu:22.04  RUN apt-get update && \     apt-get install -y openssl iproute2 ca-certificates inotify-tools rsync && \     apt-get clean  ARG SERVER_ROOT  WORKDIR /${SERVER_ROOT}  EXPOSE 28015 28016 28017 28018 25888 80 443  COPY . .

Далее все отлично запускается командой docker-compose up -d

В качестве бонуса буду рад поделиться ссылкой на свой консольный rcon-client для Rust серверов, который написан на языке Rust. Предельно прост в использовании.

А вы администрировали подобные сервера ?


ссылка на оригинал статьи https://habr.com/ru/articles/754214/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *