Наш клиент cybersport.ru — один из самых популярных информационно-новостных порталов про киберспорт в СНГ. По данным Similarweb, в октябре 2021 года у сайта было 16,5 млн посещений.

Обычно нагрузка на cybersport.ru даже во время значимых событий не превышает 400 RPS (requests per second). Так было до недавнего времени, точнее — до The International 10. Турнир вернулся после годичного перерыва из-за пандемии, что подогрело интерес к нему. Ажиотажа добавило и успешное выступление российских команд. В итоге во время турнира нагрузка достигала небывалых для сайта 2300 RPS.

Серверы и сайт выдержали в основном за счет оперативного масштабирования ресурсов. И в этом, конечно, нет ничего необычного — рядовая задача для инженеров эксплуатации. Однако, чтобы всё четко отработало во время The International 10, нашей команде пришлось провести подготовительные работы, в том числе кубернетизацию инфраструктуры cybersport.ru.

В этом посте расскажем: 

  • зачем cybersport.ru нужно было переезжать в Kubernetes и о сопутствующих трудностях;

  • почему пришлось сменить IaaS-провайдера;

  • почему было сложно во время The International 10, и как мы с этим справились;

  • почему автоматическое масштабирование ресурсов — не лучший выход для cybersport.ru (и вообще, для веб-ресурсов с таким же характером нагрузки).

Переезд в Kubernetes

Сайт cybersport.ru — часть киберспортивного холдинга ESforce, в который также входит самая титулованная киберспортивная команда России Virtus.pro. «Флант» уже более 3 лет поддерживает инфраструктуру ESforce.

Игрок Virtus.pro Тимур «buster» Тулепов (CS:GO)
Игрок Virtus.pro Тимур «buster» Тулепов (CS:GO)

До того, как мы подключились к проектам ESforce, инфраструктура всех проектов, включая cybersport.ru, была развернута на облачных серверах одного из ведущих западных провайдеров. Простейший CI/CD был организован на базе Ansible и управлялся через ​Ansible Tower. Разработчикам не хватало гибкости инфраструктуры, она была плохо адаптирована к cloud native-среде. Поэтому мы предложили перенести все важные сервисы в Kubernetes, настроить CI/CD и организовать review-окружения.

При миграции в K8s наши SRE-/DevOps-инженеры тесно взаимодействовали с разработчиками ESforce. Всё прошло без больших задержек и серьезных проблем. Небольшие трудности возникали только во время построения CI/CD-процесса, но они оперативно решались.

Чехарда с провайдерами

Переезд в Kubernetes состоялся еще в облаке зарубежного поставщика, но от него в итоге пришлось отказаться. Основные причины:

  • конфликт Telegram и Роскомнадзора: когда РКН стал блокировать пулы адресов, — включая те, что принадлежат провайдеру, — под угрозой оказались и сайты ESforce;

  • требования 152-ФЗ: российские веб-проекты должны хранить данные на территории РФ;

  • дороговизна по сравнению с ценами на российские облака.

Этап 1. Переезд в российское облако

Благодаря унифицированной и кубернетизированной инфраструктуре, миграция к одному из российских провайдеров прошла легко и быстро. Однако впоследствии мы столкнулись с проблемой, которой не было у западного провайдера: CPU steal time.

Если кратко, CPU steal time — задержка, которая вызвана перегруженностью гипервизоров при оверселлинге CPU. Виртуальные машины (ВМ) начинают конкурировать за выделение ресурсов; та ВМ, которая недополучает ресурсы, начинает сильно замедляться. Это в свою очередь приводит к проблемам в работе ОС и приложений.

Ниже — архивный график с данными нашей системы мониторинга. На нем видно, насколько высоким был уровень CPU steal time:

Проблема привела к тому, что инфраструктура стала нестабильной, постоянно сбоила; снизился SLA. Посыпались новые неприятности — даже такие, с которыми мы раньше не сталкивались. В такой обстановке не могло быть и речи об оптимизации инфраструктуры и кода, поскольку сложно было определить первопричину сбоев в работе приложения.

