Отчет по инцидентам. Как мы не справились с ростом

В августе–сентябре и во время черной пятницы произошло несколько десятков отказов и периодов снижения производительности Mindbox из-за кратного роста нагрузки.
Пожалуйста, обратитесь к вашему менеджеру за компенсацией по SLA, таким образом ваша обратная связь напрямую дойдет до ответственных. Компенсации специально сделаны существенными для нас, а бонусы ответственных привязаны к прибыли.
Такой частоты отказов никогда не было, и, из-за одновременного повышения прозрачности через status.mindbox.group, могло возникнуть ощущение резкого снижения надежности.
В реальности всё не так плохо: есть объективные сложности, мы понимаем причины, часть уже исправили, часть исправляем, в 2020 такие отказы станут невозможными.
Есть и повод для радости: с такой нагрузкой в России сталкиваются единицы компаний, мы приобретаем уникальный опыт, а рост открывает возможности. Вы можете рассчитывать на наши долгосрочные растущие инвестиции в надежность и развитие инфраструктуры для вашего бизнеса.
Ниже открытый отчет о происходящем:
  • Что произошло: кратковременные отказы части функций у части клиентов
  • Причины: не справились с ростом
  • Что сделали: выделили ресурсы, снизили частоту и критичность отказов
  • Сделаем дальше: принципиально поменяем архитектуру и подход к надежности
  • Чего ждать в будущем: ещё несколько отказов и резкое увеличение надежности
  • Технические подробности

Что произошло

Первая серия отказов, несколько десятков, случилась в августе и сентябре. Отказывал единый шлюз протокола api.mindbox.ru, т. е. почти весь функционал у большинства клиентов. Суммарно перебои в работе длились около четырех часов. Пострадавшим клиентам выплатили более миллиона рублей в виде компенсаций по SLA.
Отказывали случайные части сервиса, и нам потребовалось около месяца на идентификацию причин и запуск улучшений.
В неделю черной пятницы по схожим корневым причинам произошло еще 4 краткосрочных отказа в других местах, и ещё три — из-за сопутствующих изменений. Отказы были меньшего масштаба и эффекта. Суммарная длительность — около часа, каждый раз для разных групп клиентов (от 5% до 15%) и разного функционала.

Причины

Основная причина приятная: мы растем быстрее самых смелых прогнозов.
С августа по ноябрь среднесуточная нагрузка по вызовам наших API выросла в два раза, с 60 до 120 тысяч запросов в минуту. Всего в ноябре мы отправили 1,1 миллиарда писем. А вот сколько писем мы отправляем в неделю черной пятницы ежегодно:
Количество писем за неделю черной пятницы
Количество писем за неделю черной пятницы
К сожалению, мы не успели привести архитектуру в соответствие с возросшим уровнем нагрузки, хотя уже два года готовим нужные изменения. Требуется поменять балансировщик нагрузки и способ распределения клиентских приложений по серверам. Для этого нужно перенести все 2 миллиона строк кода на более новую версию библиотек без обратной совместимости (.net core) и отладить под другой операционной системой, параллельно переписав почти весь код мониторинга и выкладки обновлений. Всё это в режиме 24/7 работы под нагрузкой и с быстрым выпуском новых функций.
Одновременно с корневой причиной несколько раз возникало недокументированное поведение различных компонентов при достижении их предельных возможностей. К этому мы привыкли, но в комбинации с другими факторами это вызвало дополнительные сложности.
Еще несколько отказов произошло из-за желания ускорить изменения, чтобы не подводить клиентов. Если бы не общая частота перебоев, они бы уложились в рамки нормального уровня.

Что сделали

  • Выделили больше ресурсов шлюзу (с четырехкратным запасом)
  • Перенесли шлюз на отдельный балансировщик нагрузки и сервера
  • Лучше изолировали компоненты приложения
  • Улучшили систему диагностики и оповещения
  • Повысили отказоустойчивость кода и выкладку приложения
В результате с начала октября надежность шлюза вернулась к нормальной.
Среднее времени ответа и срабатывания мониторинга шлюза
Среднее времени ответа и срабатывания мониторинга шлюза
Мы ошибочно посчитали, что этого достаточно, чтобы дождаться плановых архитектурных изменений в первом полугодии 2020 года, но в неделю черной пятницы произошли новые отказы на веб-серверах с клиентскими приложениями, несмотря на сниженную нагрузку из-за изоляции шлюза. Что приятно — шлюз больше не отказывал.

Дальнейшие планы

В декабре:
  • Внепланово сменим архитектуру балансировки нагрузки
  • Уменьшим количество приложений на одном сервере
  • Усилим изоляцию клиентов друг от друга в рамках одного сервера
Это уменьшит вероятность новых отказов в ближайшие пики нагрузки.
В первом квартале 2020 года:
  • Перенесем отслеживание рассылок в облако
  • Отладим новую архитектуру балансировки нагрузки
Во втором квартале:
  • Перенесем шлюз и клиентские приложения в облако
  • Клиентские приложения будут выполняться в изолированных контейнерах
  • Клиентские приложения будут автоматически балансироваться между серверами
Важно: клиентские данные продолжат храниться на наших собственных серверах в России, облако также будет российским.
В третьем и четвертом квартале:
  • Выделим и изолируем микросервис процессинга и лояльности
  • Сменим сервис очередей на более надежный и производительный
