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

В этой статье представлены наиболее распространенные методы и шаблоны архитектуры для создания и эксплуатации системы высокодоступных микросервисов на основе опыта консалтинга и разработки RisingStack по Node.js.

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

Риск архитектуры микросервисов

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

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

Изящное ухудшение качества обслуживания

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

Сбой микросервисов по отдельности (теоретически)

В большинстве случаев сложно реализовать такую ​​постепенную деградацию сервиса, поскольку приложения в распределенной системе зависят друг от друга, и вам необходимо применить несколько логик аварийного переключения (некоторые из них будут рассмотрены в этой статье позже) для подготовки к временным сбоям и отключениям.

Службы зависят друг от друга и выходят из строя вместе без логики аварийного переключения.

Управление изменениями

Команда Google по обеспечению надежности сайта обнаружила, что примерно 70% сбоев вызвано изменениями в действующей системе. Когда вы что-то меняете в своем сервисе - вы развертываете новую версию своего кода или меняете какую-то конфигурацию - всегда есть шанс на сбой или появление новой ошибки.

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

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

Управление изменениями - последовательное развертывание

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

Возврат кода назад - неплохое дело. Не следует оставлять неработающий код в рабочей среде, а затем думать о том, что пошло не так. Всегда возвращайте изменения, когда это необходимо. Чем скорее, тем лучше.

Проверка работоспособности и балансировка нагрузки

Экземпляры постоянно запускаются, перезапускаются и останавливаются из-за сбоев, развертываний или автомасштабирования. Это делает их временно или постоянно недоступными. Чтобы избежать проблем, ваш балансировщик нагрузки должен пропускать нездоровые экземпляры из маршрутизации, поскольку они не могут удовлетворить потребности ваших клиентов или подсистем.

Состояние экземпляра приложения можно определить с помощью внешнего наблюдения. Вы можете сделать это, неоднократно вызывая GET /health конечную точку или используя самооценку. Современные решения обнаружения служб непрерывно собирают информацию о работоспособности от экземпляров и настраивают балансировщик нагрузки на маршрутизацию трафика только к работоспособным компонентам.

Самовосстановление

Самовосстановление может помочь восстановить приложение. Мы можем говорить о самовосстановлении, когда приложение может выполнять необходимые шаги для восстановления после неисправного состояния. В большинстве случаев это реализуется внешней системой, которая следит за работоспособностью экземпляров и перезапускает их, когда они находятся в неисправном состоянии в течение более длительного периода. Самовосстановление может быть очень полезным в большинстве случаев, однако в определенных ситуациях оно может вызвать проблемы из-за постоянного перезапуска приложения. Это может произойти, если ваше приложение не может дать положительный статус работоспособности из-за перегрузки или таймаутов подключения к базе данных.

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

Отказоустойчивое кэширование

Службы обычно не работают из-за проблем с сетью и изменений в нашей системе. Однако большинство этих сбоев являются временными благодаря самовосстановлению и расширенной балансировке нагрузки, и мы должны найти решение, чтобы наша служба работала во время этих сбоев. Здесь может помочь резервное кэширование и предоставить необходимые данные нашему приложению.

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

Кэширование при отказе

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

Чтобы установить кэш и кеш переключения при отказе, вы можете использовать стандартные заголовки ответов в HTTP.

Например, с помощью заголовка max-age вы можете указать максимальное время, в течение которого ресурс будет считаться свежим. С помощью заголовка stale-if-error вы можете определить, как долго ресурс должен обслуживаться из кеша в случае сбоя.

Современные сети CDN и балансировщики нагрузки обеспечивают различное поведение кэширования и аварийного переключения, но вы также можете создать для своей компании общую библиотеку, содержащую стандартные решения для обеспечения надежности.

Логика повтора

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

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

В распределенной системе повторная попытка микросервисной системы может инициировать несколько других запросов или повторных попыток и запустить каскадный эффект. Чтобы свести к минимуму влияние повторных попыток, вы должны ограничить их количество и использовать алгоритм экспоненциальной отсрочки, чтобы постоянно увеличивать задержку между повторными попытками, пока вы не достигнете максимального предела.

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

Ограничители скорости и сбросы нагрузки

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

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

Ограничитель скорости может сдерживать пики трафика

Другой тип ограничителя скорости называется ограничителем одновременных запросов. Это может быть полезно, когда у вас есть дорогие конечные точки, которые не следует вызывать чаще указанного времени, а вы все еще хотите обслуживать трафик.

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

Чтобы узнать больше об ограничителях скорости и измельчителях нагрузки, я рекомендую прочитать Статью Stripe.

Отказаться быстро и независимо

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

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

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

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

Переборки

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

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

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

Одна из основных причин, по которой «Титаник» затонул, заключалась в том, что его переборки имели неисправность, и вода могла пролиться через верхнюю часть переборок через верхнюю палубу и затопить весь корпус.

Переборки в Титанике (не сработали)

Предохранители

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

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

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

Имейте в виду, что не все ошибки должны вызывать срабатывание автоматического выключателя. Например, вы, вероятно, захотите пропустить проблемы на стороне клиента, такие как запросы с 4xx кодами ответа, но включить 5xx сбои на стороне сервера. Некоторые автоматические выключатели также могут находиться в полуоткрытом состоянии. В этом состоянии служба отправляет первый запрос на проверку доступности системы, позволяя другим запросам завершиться ошибкой. Если этот первый запрос завершается успешно, он переводит автоматический выключатель в замкнутое состояние и пропускает поток трафика. В противном случае он держит его открытым.

Автоматический выключатель

Тестирование на отказы

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

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

Одним из самых популярных решений для тестирования является инструмент обеспечения отказоустойчивости ChaosMonkey от Netflix.

Outro

Внедрить и запустить надежный сервис непросто. Это требует больших усилий с вашей стороны, а также стоит денег для вашей компании.

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

Ключевые моменты

  • Динамические среды и распределенные системы, такие как микросервисы, повышают вероятность сбоев.
  • Службы должны отказываться по отдельности, чтобы обеспечить плавное снижение производительности для улучшения взаимодействия с пользователем.
  • 70% сбоев вызваны изменениями, откат кода - неплохая вещь.
  • Не справляйтесь быстро и самостоятельно. Команды не могут контролировать зависимости своих сервисов.
  • Архитектурные шаблоны и методы, такие как кэширование, переборки, автоматические выключатели и ограничители скорости, помогают создавать надежные микросервисы.

Чтобы узнать больше о запуске надежного сервиса, ознакомьтесь с нашей бесплатной электронной книгой Node.js Monitoring, Alerting & Reliability 101. Если вам нужна помощь во внедрении системы микросервисов, свяжитесь с нами по адресу @RisingStack в Twitter или зарегистрируйтесь в нашей предстоящей статье Создание микросервисов с помощью Node.js.

Первоначально опубликовано на blog.risingstack.com 15 августа 2017 г.