Этап 2. Переезд на «железные» серверы

В 2019 году не все российские облачные поставщики гарантировали отсутствие CPU steal time. Поэтому мы решили переехать к другому провайдеру, на «железные» серверы.

После переезда, в ходе оптимизации инфраструктуры и приложения, обнаружился ряд узких мест.

1. Redis. Изучив данные APM-системы (Application Performance Monitoring), мы выявили некорректное взаимодействие приложения и Redis. Количество подключений к сервису достигало 1,2 млн запросов в минуту; объем Set/Get-операций был слишком высок. В итоге Redis отвечал долго, сайты работали медленно.

Ниже — графики с результатами нагрузочного тестирования:

В некоторые моменты в Redis поступало более 1 млн запросов в минуту
В некоторые моменты в Redis поступало более 1 млн запросов в минуту
Время веб-транзакций доходило до 750 мс из-за медленных ответов Redis
Время веб-транзакций доходило до 750 мс из-за медленных ответов Redis
Время ответа сервера при 300+ OPS (operations per second)
Время ответа сервера при 300+ OPS (operations per second)

Вместе с разработчикам нам удалось оперативно обнаружить причину. Оказалось, что дело в неправильно настроенном процессе кэширования, из-за которого в кэш помещались постоянно обновляемые данные.

2. Нагрузка на PostgreSQL. Приложение создавало сетевую нагрузку на Postgres в 1 Гбит/с. Это было потолком для «железных» серверов в текущей конфигурации и приводило к недоступности cybersport.ru.

Как мы решили проблему:

  • Перевели проект на 10 Гбит/с;

  • Оптимизировали запросы к БД со стороны кода.

Этап 3. Обратно в облако

Популярность cybersport.ru росла, нагрузка — тоже. Сайту требовалось больше вычислительных ресурсов, которые можно было бы оперативно наращивать. Вдобавок, за соответствие 152-ФЗ у текущего поставщика приходилось доплачивать. То есть нужен был cloud-провайдер с гарантированным ресурсом по CPU и приемлемым ценником за 152-ФЗ. И в 2021 году cybersport.ru наконец-таки обосновались в облаке российского провайдера, который удовлетворял обоим критериям.

Влияние пандемии

К моменту переезда к новому провайдеру все более или менее значимые киберспортивные мероприятия отменили. У cybersport.ru не было реальной возможности оценить отказоустойчивость инфраструктуры во время пиковых нагрузок, кроме как с помощью синтетических тестов. Но для таких веб-сервисов синтетические тесты малополезны: они далеки от поведения реальных пользователей. Тем не менее, других вариантов не было, и мы готовились к The International 10 как могли — «в теории»…

И на старте турнира возникли некоторые проблемы.

The International 10. Проверка боем

В реальных условиях не всё пошло гладко. Сперва даже относительно невысокая нагрузка — в 600 RPS — приводила к тому, что сайт падал. Основные причины:

  • «разумная экономия» и, как следствие, ограничение количества заказываемых ресурсов под возрастающую нагрузку;

  • характер самой нагрузки, ее пиковость — мы быстро упирались в доступный объем ресурсов, а пока заказывали новые, серверы «пятисотили».

Пример пиков нагрузки во время The International 10
Пример пиков нагрузки во время The International 10

Небольшое отступление: автомасштабирование в Kubernetes

И в идеальном случае, т. е. когда нет финансовых и технических ограничений, желательно обеспечить несколько уровней горизонтального масштабирования и резервирования приложения — на уровне Pod’ов (количества экземпляров приложения) и на уровне кластера (количества узлов).

