kai.kzLab

Pokémon GO в 2016 году: как игра получила в 50 раз больше трафика, чем планировали, и что сделали Niantic и Google

Команда KAI Lab · · 9 мин чтения

Летом 2016 года Pokémon GO получила в 50 раз больше трафика, чем закладывала в план студия Niantic, и в 10 раз больше её худшего сценария. Игру удержали на Google Container Engine (GKE): на ходу обновили кластер Kubernetes, чтобы добавить в него больше тысячи узлов, и заменили сетевой балансировщик на глобальный HTTP(S) Load Balancer.

Сам переход на новый балансировщик вызвал каскадный сбой. Клиенты игры повторяли запросы синхронно, всплески дошли до 20× прошлого пика, и ёмкость балансировщиков Google по всему миру фактически упала вдвое. Эту историю рассказал сам Google: в блоге Google Cloud и в книге The Site Reliability Workbook.

Главное о запуске в одной таблице

ВопросОтвет
Когдаиюль 2016: Австралия и Новая Зеландия, на следующий день США, через две недели Япония
План1X трафика, худший сценарий — примерно 5X. Нагрузочные тесты — до 5X самых оптимистичных оценок
Факт50X плана, в 10 раз больше худшего сценария
Где работала игралогика — в GKE на Kubernetes, основная база — Cloud Datastore, всего больше десятка сервисов Google Cloud
Масштабдесятки тысяч ядер, крупнейшее на тот момент развёртывание Kubernetes в GKE
Что меняли на ходуверсию GKE ради тысячи с лишним новых узлов; Network Load Balancer на HTTP(S) Load Balancer
Каскадный сбойспрос на 200% выше наблюдаемого, синхронные повторы до 20× прошлого пика, ёмкость балансировщиков в мире −50%
Итогзапуск в Японии прошёл без инцидентов, хотя новых игроков было втрое больше, чем при запуске в США

Хронология запуска Pokémon GO

КогдаЧто произошло
до запускаNiantic тестирует стек на нагрузку до 5X самых оптимистичных оценок
запуск в Австралии и Новой ЗеландииЧерез 15 минут трафик уходит далеко за ожидания. Niantic зовёт на помощь команду Google CRE: запуск в США — на следующий день
запуск в СШАТрафик растёт до 50X плана. График транзакций Cloud Datastore — главной базы игры — становится прямым показателем её популярности
следующие неделиGoogle выделяет десятки тысяч ядер. Кластер обновляют до новой версии GKE, в которую можно добавить больше тысячи узлов
перед запуском в ЯпонииПосле крупной SYN-флуд-атаки Network Load Balancer заменяют на HTTP(S) Load Balancer. Через два дня Pokémon GO — самый большой сервис на нём
после переездаКаскадный сбой: балансировщики Google теряют половину ёмкости, страдают все их клиенты
через две недели после СШАЗапуск в Японии без инцидентов, регистраций втрое больше, чем при запуске в США

50X — это не «просто добавить подов»

Команды Google и Niantic планировали 1X и держали в уме худший сценарий в 5X. Реальность оказалась в 50X. Google выделил под кластер Niantic десятки тысяч ядер. По словам Google, это было крупнейшее развёртывание Kubernetes в Container Engine за всю его историю.

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

Ёмкость в Kubernetes многослойная. Поды упираются в узлы, узлы — в лимиты кластера и облачные квоты, всё вместе — в балансировщик и базу. Когда кончаются узлы, новые поды просто висят в статусе Pending, как в разборе ошибки Pod Pending. В июле 2016 года Niantic прошла все эти слои по очереди.

Новый балансировщик и каскадный сбой

Как было

Трафик принимал региональный Network Load Balancer и раскидывал его по кластеру Kubernetes. В кластере работали поды с Nginx. Они завершали SSL — это две сетевые поездки от телефона до прокси — и буферизовали HTTP-запросы, из-за чего на прокси кончались ресурсы. Защиты от низкоуровневых сетевых атак у этой схемы не было, и крупная SYN-флуд-атака сделала переезд срочным.

Что дал GCLB

В глобальном балансировщике Google (GCLB) SSL завершают Google Front End (GFE) — серверы на границе сети Google, как можно ближе к пользователю. К бэкендам GFE держат постоянные соединения, а трафик между GFE распределяет Maglev. Через два дня после переезда Pokémon GO стала самым большим сервисом GCLB.

Как всё сломалось

  1. Спрос оказался больше, чем думали. Раньше прокси Niantic не успевали принять все соединения, а отказанные соединения не попадали в мониторинг входящих запросов. После переезда выяснилось, что реальный спрос на 200% выше наблюдаемого.
  2. Бэкенды стали медленными, а не отказывали. Перегруженные бэкенды не отвечали отказом, а тормозили. Запросы упирались в таймауты, и балансировщик повторял GET-запросы, добавляя нагрузку.
  3. Пострадал сам балансировщик. Код GFE, который переподключался по SSL к зависшим бэкендам, получил небывалую нагрузку. Производительность GFE просела так, что ёмкость GCLB по всему миру фактически упала на 50%.
  4. Клиенты пришли толпой. Приложение при ошибке делало один мгновенный повтор, потом ждало фиксированное время. Когда общие бэкенды падали и перезапускались, клиенты повторяли запросы практически одновременно. Google называет это «thundering herd»: всплески дошли до 20× прошлого мирового пика запросов в секунду.

Очереди и высокие задержки получили все сервисы на GCLB, а не только игра.

Как остановили

