При тестировании распределенных систем нефункциональные требования выходят на первое место, а для обнаружения сложных дефектов приходится применять специальные методы. Мы уже говорили о них с Андреем Сатариным в предыдущем интервью и сегодня попытаемся развить эту тему.
Андрей Сатарин занимается тестированием распределенных систем в Яндексе. Принимал участие в совершенно разных проектах: тестировал игру в Mail.ru, систему облачного детектирования в Лаборатории Касперского, а также систему расчета валютных цен в Deutsche Bank.
— Отказоустойчивость — одно из важнейших нефункциональных требований к распределенным системам. Как проводится тестирование отказоустойчивости?
Андрей Сатарин: Сбои можно эмулировать в тестовой среде, так работает известный инструмент Jepsen, созданный Кайлом Кингсбери (Kyle Kingsbury). Второй подход предполагает внедрение сбоев в продуктивном окружении и обычно ассоциируется с Chaos Monkey компании Netflix, из которого выросло целое движение — хаос-инжиниринг. Он избавляет нас от проблем с повторением продуктовой среды и дает высокую уверенность в работоспособности системы, но более опасен и требует определенной зрелости продукта.
Есть и третий подход, позволяющий проверить работоспособность алгоритмов еще до написания кода с помощью специальных инструментов, таких, например, как TLA+. Два наиболее известных примера его использования: разработка Amazon Web Services и Azure Cosmos DB.
— Как вам кажется, лучше использовать тестовый кластер или рабочую систему? В чем заключаются преимущества и недостатки каждого подхода?
Андрей Сатарин: Все подходы допустимы, но в тестовом кластере мы можем творить что угодно и создавать более опасные для системы ситуации, а в рабочее окружение вносить много сбоев не стоит, потому что велик риск серьезных последствий и есть достаточно жесткие SLA, которые крайне нежелательно нарушать. Здесь нам нужно перезакладываться, например, по железу, либо по другим ресурсам кластера. С другой стороны, тестирование в продакшене добавляет уверенности в работоспособности именно использующейся у нас конфигурации: во многих случаях рабочее окружение чрезвычайно сложно эмулировать не только по железу, добиться аналогичного поведения вашей системы в тестовом кластере получается не всегда.
В тестах обычно используются синтетические нагрузки, но ваша система может работать с другими, имеющими достаточно сложное поведение. Эмулировать это взаимодействие трудно, а в продакшене можно достичь большего покрытия в плане ширины использования функциональности. Но, повторюсь, такой подход более рискован и, чтобы им воспользоваться, нужен зрелый продукт. Необходимы развитые системы реагирования, т. е. нельзя внедрять сбои в продакшене, например, в 3 часа ночи — это достаточно тяжело для людей. Обычно это делается в рабочее время, когда доступна вся команда и даже сложные проблемы можно относительно быстро разрешить.
— Каковы наиболее распространенные ошибки при тестировании отказоустойчивости распределенных систем?
Андрей Сатарин: На мой взгляд, самая главная ошибка — непонимание системы. Нужно четко себе представлять, что система делает, какие в ней есть ограничения и как мы их тестируем. Если у нас, например, есть требования по аптайму, то как их проверять в случае сбоев?
Даже не приводящая к потере данных мелкая проблема может нарушить нам SLA. Нужно понимать, какие сбои будут происходить в реальности, потому что потенциально их можно внедрить огромное множество, но к определенным классам сбоев ваша система может быть не готова by design — следует отделять одно от другого и не пытаться тестировать систему в ситуациях, в которых она в принципе не должна работать.
— Какие есть основные методики проверки производительности распределенной системы? Какие здесь есть тонкости, подводные камни, какие ошибки допускают при тестировании?
Андрей Сатарин: Подходы здесь примерно те же, что и при тестировании состоящих из одного узла систем, только поведение чуть более сложное. Чтобы анализировать результаты тестирования производительности, нужно хорошо понимать поведение системы и в голове одного человека это уложиться не может.
Очень важный момент — сообщать коллегам результаты проведенных экспериментов. В нашей практике часто бывает, когда кто-то исследует производительность одной части системы, а потом присылает на общую рассылку свои данные и выводы — другие члены команды анализируют проведенный эксперимент со своей стороны и добавляют какие-то аспекты. Здесь важно работать в связке с коллегами, которые лучше знают другие части системы, и смотреть на нее под разными углами.
Еще один важный момент при тестировании производительности распределённых систем — показатель масштабируемости. Часто наблюдается ситуация, когда система прекрасно работает, скажем, на десяти нодах в тестовом кластере, но когда вы пытаетесь запустить её в продакшене на ста нодах, выясняется наличие узкого места и производительность рабочего кластера оказывается, допустим, всего в два раза выше. Оценить эту проблему априори на небольших масштабах очень сложно, обычно продуктовое окружение имеет на порядок больший размер чем тестовое. Сделать тестовый кластер тех же масштабов чаще всего невозможно из-за высоких расходов и для проверки масштабируемости системы приходится придумывать специальные подходы.
— Какие еще есть нюансы при тестировании выполнения нефункциональных требований?
Андрей Сатарин: Некоторые узлы системы могут работать медленно из-за аппаратных проблем или, например, из-за слишком большого потребления ресурсов другими сервисами. Часто такие машины существенно тормозят всю систему — простое выключение проблемных узлов приводит к восстановлению производительности, но в продуктовом окружении их бывает сложно автоматически детектировать.
Еще один важный момент — тестирование конфигурации системы. Ваш код может работать прекрасно, но в сложной распределенной системе много конфигурационных параметров и неправильная их настройка может привести к падению производительности, например, или даже к потере данных. Хороший пример такой ситуации рассмотрен в статье Paxos Made Live — An Engineering Perspective. Речь в ней идет о ситуации с Google Chubby, когда сконфигурированный для работы на пяти узлах кластер работал на четырех. Благодаря изначально заложенной в систему отказоустойчивости сервис функционировал, но уже не мог выдержать потерю двух узлов.
— Насколько важна производительность самих тестов и как её можно повысить?
Андрей Сатарин: Если говорить о классических тестах (модульных, системных, интеграционных), то они должны быть достаточно быстрыми. Более высокоуровневые тесты в распределенных системах идут гораздо дольше. Если это случайные внедрения сбоев, например, вам нужно долго искать перебором по пространству возможных сбоев и фундаментальных способов сокращения времени здесь нет — обычно это часы или даже дни.
Нельзя эмулировать кучу разных комбинаций сбоев за короткое время, особенно если вы это делаете на реальном железе. Но чем больше нагрузки вы даете на систему, тем выше вероятность нахождения дефектов. Генерирующая нагрузку часть должна работать быстро — это действительно важно. Здесь можно посоветовать только параллельно запускать тесты или придумывать какие-нибудь хитрости, чтобы они интенсивно нагружали систему.
— Есть какие-то инструменты для автоматизации тестирования нефункциональных параметров распределенных систем?
Андрей Сатарин: Есть известный инструмент Jepsen, который применяется для достаточно широкого класса различных систем: Apache Cassandra, MongoDB и т. д. К сожалению, его нельзя просто запустить из коробки — придется программировать. Имеющиеся инструменты нужно затачивать под тестируемую систему и порог входа у них достаточно высокий. Если говорить о производительности, существуют разнообразные бенчмарки, такие, например, как Yahoo Cloud Server Benchmark, который проверяет различные хранилища, вроде уже упомянутых Cassandra и MongoDB.
— Какие проблемы в сфере тестирования распределенных систем пока остаются нерешенными? Расскажите об основных тенденциях развития этого направления.
Андрей Сатарин: Эта область становится более зрелой, многие компании начинают использовать сложные тесты типа Jepsen для внедрения сбоев с проверкой уровня консистентности своих систем. В последнее время активно применяются формальные методы, о которых я уже говорил — TLA+ и формальная верификация заложенных в распределенную систему алгоритмов.
Конечно, здесь есть подводные камни: даже в полностью верифицированной распределенной системе, код которой был сгенерирован из формальной спецификации (такие разработки есть у Microsoft Research, например) остаются дефекты, которые влияют в т. ч. на отказоустойчивость и безопасность системы.
На Гейзенбаг 2017 Андрей Сатарин расскажет об использовании санитайзеров — замечательных инструментов, позволяющих находить сложные дефекты в программах на C++. В Яндексе их активно применяют для тестирования распределнных систем.
Мойте руки перед едой, или санитайзеры в тестировании
Полная программа конференции доступна на сайте.
Автор: jetliner