Автомасштабирование Pod’ов:

  • выбираем метрику, на основе которой будут масштабироваться Pod’ы. В случае с PHP-приложениями ориентируемся на количество занятых php-fpm-процессов (детальнее об этом — ниже);

  • выбираем количество min/maxReplicas на основе предварительных нагрузочных тестов — если гипотетические уровни нагрузки еще неизвестны;

  • настраиваем Cluster Autoscaler, где min/max также выставляется на основе тестов и с некоторым запасом по max.

Автомасштабирование узлов: 

  • используем standby-узлы для ситуаций с резкими и непредсказуемыми всплесками трафика. Они вроде «горячего резерва», который позволяет пережить первый наплыв пользователей и не ждать дозаказа обычных узлов при масштабировании Pod’ов;

  • настраиваем триггеры и алерты на желаемые значения, чтобы контролировать траты на дозаказ узлов.

Но случай с cybersport.ru был особый.

Почему мы ограничили автомасштабирование

С помощью Kubernetes-платформы Deckhouse легко настраивать гибкое автоматическое масштабирование узлов. Более того, Deckhouse сама умеет заказывать дополнительные ВМ у провайдера. Однако в случае с cybersport.ru автомасштабирование оказалось не самым оптимальным решением. 

В проекте была необходимость строго соблюдать бюджет на инфраструктуру. При этом нетипичная нагрузка на сайт приходит хотя и резко, но редко и прогнозируемо. Например, во время старта значимого турнира RPS всего за несколько секунд может вырасти с 300 до 2000.

Вместе с клиентом мы решили пойти на компромисс и выработали особый подход: масштабировать инфраструктуру с помощью изменения пары параметров в манифесте. При этом в 100% случаев изменение затрат на инфраструктуру — например, дозаказ ВМ — контролирует инженер (согласуя с менеджером проекта).

Суть в том, чтобы заранее выставить оправданные и достаточные min/max-значения для HPA/CA и подготовить нужное количество standby-узлов; это занимает буквально несколько минут, весь остальной процесс — автоматический. После чего запуск новых Pod’ов происходит самостоятельно и в достаточном объеме для приходящей нагрузки. Всё остальное время количество ВМ можно держать на минимуме, не переплачивая за простаивающие ВМ. По такой схеме мы и действовали во время The International 10 — правда, в чуть более стрессовом режиме, чем планировали, поскольку недооценили нагрузки, которые предстоит принять. 

Пример настройки HPA (Horizontal Pod Autoscaler)

Вот пример конфига, который отвечает за автомасштабирование Pod’ов — аналогичный тому, что мы использовали для Cybersport.ru:

apiVersion: deckhouse.io/v1alpha1
kind: PodMetric
metadata:
  name: php-fpm-active-worker
spec:
  query: round(sum by(<<.GroupBy>>) (phpfpm_processes_total{state="active",<<.LabelMatchers>>}) / sum by(<<.GroupBy>>) (phpfpm_processes_total{<<.LabelMatchers>>}) * 100)
---
kind: HorizontalPodAutoscaler
apiVersion: autoscaling/v2beta2
metadata:
  name: backend-fpm-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: backend
  minReplicas: {{ pluck .Values.global.env .Values.fpm_hpa.min_replicas | first | default .Values.fpm_hpa.min_replicas._default }}
  maxReplicas: {{ pluck .Values.global.env .Values.fpm_hpa.max_replicas | first | default .Values.fpm_hpa.max_replicas._default }}
  metrics:
  - type: Pods
    pods:
      metric:
        name: php-fpm-active-worker
      target:
        type: AverageValue
        averageValue: 60
Примечание

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

Кратко — о ключевых параметрах этой конфигурации:

1. Выбираем важные метрики. Прежде всего, необходимо определиться со спецификой приложения, которое мы собираемся масштабировать. Многое зависит от того, как правильно определять его загруженность. В случае с PHP-приложением, которое работает на php-fpm, идеальная метрика — загруженность child’ов (количество idle или active). По этой метрике легко определить, сколько child’ов в запасе. Отталкиваясь от этого, решаем — увеличивать или уменьшать количество реплик.

