Сервис развивается: тестируем формат, собираем идеи, улучшаем сервис. Есть идеи? Написать
Войти
Дайджесты новостей
Кластер Kubernetes с игровыми серверами Agones, динамическими UDP-портами и панелью матчмейкинга в пиксель-арт стиле

Масштабирование игровых серверов в Kubernetes: почему стандартные Deployment не подходят и как Agones решает проблему сессионных нагрузок

Разбор архитектуры Agones на Kubernetes для выделенных игровых серверов: почему классические балансировщики и поды вызывают сбои UDP-сессий игроков, как CRD GameServer управляет жизненным циклом и как организовать автомасштабирование пула серверов по длине очереди матчмейкера на Go.

Масштабирование игровых серверов в Kubernetes: почему стандартные Deployment не подходят и как Agones решает проблему сессионных нагрузок

Перенос серверной инфраструктуры в Kubernetes стал отраслевым стандартом для веб-приложений. Однако команды, запускающие в кластерах серверы для соревновательного мультиплеера (шутеров, королевских битв или сессионных стратегий), быстро упираются в ограничения базовой модели оркестратора. Механизмы управления подами (pod — наименьшая единица развертывания в Kubernetes из одного или нескольких контейнеров) и маршрутизации входящего трафика, созданные для сайтов и stateless API (сервисов без сохранения состояния между независимыми запросами), приводят к сбоям в сессионных играх.

Платформа с открытым исходным кодом Agones, созданная Google совместно с игровым издательством Ubisoft и развиваемая сообществом Google for Games, предлагает специализированную модель оркестрации выделенных игровых серверов (dedicated game servers) поверх Kubernetes, учитывая специфику протокола UDP, долгих сессий и прямого подключения игроков.

Архитектурный конфликт: почему стандартный Deployment ломает игры

В веб-разработке базовой сущностью служит Deployment — контроллер, поддерживающий заданное количество идентичных реплик сервиса. Если нагрузка падает, механизм масштабирования удаляет лишние поды; при релизе новой версии запускается плавное обновление (rolling update), поочередно заменяющее старые контейнеры новыми. Для веб-трафика это безопасно, так как сетевой балансировщик (Service) распределяет входящие HTTP-запросы по любым рабочим узлам.

В соревновательных сетевых играх физика взаимодействия принципиально иная:

  1. Непрерывное состояние в памяти (in-memory state). Сервер непрерывно рассчитывает физику мира, координаты персонажей, попадания и таймеры раундов с частотой 30–128 тактов в секунду (tick rate). Состояние матча находится исключительно в оперативной памяти конкретного процесса. Если Kubernetes решит завершить под в рамках масштабирования или обновления ноды, матч для всех участников мгновенно прервется.
  2. Сессии поверх UDP вместо независимых запросов. Игры передают данные по протоколу UDP (быстрому протоколу без накладных расходов на подтверждение доставки каждого пакета). В отличие от HTTP, здесь нет постоянного соединения, которое балансировщик мог бы прозрачно переключить на другой под: пакеты должны попадать ровно в один и тот же процесс операционной системы.
  3. Прямое сетевое подключение. Игроку не нужен виртуальный адрес балансировщика. Клиент должен соединяться напрямую с внешним IP-адресом конкретного серверного узла и портом, выделенным под его матч.

Стандартные контроллеры Kubernetes не знают, идет ли на сервере матч или он простаивает. Управление игровыми сессиями через стандартный Deployment неизбежно приводит к срыву матчей.

Анатомия Agones: кастомные ресурсы и жизненный цикл GameServer

Agones расширяет Kubernetes через пользовательские описания ресурсов (Custom Resource Definition, CRD). Ключевой сущностью платформы становится GameServer.

Каждый GameServer управляет одним игровым процессом. Рядом с контейнером игры Agones развертывает вспомогательный контейнер — сайдкар (sidecar). Сервер общается с сайдкаром через локальный SDK по протоколу gRPC или REST. Контейнер игры не должен занимать сетевые порты 8080, 9357 и 9358, зарезервированные сайдкаром под служебный API и метрики.

Жизненный цикл GameServer состоит из четырех фаз:

  • Creating / Starting: инициализация контейнера, загрузка игровых карт и запуск бинарного файла.
  • Ready: процесс готов принять игроков и уведомляет платформу вызовом Ready() в SDK.
  • Allocated: сервер закреплен за начавшимся матчем. Как только матчмейкер резервирует сервер, Agones переводит его в статус Allocated. С этого момента сервер неприкосновенен: автоматическое масштабирование никогда не удалит занятый под до конца игры.
  • Shutdown: раунд окончен, игроки отключились, сервер вызывает Shutdown() и безопасно освобождает ресурсы.

Agones строго разделяет «здоровье» (health) и «готовность» (readiness). Сервер может быть исправен, слать сигналы Health(), но находиться в статусе Allocated, оставаясь недоступным для других игроков.