Команда Traffic SRE отделила GFE, которые обслуживали Pokémon GO, от общего пула и расширяла этот пул, пока он не начал выдерживать пиковый трафик. Затем поставила административный лимит на скорость трафика, который балансировщики принимали для игры, чтобы Niantic восстанавливалась и наращивала бэкенды постепенно.

После сбоя Niantic добавила в клиент джиттер и ограниченную экспоненциальную задержку повторов. Google ввёл нагрузочные тесты, которые ловят деградацию GFE из-за медленных бэкендов.

Уроки для DevOps

1. Худший сценарий — это ещё не худший сценарий

Niantic тестировала нагрузку до 5X, а пришло 50X. Не существует запаса, который гарантированно хватит. Поэтому, кроме запаса, нужны способы деградировать управляемо: лимиты, очереди, отключаемые функции.

2. Мерьте нагрузку как можно ближе к клиенту

Это главный вывод Google из case study. Если прокси отбрасывает соединения, в логах приложения их нет, и вы видите не спрос, а ту часть, которую успели принять. На Linux часть потерь видна в счётчиках ядра:

# соединения, отброшенные из-за переполненной очереди accept, — в логах nginx их не будет
nstat -az TcpExtListenOverflows TcpExtListenDrops

Растущие значения значат, что клиенты стучатся, а сервер их не пускает. Метрики доступности, которые считают со стороны клиента, мы разбирали в статье про SLO, SLI и бюджет ошибок.

3. Медленный отказ опаснее быстрого

Перегруженный бэкенд, который отвечает за 30 секунд, держит соединения, съедает ресурсы балансировщика и провоцирует повторы. Быстрый отказ с кодом 503 и коротким таймаутом дешевле для всех. Отдавайте ошибку сразу, когда очередь переполнена.

4. Повторы — с экспоненциальной задержкой и джиттером

Мгновенный повтор и фиксированная пауза синхронизируют клиентов: все упали вместе и вместе вернулись. Задержка должна расти, иметь потолок и случайную часть:

# повтор с экспоненциальной задержкой, потолком и «полным джиттером»
max_delay=30
for attempt in 1 2 3 4 5 6; do
  curl -fsS --max-time 5 https://api.example.com/v1/status && break
  delay=$(( 2 ** attempt )); (( delay > max_delay )) && delay=$max_delay
  sleep $(( RANDOM % delay + 1 ))   # случайно от 1 до delay секунд
done

То же относится к ретраям в сервис-мешах, клиентских библиотеках и джобах CI: везде, где повтор настроен по умолчанию, проверьте, есть ли у него задержка и джиттер.

5. Выкатывайтесь волнами

Австралия и Новая Зеландия оказались канарейкой для США, США — для Японии. Каждая волна показала узкие места до следующей. Регион за регионом — тот же принцип, что поэтапная выкатка релиза.

6. Держите аварийный тормоз

Административный лимит трафика вернул систему под контроль. У вашего сервиса должен быть заранее отрепетированный рычаг: rate limit на входе, отключение тяжёлых функций, страница «мы перегружены» вместо зависания.

Что проверить у себя завтра:

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

Реши сам

Два вопроса по этому сбою. Ответы — под спойлером.

Вопрос 1. Почему повторы запросов из приложения усилили сбой?

  1. Приложение не повторяло запросы вообще
  2. Один мгновенный повтор и фиксированная пауза синхронизировали клиентов, и они возвращались толпой
  3. Повторы шли через другой балансировщик
  4. Повторы блокировал антивирус
Показать ответ

Верный ответ — 2. Когда бэкенды падали и поднимались, клиенты повторяли запросы одновременно, и всплески доходили до 20× прошлого пика. Niantic добавила джиттер и ограниченную экспоненциальную задержку.

Вопрос 2. Почему после переезда на GCLB реальный спрос оказался на 200% выше наблюдаемого?

  1. Игроков стало втрое больше за два дня
  2. Раньше прокси отбрасывали часть соединений, и эти отказы не попадали в мониторинг входящих запросов
  3. GCLB считает запросы дважды
  4. Мониторинг был выключен
Показать ответ

Верный ответ — 2. Мониторинг видел только принятые запросы. Новый балансировщик принимал больше соединений и показал настоящий спрос. Отсюда вывод Google: мерить нагрузку как можно ближе к клиенту.

Частые вопросы

Во сколько раз трафик Pokémon GO превысил план? В 50 раз. Команды планировали 1X, худший сценарий оценивали примерно в 5X, так что реальный трафик оказался в 10 раз больше худшего сценария.

На чём работала Pokémon GO? Логика игры работала в Google Container Engine (сегодня Google Kubernetes Engine) на Kubernetes. Основной базой был Cloud Datastore, а всего игра использовала больше десятка сервисов Google Cloud.

Что такое Google CRE? Customer Reliability Engineering — команда Google, которая работает внутри команд клиентов и делит с ними ответственность за надёжность важных приложений. Niantic позвала CRE через 15 минут после запуска в Австралии и Новой Зеландии.

Зачем меняли балансировщик посреди кризиса? Старая схема завершала SSL на подах Nginx, тратила на это лишние поездки по сети и не защищала от низкоуровневых атак. После крупной SYN-флуд-атаки переезд на глобальный HTTP(S) Load Balancer стал приоритетом.

Пройти похожий сбой самому

В симуляторе DevOps KAI Lab седьмой месяц посвящён переезду в Kubernetes. В нём есть разбор запуска Pokémon GO и тикет про автомасштабирование checkout перед ростом нагрузки. Как обновление Kubernetes может уронить весь сайт, показывает сбой Reddit 2023, а все разборы собраны в подборке 18 сбоев, которые должен знать DevOps-инженер.

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

Источники

Читать дальше