2. Настраиваем масштабирование. Для HPA создаем специальную метрику, измеряющую процент занятых child’ов. Затем в самом манифесте HPA ссылаемся на эту метрику и выставляем желаемые пороги срабатывания (averageValue). В нашем случае 60% занятых процессов, приводят к появлению новых Pod’ов, чтобы снизить этот средний процент.

Также два ключевых параметра, которыми мы можем управлять, — это minReplicas и maxReplicas, то есть желаемое минимальное и максимальное количество Pod’ов. 

Ну, и в конечном счете направляем действие HPA на нужный контроллер через спецификацию scaleTargetRef

Важный момент. Сразу после запуска HPA необходимо забыть о ручной регулировке количества реплик через deployment scale. Если этот параметр не будет совпадать с «мнением» HPA, то вызовет ненужное создание или пересоздание Pod’ов — это может негативно сказаться на пользователях.

От 600 до 2300 RPS

Как выяснилось, 600 RPS — это не предел: нагрузка росла ежедневно и достигла пика в последний день The International — 17 октября.

То, что происходило с сайтом во время турнира, лучше всего описал Павел Вирский, тимлид команды разработки cybersport.ru (приводим фрагмент поста Павла из корпоративного Slack’а нашей DevOps-команды):

Павел Вирский, тимлид команды разработки cybersport.ru

«Закончился The International, который был большим испытанием для нашего сайта и серверов и не меньшим вызовом и для нас и для вас. Начиналось всё 8 октября, когда 600 rps укладывали сайт на лопатки. Затем рекорд побивали ещё несколько раз, но с каждым разом всё более подготовленными. И закончилось сегодня, когда при (невероятных для меня) 2300 rps сайт работал. А нагрузка в 1200-1400 стала настолько рядовой, что мы спокойно выкладывали обновления...»

Подытожим

Сейчас инфраструктура cybersport.ru оптимизирована. Единственное, что требуется для принятия нагрузки, — своевременный дозаказ ресурсов. Но это не значит, что улучшать больше нечего. Что еще можно сделать:

  • Разместить Redis на выделенных узлах кластера. Если во время резкого роста нагрузки Redis окажется на нагруженном узле, это может негативно повлиять на инфраструктуру: Redis будет «аффектиться» и вызывать проблемы в работе всех сайтов, которые с ним взаимодействуют. В идеале для сервиса нужно выделить отдельную группу узлов.

  • Увеличить количество slave-узлов для PostgreSQL. Чтобы БД не «упиралась» по скорости чтения в возможности диска, для гипотетических ситуаций можно добавить slave-узлов. По сути, основная нагрузка во время мероприятий создается операциями чтения в базу. Она достаточно легко масштабируется добавлением новых slave-узлов для репликации и перераспределении нагрузки.

Сейчас cybersport.ru такая оптимизация не нужна, и в обозримом будущем потребность в ней вряд ли не возникнет. 

Главное, что благодаря Kubernetes, отлаженному CI/CD, а также четкому взаимодействию инженеров «Фланта» и разработчиков наш клиент достиг важных для себя результатов:

  • Инфраструктура стала надежнее: обновления можно деплоить даже под большой нагрузкой.

  • … и гибче: нет технических ограничений для своевременного масштабирования.

  • Появилась независимость от поставщика инфраструктуры: K8s-кластеры можно легко перенести в новое облако, на bare metal, куда угодно.

  • Приложение оптимизировано и лучше адаптировано для работы в cloud native-среде.

P.S.

Читайте также в нашем блоге:

