Top.Mail.Ru
 

Нагрузочное тестирование продакшена игры в Телеграме: узкие места устранили до запуска

Наш клиент готовил к запуску игру в Телеграме и ждал на старте большого наплыва пользователей. Бэкенд игры написан на PHP и работает на сервере приложений RoadRunner, данные хранятся в MongoDB и Redis, события передаются через Kafka. Продакшена у игры ещё не было: его собирали с нуля в Kubernetes у облачного провайдера.

Задача:

Собрать продакшен-окружение и до запуска проверить его под нагрузкой: найти, во что упирается система при росте трафика, и устранить это. Обязательным требованием было автомасштабирование подов и нод. По оценкам заказчика, на старте могли одновременно зайти до 500 тыс. человек, а за первый день — от 1 до 10 млн. Тестировать пришлось параллельно со сборкой: продакшен доводили до готовности в те же дни.

Итоги:

Вместе с разработкой заказчика мы провели серию нагрузочных тестов на продакшен-окружении до запуска и устранили узкие места, которые проявлялись только под нагрузкой: нехватку процессора у бэкенда, выключенный кэш скомпилированного PHP-кода, недостаточный размер кластера MongoDB и точку входа трафика в кластер. После включения кэша простой запрос вместо 270 запросов в секунду на 8 ядрах выдержал 1800 на 6 ядрах. В финальном тесте перед запуском система стабильно держала 10 000 запросов в секунду примерно на 70 подах бэкенда.

Период работы:

23 августа — 25 октября 2024 года, нагрузочные тесты — 13–29 сентября

Команда:

Тимлид DevOps, senior DevOps-инженер, middle DevOps-инженер

Инструменты:

  • Kubernetes
  • Terraform
  • Helm
  • Argo CD
  • nginx ingress controller
  • Locust
  • Docker
  • New Relic
  • OpenTelemetry Collector
  • Grafana
  • Blackfire
  • RoadRunner
  • PHP opcache
  • MongoDB
  • Redis

Решение:

Продакшен мы описывали в Terraform, приложения выкатывали Helm-чартами через Argo CD. Первый нагрузочный тест прошёл в тот же день, когда бэкенд стал доступен в кластере. Для разработки был отдельный dev-стенд, но нагрузку давали на само продакшен-окружение, на котором игре предстояло запуститься.

Как устроили тесты

Сценарии нагрузки писала и прогоны вела разработка заказчика, мы отвечали за инфраструктуру тестов и за исправления по их итогам. Генератор нагрузки — Locust, инструмент, который имитирует запросы множества пользователей по сценарию. Он запускается в Docker на отдельных виртуальных машинах, которые поднимаются Terraform-ом. К коду генератора мы написали README и передали его разработке заказчика: дальше они запускали тесты сами.

Наблюдаемость под тесты

Наблюдаемость достраивали по ходу тестов. Стандартной интеграции New Relic для RoadRunner не хватило, поэтому в кластер добавили OpenTelemetry Collector — сборщик трейсов (записей о прохождении запроса через систему) и метрик — и отправили через него трейсы приложения в New Relic. Трейсы разметили идентификатором пода, чтобы сравнивать экземпляры бэкенда между собой. Затем добавили метрики RoadRunner и мониторинг кластера с дашбордами в Grafana.

Тест, узкое место, исправление

Каждый следующий прогон упирался в новое место. Мы находили причину, исправляли её и давали нагрузку снова. Узкие места — в том порядке, в котором их нашли:
  1. Процессор бэкенда. Время отклика подов было неравномерным: под нагрузкой они упирались в процессор. Мы добавили пул более мощных нод и вынесли бэкенд на выделенные ноды с помощью taints и tolerations — механизма Kubernetes, который не пускает на ноду посторонние поды. Ресурсы подов бэкенда подбирали вместе с разработкой заказчика.
  2. Кэш скомпилированного PHP-кода. Чтобы понять, что именно нагружает процессор, развернули профилировщик Blackfire. С его помощью нашли, что opcache — кэш скомпилированного PHP-кода — был выключен. После включения кэша получили прирост на простом запросе (получение JSON): с 270 запросов в секунду на 8 ядрах до 1800 на 6 ядрах. После этого перешли к расширенным тестам.
  3. Размер кластера MongoDB. Во время очередного теста попытались увеличить кластер MongoDB, но ни добавить реплики, ни увеличить их размер не получилось: это оказалась ошибка на стороне провайдера. Мы завели обращение в поддержку и временно подняли другой кластер. После исправления проверили, что размер меняется на лету: запросы во время изменения шли без ошибок. Один из следующих тестов упёрся в MongoDB, и по его итогам кластер увеличили.
  4. Входящий трафик. Ingress-контроллер — точку входа HTTP-трафика в кластер — вынесли на отдельный пул нод, дали ему больше ресурсов и настроили его масштабирование.

Финальный тест

В финальном тесте нужно было увидеть, как производительность растёт вместе с числом подов, и понять, с какими ресурсами стартовать. Перед ним разработка заказчика ещё раз оптимизировала код. Система стабильно держала 10 000 запросов в секунду примерно на 70 подах бэкенда — в начале тестов автомасштабирование было настроено на 1–3 пода.

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

Перед запуском и после

Перед запуском фронтенд развернули на отдельной группе нод с автомасштабированием и подключили мониторинг Redis. После запуска изменения, внесённые вручную, перенесли в Terraform и написали документацию.
Обсудить проект _
Если у вас есть ИТ-проблема, оставьте свои контакты, и мы поможем спланировать ее решение. Обещаем не рассылать спам.
hello@softwarecats.dev
Новосибирск, ул. Демакова
23/5, оф.308
Контакты _

Еще про наши проекты:

    • →
    • →
    • →