Над изменениями архитектуры работает выделенная команда с самыми опытными специалистами. Начиная со второго квартала 2020 года подключится вторая команда.
По итогам мы ожидаем повышение скорости работы и долгосрочное уменьшение количества инцидентов из-за лучшей изоляции, автобалансировки приложений в кластере kubernetes и уменьшения ошибок администрирования собственной инфраструктуры.
К сожалению, при таких масштабных изменениях неизбежно будут совершаться ошибки, которые вызовут ещё некоторое количество разовых отказов. Мы постараемся их избежать, но просим отнестись к ним с пониманием и запастись терпением.

Технические подробности

Причины

Основная причина: устаревшая архитектура веб-сервисов

С самого начала существования Mindbox мы проектируем веб-сервисы так, чтобы максимально изолировать клиентские данные друг от друга. Бóльшая часть работы с ними происходит в отдельном веб-приложении (в IIS) для каждого клиента. Часть сервисов, включая api.mindbox.ru, обслуживает всех клиентов. Это позволяет управлять безопасностью из единой точки, работать с асинхронными интеграциями, изменять инфраструктуру незаметно для клиентов.
Масштабирование веб-приложений под возрастающую нагрузку происходит через добавление веб-серверов в кластер. Балансировку между серверами осуществляет Microsoft NLB, поэтому все веб-приложения расположены на всех серверах.
Со временем число клиентов достигло предела, когда один сервер с трудом выдерживает такое количество приложений из-за количества потоков, процессов, сокетов.
Ещё одна неудачная для нас особенность NLB — принципы его работы с трафиком. NLB всегда распределяет весь входящий трафик по всем серверам, а отвечает на конкретный запрос только один из них. Какой именно — рассчитывает механизм балансировки NLB. Таким образом, с ростом количества клиентов росло и количество входящего трафика, и все сервера нагружались поровну. Это также вызывало сложно диагностируемые проблемы на сетевом уровне.
Приложение api.mindbox.ru располагалось на тех же веб-серверах. С ростом количества приложений и объемов трафика вероятность его отказа повышалась, пока не стала критической.

Дополнительная причина: метрики и мониторинг

Недостаток метрик затруднил диагностику. На момент первых инцидентов мы не могли смотреть:
  • Метрики в разрезе по клиентам, чтобы исключить некорректные интеграции. В результате наш мониторинг генерировал ложные срабатывания, на разборы которых тратилось время.
  • Долю каждого узла инфраструктуры в общем времени ответа сервиса. Без этой метрики сложно определить точку отказа в случае инцидента.

Дополнительная причина: пределы возможностей инфраструктуры

Для механизма очередей мы используем RabbitMQ. Мы достигли недокументированных пределов его производительности, а из-за недостатка метрик не были уверены, где именно возникает замедление.
Аналогичные сложности возникли с клиентом Redis, который мы используем. Оказалось, что в некоторых ситуациях при большой нагрузке в нем копится очередь блокировок, что приводит к отказу. И снова мы смогли диагностировать проблему, только улучшив метрики.

Дополнительная причина: интернет-канал

С ростом количества клиентов мы стали отправлять 50 терабайт писем в месяц. В результате в августе и сентябре несколько раз полностью использовали наш исходящий канал — 2Гбит/сек. Это незначительно влияло на доступность api.mindbox.ru, однако из-за множества параллельных инцидентов мешало диагностике.
Оперативное расширение канала было невозможно из-за устаревшего способа коммутации с ISP: переключение на 10Гбит-ную оптику заняло несколько недель.

Шаги и планы

Крупные выполненные шаги

  • Создали основу для микросервисной архитектуры: отказы приложений CDP уже не влияют на работоспособность рекомендаций, персонализации и ещё нескольких компонентов
  • Поменяли процесс надежности и восстановления, лучше определили ответственность и выделили дополнительных дежурных для ускорения реакции
  • Выделили инфраструктурную команду из разработчиков, архитекторов и devops / sre специалистов
  • Перевели основную массу кода на .NET Core (в рамках подготовки к переходу на linux стек)
  • Начали миграцию вычислительных мощностей в облако, linux и кластер kubernetes: выбрали провайдера, установили выделенный канал, завершили прототип новой архитектуры и процессов разработки на небольшом приложении

Малые выполненные шаги

  • Изолировали все компоненты api.mindbox.ru от других приложений
  • Разместили api.mindbox.ru на серверах с большим запасом по производительности (пиковая нагрузка на CPU веб-серверов — 13%)
  • Переписали приложение, сделав его менее чувствительным к кратковременным отказам БД и Redis
  • Доработали мониторинг и диагностику: отслеживаем распределение времени ответа по частям кода и инфраструктуры, по типу запроса, по клиенту, а также размеры асинхронных очередей, системные метрики RabbitMQ, Redis и другие
  • Обновили политики алертов: в случае деградации производительности сервиса SMS дежурному теперь приходит в течение двух минут, при этом нет лишних SMS.
  • Улучшили конвейер доставки api.mindbox.ru: дежурный может одной командой сделать откат и в течение нескольких минут вернуть предыдущую версию кода в случае недоступности
  • Распределили нагрузку на RabbitMQ между пятью экземплярами приложения
  • Балансировщик NLB внепланово меняем на HaProxy для уменьшения количества приложений на текущих веб-серверах

Планируем

  • Плавное перемещение в облако частей приложения: трекинг писем, шлюз, клиентские приложения
  • Миграцию на другую систему очередей (Kafka)
  • Выделение микросервиса лояльности
  • Дальнейшие улучшения процесса надежности, метрики и организационные меры: расширение штата, регулярные тренировки по отказоустойчивости