Комментарии (15)


  1. navicrstl
    24.11.2021 12:01
    +4

    Сайт сайберспорта регулярно падает по время знаковых событий. Когда NaVi выиграли мажор - упал, VP вылетели из турнира - упал. TS победили на инте - упал. Если этим занимались тоже вы, то я бы не сказал что вы справились.

    С другой стороны масштабировать битрикс больно, я вас понимаю


    1. andreios Автор
      24.11.2021 12:02
      +9

      Вот именно из-за таких проблем и проводятся работы вроде упомянутых в статье. 
      По факту - мы не утверждаем, что падений не было и не будет, падает кто угодно (даже google). Не падает только то, что вообще не работает. Самое главное в таких ситуациях - разобраться в причинах и научиться на ошибках, чтобы их не повторить. Ранее было много нюансов в работе проекта, площадок, приложения (часть этих нюансов мы описали в статье), и бОльшую часть из них мы итеративно устраняли совместно с разработчиками. Каждое следующее событие давало нам новый опыт и новый вызов. В статье - показательный пример такого события, которое помогло прийти инфраструктуре к новому витку развития, чтобы соответствовать текущим требованиям.Что касается битрикса - вы заблуждаетесь, он никогда (при нас по крайней мере) не использовался в качестве движка для сайта.


      1. navicrstl
        24.11.2021 12:07
        +2

        Хм, они все таки его наконец поменяли. Cybersport был ранее сайтом virtuspro, который был на битриксе

        Ваше сообщение более чем логично, но если даже масштабирование не спасает от ухода в 403 в пике, то что тогда спасает ? :)

        Зачем было разворачивать инфраструктуру такого рода, если все равно есть таймауты?


        1. andreios Автор
          24.11.2021 12:27
          +13

          Ваше сообщение более чем логично, но если даже масштабирование не спасает от ухода в 403 в пике, то что тогда спасает ? :)

          403 - это все-таки про другое, вы наверное имели в виду 5** ошибки.
          Спасает конечно, разница между упасть на все время всплеска посещений против 5% таймаутов на 10% запросах, как пример :). Плюс вопрос в трудоемкости операций, запасе отказоустойчивости, гибкости и простоты изменения инфраструктуры, и многих других факторах. Я уж не говорю про запас ресурсов, которые без удобной автоматизации, наращивать достаточно некомфортно, особенно в условиях "пожара".
          Инфраструктура такого рода позволила нам избавиться от множества узких мест и перейти на качественно иной уровень. Что касается частных случаев ошибок на сайте - их надо разбираться отдельно и предметно, это в том числе позволит улучшить пользовательский сервис ;).


  1. HellWalk
    24.11.2021 13:34

    В проекте была необходимость строго соблюдать бюджет на инфраструктуру. 

    Не расскажете поподробнее - почему?

    У меня такая логика в голове не укладывается - ключевое мероприятие года, а руководству главное лишнюю копейку не переплатить.

    Плюс, на сколько я знаком (а знаком поверхностно) с облаками - их основная фишка как раз в возможности иметь неограниченные ресурсы, а платить только за то, сколько реально израсходовал.

    поскольку недооценили нагрузки, которые предстоит принять. 

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

    Это не претензия, это больше вопрос на тему того, как вы сами себя оцениваете. Или, как это часто бывает, кто-то в команде говорил, что нужно закладывать больше, но его мнением пренебрегли, потому что "да там же все просто".


    1. andreios Автор
      24.11.2021 18:21
      +5

      Не расскажете поподробнее - почему?У меня такая логика в голове не укладывается - ключевое мероприятие года, а руководству главное лишнюю копейку не переплатить.

      К сожалению не расскажу, это бизнес история которая наверняка имеет свои причины, и мы не хотели бы выдавать какие-то суждения по этому поводу. У нас была вполне конкретная задача и условия.

      Плюс, на сколько я знаком (а знаком поверхностно) с облаками - их основная фишка как раз в возможности иметь неограниченные ресурсы, а платить только за то, сколько реально израсходовал.

      Все так, а вопрос в чем? Мы можем неограниченно скейлиться (докупать ресурсы в автоматическом режиме), но за то, что сейчас запущено и работает - платить обязаны.

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

      Богатый опыт эксплуатации разных по размеру и сложности проектов показывает, что всегда найдется возможность для косяка (уж простите за слэнг) :). Тут же речь не о том, что "кто-то говорил надо больше - мы не послушали" или "мы чайники и не поняли, что надо иначе/больше/лучше", а исключительно в разрезе комплекса факторов в виде определенной ограниченности в ресурсах, сложности выстраивания нагрузочных тестов, невозможности быстро и просто определить, а сколько ресурсов потребуется для сайта, если посетителей будет в 2 раза больше, или в 5, или в 10. Об этом и было сказано в статье. С нашей стороны мы сделали best effort - подготовили такую инфру, которая в базе уже покрывает все эти кейсы, а дальше вопрос регулировки и подстройки для частных случаев.


      1. HellWalk
        24.11.2021 20:45
        +2

        Все так, а вопрос в чем?

        В том, что непонятен смысл облака, с ограничениями.

         невозможности быстро и просто определить, а сколько ресурсов потребуется для сайта, если посетителей будет в 2 раза больше, или в 5, или в 10

        Если бы задачу выполнял рядовой программист - да, согласен, у рядового программиста нет такого опыта. Но вы же специализируетесь на поддержке.

        В общем, как понимаю, не смотря на падения прода, вы считаете, что сделали все, что смогли, и ошибок не допустили?


        1. andreios Автор
          24.11.2021 21:08
          +3

          В том, что непонятен смысл облака, с ограничениями.

          Так их нет, ограничений :). Вы же видите разницу между "заказать железки, дождаться их поставки, настройки, сетапа ОС, настройки ОС, подключить в кластер" и "изменить два параметра в yaml-манифесте и через 5-10 минут получить +20 виртуалок (уже подключенных в кластер и принимающих pod'ы и трафик)"? Так вот это самая поверхностная разница и смысл.

          Если бы задачу выполнял рядовой программист - да, согласен, у рядового программиста нет такого опыта. Но вы же специализируетесь на поддержке.

          Везде хватает своих вызовов. Иначе программист через 10 лет работы не допускал бы уже ошибок и весь его софт работал бы без багов, автослесарь через 200 машин мог бы починить любую проблему с закрытыми глазами и так далее, и развиваться бы им было некуда, верно?

          В общем, как понимаю, не смотря на падения прода, вы считаете, что сделали все, что смогли, и ошибок не допустили?

          Удивительно, я везде говорю и пишу о том, что "да, мы допустили немало ошибок, но мы их учитывали и делали все лучше и лучше с каждым разом, в конечном счете получив на текущий момент качественно новую инфраструктуру", поэтому совершенно не понимаю, каким образом вы сделали такой вывод :).


          1. ToSHiC
            25.11.2021 01:26
            +1

             автослесарь через 200 машин мог бы починить любую проблему с закрытыми глазами

            Опытный и правда может перебрать мотор с закрытыми глазами: https://www.youtube.com/watch?v=1S8R-MKjgfo


  1. hatman
    24.11.2021 15:07
    +4

    Так, он вроде бы из экосистемы VK GROUP. Почему решение вопроса с ним отдали сторонней орге, а не сами спецы из VK.


    1. SaxaROK
      25.11.2021 06:27
      +1

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


  1. IndyCar
    24.11.2021 15:51
    +3

    российские веб-проекты должны хранить данные на территории РФ;

    это относится ко всем данным или только к первичной базе с персональными данными? Лет 6 назад достаточно было хранить первичные персональные данные.


    1. andreios Автор
      24.11.2021 18:33
      +1

      Речь конечно про персональные данные.


  1. Saamm
    25.11.2021 09:05
    +2

    Смотрел интервью с директором Фланта на итбороде, интересно было


    1. Avokados
      25.11.2021 21:02

      Вспомнил интервью после упоминания "slave-узлов" )