Docker-контейнер или классическая установка игрового сервера
Рано или поздно у любого, кто держит больше одного игрового сервера на одной машине, встаёт вопрос: заворачивать всё в Docker или ставить как обычно — SteamCMD/бинарник прямо на систему. Кто-то тащит контейнеры туда, где они не нужны, просто потому что "так модно", а кто-то до сих пор боится Docker как чёрной магии и теряет часы на ручной пересборке окружения после каждого краша. Разберём по-честному, где контейнер реально экономит время, а где только добавляет слой абстракции между вами и логами сервера.
Содержание
Что вообще меняет Docker в контексте игрового сервера
Игровой сервер — это почти всегда один процесс (иногда с парой воркеров), который слушает UDP/TCP порт, пишет мир на диск и жрёт RAM пропорционально числу игроков и загруженным чанкам/зонам. Никакой сложной микросервисной архитектуры тут обычно нет — и это важно, потому что Docker исторически создавался для изоляции и оркестрации именно множества сервисов.
Что даёт контейнеризация конкретно для геймсервера:
- Изоляция зависимостей. Java 21 для актуального Minecraft и Java 17 для старого модпака не конфликтуют, потому что каждая версия сидит в своём образе.
- Воспроизводимость.
docker-compose.yml— это по сути описание сервера как кода. Поднять точную копию на другой машине — одна команда, а не два часа вспоминания "а что я туда доустанавливал в марте". - Простое обновление образа.
docker pull+ пересоздание контейнера вместо ручного скачивания новых бинарников и патчинга конфигов руками. - Ограничение ресурсов на уровне cgroups, а не только через конфиг самой игры — то есть даже если сервер решит сожрать всю RAM из-за утечки в моде, соседний контейнер не пострадает.
Чего Docker НЕ даёт бесплатно — производительности. Сетевой стек контейнера (особенно в режиме bridge) добавляет небольшой, но реальный оверхед на каждый пакет, а для игр с высокой частотой тикрейта (Rust, ARK, CS2) задержка на паре сотен микросекунд обычно не критична, но это не ноль, и на слабом железе оно ощутимо.
Классическая установка: когда это реально проще
Если у вас один сервер под одну игру и вы не планируете разворачивать вторую копию через месяц — классическая установка часто банально быстрее в освоении. Для примера, поднять ванильный Minecraft-сервер напрямую:
mkdir -p /opt/mc-server && cd /opt/mc-server
wget https://piston-data.mojang.com/v1/objects/<hash>/server.jar -O server.jar
echo "eula=true" > eula.txt
java -Xms2G -Xmx4G -jar server.jar nogui
Три команды — и сервер уже работает. Никакого Dockerfile, никакого понимания volumes и networking драйверов. Логи прямо в консоли, файлы прямо в файловой системе — открыл FTP или nano server.properties, поправил и перезапустил systemd-юнит.
Для CS2 через SteamCMD классика выглядит так же прямолинейно:
./steamcmd.sh +force_install_dir /opt/cs2 +login anonymous +app_update 730 validate +quit
Никакой Docker-магии — SteamCMD и так умеет ставить и обновлять сервер в один вызов. Заворачивать это в контейнер имеет смысл, только если вы держите десяток таких серверов и хотите единый пайплайн обновлений — иначе это лишний уровень между вами и понятной командой SteamCMD.
Минусы классики проявляются, когда серверов становится больше одного: зависимости расползаются по системе, конфликты версий Java/Python/Node решаются через боль (или через костыли вроде update-alternatives), а откат к прошлой версии после неудачного обновления мода превращается в ручное восстановление из бэкапа файлов вместо docker rollback.
Нужен игровой сервер?
MAATRIX GAMES — 45+ игр, деплой за минуты, DDoS-защита, автобэкапы. Тарифы от 399 ₽/мес.
Создать серверDocker: практический пример для игрового сервера
Возьмём тот же принцип, но в контейнере. Минимальный docker-compose.yml для сервера на образе itzg/minecraft-server (популярный community-образ, не официальный от Mojang):
services:
mc:
image: itzg/minecraft-server:latest
container_name: mc-survival
ports:
- "25565:25565"
environment:
EULA: "TRUE"
MEMORY: "4G"
TYPE: "PAPER"
VERSION: "1.21.1"
volumes:
- ./data:/data
restart: unless-stopped
stop_grace_period: 60s
Ключевой момент — stop_grace_period. Без него Docker может убить процесс SIGKILL раньше, чем сервер успеет сохранить мир на диск, и вы получите откат прогресса игроков после каждого рестарта хоста. Для Minecraft/Paper обычно хватает 30-60 секунд на корректный save-all + shutdown, но если у вас большой мир на медленном диске — увеличивайте.
Обновление версии в этом сценарии — правка одной переменной и пересоздание контейнера:
docker compose pull
docker compose up -d
Данные мира лежат в ./data на хосте (volume), поэтому пересоздание контейнера не трогает сохранения — контейнер эфемерный, состояние живёт отдельно. Это правило стоит запомнить железно: всё, что должно пережить обновление образа, обязано быть в volume, а не внутри слоёв контейнера.
Когда контейнеризация реально оправдана
Есть конкретные сценарии, где Docker окупает свою сложность:
- Несколько серверов на одной машине с разными версиями Java/зависимостей. Modded Minecraft на Forge 1.20.1 (Java 17) и ванильный сервер на 1.21 (Java 21) без Docker потребуют либо контейнеров, либо ручного управления путями к JDK через переменные окружения при каждом запуске.
- Частые тестовые разворачивания. Если вы гоняете модпак через десятки итераций подбора модов,
docker compose down && docker compose up -dс чистым образом быстрее, чем откатывать вручную установленные файлы. - Команда из нескольких админов.
docker-compose.ymlв git-репозитории — это self-documenting конфигурация: новый человек в команде видит весь стек одним файлом, а не разгребает, что и куда было накатано полгода назад. - CI/CD для модпаков. Если сборка мода собирается автоматически (например, Packwiz или CurseForge-экспорт) и деплоится по пушу — Docker-образ как артефакт вписывается в пайплайн естественно.
Если у вас ничего из этого нет — вероятно, вы платите сложностью Docker (netns, изучение volumes, отладка "почему контейнер не видит порт") за фичи, которыми не пользуетесь.
Ресурсы и накладные расходы: честный разговор
Тут нет смысла придумывать точные цифры FPS/тикрейта — они сильно зависят от драйвера сети Docker, ядра хоста и конкретной игры, и любой "бенчмарк" без контекста железа будет враньём. Что можно сказать предметно:
| Параметр | Классическая установка | Docker (bridge network) |
|---|---|---|
| CPU/RAM оверхед | Практически нулевой | Небольшой, обычно единицы процентов |
| Сетевая задержка | Минимальная | Чуть выше из-за NAT/iptables в bridge-режиме |
| Простота отладки логов | tail -f, systemd journal | docker logs -f, отдельный слой абстракции |
| Скорость первого запуска | Зависит от игры, обычно быстрее | Дольше первый pull образа, дальше сопоставимо |
| Откат после неудачного обновления | Ручной, из бэкапа файлов | docker compose down + старый тег образа |
Для UDP-тяжёлых игр (Rust, ARK, DayZ) при желании минимизировать сетевой оверхед в Docker используют network_mode: host — это убирает NAT-прослойку ценой части изоляции портов:
services:
rust:
image: your-rust-image
network_mode: host
restart: unless-stopped
Это работает только на Linux-хосте (на Docker Desktop для Mac/Windows host-режим не даёт того же эффекта из-за архитектуры виртуализации), и вы теряете часть удобства маппинга портов — но получаете сеть, максимально близкую к классической установке.
Гибридный подход: лучшее из обоих миров
На практике многие опытные админы не выбирают строго одно или другое, а используют Docker точечно:
- Панель управления в контейнере, сам игровой процесс — нативно. Например, Pterodactyl Panel и его Wings-демон часто разворачивают в Docker для панели, а сами игровые серверы Wings уже запускает как процессы (в изолированных cgroups, но без полноценных отдельных Docker-образов на каждую игру).
- Docker только для "капризных" зависимостей. Если конкретный мод-пак требует специфичную сборку Java или редкую системную библиотеку, которую не хочется тащить в основную ОС — заворачиваете именно его, а остальные серверы держите классически.
- Docker для staging, классика для прода. Тестируете новую версию модпака в одноразовом контейнере, а боевой сервер, который и так стабильно работает, не трогаете лишний раз.
Если вы уже смотрели в сторону панелей управления, у нас есть отдельный разбор панели управления против консольного доступа — многие вопросы "что проще для новичка" там пересекаются с этим сравнением.
Нужен игровой сервер?
MAATRIX GAMES — 45+ игр, деплой за минуты, DDoS-защита, автобэкапы. Тарифы от 399 ₽/мес.
Создать серверЧастые вопросы
Docker снижает FPS/тикрейт на игровом сервере?
Напрямую — нет, тикрейт считает сама игра на CPU, а не Docker. Но сетевая задержка в bridge-режиме может добавить микросекунды к обработке пакетов; для большинства серверов это некритично, но при большом онлайне на слабом CPU лучше использовать network_mode: host на Linux.
Нужно ли изучать Kubernetes, чтобы держать игровой сервер в Docker?
Нет. Для одного-двух серверов достаточно docker и docker compose — Kubernetes оправдан только при десятках контейнеров и потребности в автомасштабировании, чего у обычного игрового сервера почти никогда не бывает.
Что будет с миром/сохранениями при обновлении образа?
Ничего, если вы правильно настроили volume — данные лежат на хосте отдельно от контейнера, и пересоздание контейнера их не трогает. Проблемы бывают только если забыли вынести директорию с данными в volume.
Можно ли перейти с классической установки на Docker без потери прогресса?
Да — копируете директорию с миром/сохранениями в volume нового контейнера, сверяете версию игры/сборки (например, Paper build) и запускаете. По сути та же логика, что и при миграции сервера к другому хостеру — главное сохранить структуру файлов.
А что с автообновлением модов внутри контейнера?
Работает так же, как и без Docker — если у вас настроен процесс автообновления модов, он просто выполняется внутри контейнера при старте или по расписанию. Логику разворачивания смотрите в статье про автообновление модов на сервере.