Маршрут игрока: интеграция с матчмейкингом

В Agones сетевой трафик разделен на управляющий контур (Control Plane) и игровой поток (Data Plane):

  1. Игровой клиент запрашивает матч в сервисе подбора соперников — матчмейкере (Matchmaker).
  2. Матчмейкер подбирает группу игроков и отправляет в API Kubernetes запрос на создание объекта GameServerAllocation.
  3. Контроллер Agones находит свободный сервер со статусом Ready, атомарно переводит его в статус Allocated и назначает внешний сетевой порт из разрешенного диапазона узла (например, порт 7432 на ноде с IP 198.51.100.25).
  4. Матчмейкер возвращает адрес 198.51.100.25:7432 игровым клиентам.
  5. Игроки подключаются напрямую к выделенному сокету по протоколу UDP без посредничества прокси и балансировщиков.

Управление пулом: Fleet и политики масштабирования

Запуск игрового сервера занимает от десятков секунд до пары минут. Чтобы игроки не ждали старта контейнеров, Agones объединяет серверы в абстракцию Fleet (флот). Флот поддерживает «теплый резерв» готовых серверов в статусе Ready.

Размером пула управляет контроллер FleetAutoscaler, поддерживающий две стратегии:

  • Буферная политика (Buffer Policy): контроллер держит постоянный резерв свободных серверов (Ready) сверх занятых (Allocated). Если буфер равен 5 серверам, а занято 15, Agones будет удерживать 20 экземпляров, восполняя буфер по мере старта новых матчей.
  • Вебхук-политика (Webhook Policy): контроллер обращается к внешнему HTTP-сервису, передавая объект FleetAutoscaleReview. Сервис оценивает длину очереди игроков в матчмейкере и заранее заказывает необходимые мощности.

При размещении подов Agones поддерживает стратегию Packed: серверы плотно компонуются на минимальном числе физических узлов. Это позволяет механизму Cluster Autoscaler безопасно выключать полностью освободившиеся серверные ноды и экономить инфраструктурный бюджет.

Пошаговое развертывание: запуск флота в кластере

Развертывание игрового пула описано в официальных спецификациях Agones Documentation и Fleet Autoscaler.

Шаг 1. Системные требования

Перед началом убедитесь в наличии:

  • Доступа к кластеру Kubernetes через утилиту kubectl.
  • Открытого диапазона входящих портов UDP 7000–8000 в файрволе облачной сети.
  • Установленного контроллера Agones в кластере.
Шаг 2. Запуск тестового флота

Примените официальный манифест тестового флота:

kubectl apply -f https://raw.githubusercontent.com/googleforgames/agones/release-1.60.0/examples/simple-game-server/fleet.yaml

Команда создаст две реплики простого игрового сервера на Go.

Шаг 3. Верификация состояния

Проверьте статус развернутых серверов:

kubectl get gameservers

Убедитесь, что серверы перешли в статус Ready и получили внешние адреса:

NAME                       STATE   ADDRESS         PORT   NODE       AGE
simple-game-server-4b5c6   Ready   198.51.100.25   7432   node-pool  25s
simple-game-server-8f2a1   Ready   198.51.100.26   7115   node-pool  25s

Отправьте тестовый UDP-пакет через nc -u 198.51.100.25 7432. Сервер немедленно вернет подтверждение ACK.

Шаг 4. Автомасштабирование пула

Создайте манифест fleet-autoscaler.yaml:

apiVersion: autoscaling.agones.dev/v1
kind: FleetAutoscaler
metadata:
  name: simple-game-server-autoscaler
spec:
  fleetName: simple-game-server
  policy:
    type: Buffer
    buffer:
      bufferSize: 2
      minReplicas: 2
      maxReplicas: 10

Примените конфигурацию командой kubectl apply -f fleet-autoscaler.yaml. Система начнет автоматически удерживать две готовые реплики в горячем резерве.

Чек-лист надежности перед продакшеном

Перед запуском боевого проекта проверьте ключевые параметры надежности:

  • Сетевые порты: убедитесь, что приложение не занимает зарезервированные сайдкаром порты 8080, 9357 и 9358.
  • Защита секретов: передавайте токены баз данных и ключи API через Kubernetes Secrets или HashiCorp Vault, избегая открытого текста в переменных окружения.
  • Тест на эвикшен: сымитируйте дренаж узла (kubectl drain) с сервером в статусе Allocated. Оркестратор обязан сохранять работающий под до завершения матча.
  • Задержка старта нод: закладывайте время инициализации виртуальной машины (2–3 минуты) в размер буфера свободных серверов флота.
  • Бизнес-метрики: настраивайте оповещения по числу занятых серверов, сбоям аллокаций и длине очереди в матчмейкере, а не по усредненной утилизации CPU.