MAATRIX GAMES / Блог / Docker-контейнер или классическая установка игрового сервера

Docker-контейнер или классическая установка игрового сервера

MAATRIX GAMES

Рано или поздно у любого, кто держит больше одного игрового сервера на одной машине, встаёт вопрос: заворачивать всё в 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 окупает свою сложность:

  1. Несколько серверов на одной машине с разными версиями Java/зависимостей. Modded Minecraft на Forge 1.20.1 (Java 17) и ванильный сервер на 1.21 (Java 21) без Docker потребуют либо контейнеров, либо ручного управления путями к JDK через переменные окружения при каждом запуске.
  2. Частые тестовые разворачивания. Если вы гоняете модпак через десятки итераций подбора модов, docker compose down && docker compose up -d с чистым образом быстрее, чем откатывать вручную установленные файлы.
  3. Команда из нескольких админов. docker-compose.yml в git-репозитории — это self-documenting конфигурация: новый человек в команде видит весь стек одним файлом, а не разгребает, что и куда было накатано полгода назад.
  4. CI/CD для модпаков. Если сборка мода собирается автоматически (например, Packwiz или CurseForge-экспорт) и деплоится по пушу — Docker-образ как артефакт вписывается в пайплайн естественно.

Если у вас ничего из этого нет — вероятно, вы платите сложностью Docker (netns, изучение volumes, отладка "почему контейнер не видит порт") за фичи, которыми не пользуетесь.

Ресурсы и накладные расходы: честный разговор

Тут нет смысла придумывать точные цифры FPS/тикрейта — они сильно зависят от драйвера сети Docker, ядра хоста и конкретной игры, и любой "бенчмарк" без контекста железа будет враньём. Что можно сказать предметно:

ПараметрКлассическая установкаDocker (bridge network)
CPU/RAM оверхедПрактически нулевойНебольшой, обычно единицы процентов
Сетевая задержкаМинимальнаяЧуть выше из-за NAT/iptables в bridge-режиме
Простота отладки логовtail -f, systemd journaldocker 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 — если у вас настроен процесс автообновления модов, он просто выполняется внутри контейнера при старте или по расписанию. Логику разворачивания смотрите в статье про автообновление модов на сервере.