Регрессия и нагрузочное тестирование: как проверить надежность до продакшена?¶
Когда говорят о надежности, неинженерная аудитория часто представляет себе реакцию: мониторы, дежурных, экстренные вызовы посреди ночи, героическое тушение пожаров. Это красивая картинка, но она в корне неверна. Настоящая надежность начинается не в момент падения системы, а задолго до него. Она закладывается тогда, когда код еще даже не попал в продакшен, на этапе разработки и тестирования. Это проактивная, а не реактивная работа.
Представьте, что вы ставите спектакль. Вы написали пьесу, выучили роли, сделали декорации. И вот наступает премьера. Зал полон. Свет гаснет, занавес открывается... и вы понимаете, что у актеров не хватает голоса, чтобы перекричать оркестр, или что декорации поставили так, что они загораживают актеров, или что монолог, который так хорошо звучал на репетиции в пустом зале, совершенно не слышен на последних рядах.
Что пошло не так? Да все, и из-за того, что вы не репетировали в условиях, приближенных к реальным.
В разработке программного обеспечения происходит то же самое. Код может быть идеальным с точки зрения функциональности. Он может проходить все тесты, быть красивым, читаемым, с комментариями. Но когда на него обрушивается настоящий, реальный пользовательский трафик, то тут начинаются сюрпризы. База данных не справляется с количеством запросов; таймауты срабатывают раньше, чем сервис успевает ответить; память заканчивается через час после релиза.
Поэтому прежде чем выпускать систему в настоящий полет, нужно устроить ей генеральную репетицию. И здесь у SRE есть два главных инструмента: нагрузочное тестирование и chaos (кейос) engineering.
Нагрузочное тестирование это репетиция при полном зале. Вы генерируете искусственную нагрузку, максимально приближенную к реальной, и смотрите, как система себя ведет. Сколько запросов в секунду она может обработать, прежде чем начнет подыхать? Как растет время ответа сервиса с увеличением нагрузки? В какой момент начнут падать первые запросы? Где узкое горлышко, которое задушит всю систему?
Хорошее нагрузочное тестирование отвечает не на вопрос "работает или не работает", оно отвечает на вопрос "как долго это будет работать при таком-то количестве пользователей". Это принципиально другой уровень понимания.
Иногда результаты бывают обескураживающими. Система, которая блестяще справлялась с одним пользователем на тестовом стенде, падает на десяти. Или время ответа линейно растет с нагрузкой, и вы понимаете, что через месяц, когда придет миллион пользователей, все встанет. Лучше узнать об этом сейчас, в лабораторных условиях, чем в день запуска рекламной кампании.
Нагрузочное тестирование бывает разным. Нагрузочное в узком смысле это когда вы постепенно увеличиваете нагрузку и смотрите, до какого предела система тянет. Стресс-тестирование это когда вы сразу даете нагрузку значительно выше ожидаемой, чтобы увидеть, как система умирает и умирает ли она gracefully или падает с дампом памяти. Тестирование стабильности это когда вы держите высокую нагрузку длительное время и смотрите, не течет ли память, не перегреваются ли серверы, не накапливаются ли ошибки.
У каждого типа тестирования своя цель, но объединяет их одно: они переносят обнаружение проблем из боевых условий в контролируемую среду, когда есть шанс эти проблемы исправить заранее, до грандиозного факапа. Да и цена ошибки в контролируемой среде на порядки ниже, чем на проде.
Но нагрузочное тестирование проверяет только то, что вы предусмотрели. Вы создаете сценарии, которые, как вы думаете, будут в реальности. Вы моделируете типичное поведение пользователей. Но реальность, как известно, богаче любых сценариев и не все сценарии можно учесть и протестировать.
Пользователи делают неожиданные вещи. Они кликают туда, куда не надо. Они отправляют формы по сто раз. Они используют приложение с допотопных браузеров и с суперсовременных устройств. Они приходят волнами, которые невозможно предсказать. А еще бывают сбои. Отключаются серверы. Падает сеть. Зависают базы данных. И весь этот хаос происходит одновременно с пиковой нагрузкой.
Как проверить, что система выживет в таком аду? Для этого существует chaos engineering.
Это дисциплина, направленная на повышение отказоустойчивости систем путем умышленного внесения сбоев (сбои сети, отказ узлов, высокая нагрузка) в контролируемую, обычно продакшн, среду. Этот подход помогает выявлять скрытые слабые места до возникновения реальных инцидентов, обеспечивая надежность.
Chaos engineering это когда вы во время репетиции внезапно отключаете свет. Или убираете стул из-под актера. Или включаете пожарную сирену в середине монолога. Звучит безумно, но именно так проверяется настоящая устойчивость.
Идея chaos engineering родилась в Netflix, и она гениальна в своей простоте. Если у вас распределенная система, в которой тысячи серверов работают вместе, рано или поздно какой-нибудь сервер гарантированно упадет. Это не вопрос вероятности, это вопрос времени, это однозначно случится. Так зачем ждать, пока это случится случайно? Давайте сделаем это специально, прямо сейчас, в контролируемых условиях, и посмотрим, что произойдет.
Netflix создал инструмент Chaos Monkey, который в случайное время отключает случайные серверы в продакшене. Буквально убивает работающие инстансы. И смотрит, выживет ли система. Если выживает - отлично, надежность подтверждена. Если нет, то инженеры идут чинить код, пока следующий Chaos Monkey не придет снова.
Это кажется безумием только на первый взгляд. Но на самом деле это высшая форма проактивной работы над надежностью, высшая форма зрелости SRE в компании. Вы не ждете, пока реальная авария покажет слабые места. Вы создаете аварии сами, в удобное для вас время, и учитесь на них.
Chaos engineering бывает разной степени жестокости. Можно начинать с малого: отключать один экземпляр сервиса в тестовой среде. Потом переходить к отключению целых зон доступности в стейджинге. Потом, когда команда наберется опыта и уверенности, можно проводить эксперименты в продакшене, но с защитными механизмами. Например, отключать серверы только в часы минимальной нагрузки. Или только те, которые не обслуживают критический трафик.
Главное правило chaos engineering: эксперимент должен иметь возможность быть остановленным в любой момент, если что-то пошло не по плану, он должен быть полностью контролируемым.
Важно понимать, что и нагрузочное тестирование, и chaos engineering это не разовые акции, это регулярная практика. Нельзя провести учение, отчитаться руководству, поставить галочку и забыть. Системы меняются каждый день. Новый код, новые конфигурации, новые зависимости. То, что выдерживало нагрузку месяц назад, может не выдержать сегодня из-за невинного на первый взгляд изменения.
Поэтому в зрелых SRE-организациях нагрузочные тесты и chaos-эксперименты встроены в конвейер разработки. Каждое изменение проходит через автоматические сценарии, которые проверяют, не сломало ли оно что-то важное. Конечно, не каждый коммит гоняет полномасштабное нагрузочное тестирование, это слишком дорого. Но ключевые метрики производительности можно проверять автоматически на каждом билде.
Если время ответа выросло на пять процентов после вчерашнего релиза, вы должны узнать об этом сегодня, а не через месяц, когда пользователи начнут жаловаться.
Есть еще один важный аспект, о котором часто забывают: нагрузочное тестирование и chaos engineering проверяют не только код, они проверяют процессы и людей.
Когда во время эксперимента что-то ломается, включаются те же механизмы, что и при реальной аварии. Срабатывают алерты, приходят дежурные, начинается диагностика. И здесь можно проверить, работают ли ваши процессы и процедуры. Достаточно ли быстро приходят оповещения? Правильно ли настроены эскалации? Есть ли у нужных людей доступ к нужным системам? Актуальны ли и работают ли runbook'и?
Хороший chaos-эксперимент это тренировка для всей организации. Он показывает не только технические дыры, но и дыры в коммуникации, в процессах, в культуре.
Конечно, у всего этого есть цена. Нагрузочное тестирование требует инфраструктуры. Чтобы проверить, выдержит ли система миллион пользователей, нужно создать нагрузку, эквивалентную миллиону пользователей. Это стоит денег. Chaos engineering требует времени инженеров и может приводить к реальным сбоям, даже если они очень осторожны.
Но сравните это с ценой реального сбоя. Один час простоя ключевого сервиса/продукта может стоить миллионы. Один испорченный релиз может убить доверие пользователей на годы. Инвестиции в проактивное тестирование окупаются многократно.
Поэтому ответ на вопрос "как проверить надежность до продакшена" звучит так: вы должны сделать продакшен максимально скучным событием. К моменту, когда код попадает к реальным пользователям, вы уже должны знать о нем всё: как он ведет себя под нагрузкой, где его пределы, при каких условиях и как он умирает, как восстанавливается, что происходит, когда отключаются зависимости.
Идеальный релиз это не тот, после которого ничего не сломалось. Идеальный релиз это тот, после которого не случилось ничего неожиданного. Потому что все возможные сценарии вы уже отрепетировали заранее. При полном зале. И с выключенным светом.