Хабрахабр

[Перевод] Проверки работоспособности и постепенная деградация распределенных систем

Как всегда, спасибо Фреду Хеберту и Саргуну Дхиллону за то, что прочли черновик этой статьи и предложили нескольких бесценных советов.

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

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

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

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

Два типа проверок работоспособности

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

Проверка готовности используется, чтобы определить способность пода обслуживать трафик. Например, Kubernetes реализует проверку путем анализа готовности и живучести. С другой стороны, проверка живучести используется, чтобы определить реакцию службы на зависание или блокировку. Если проверка готовности не выполнена, удаляется под из оконечных точек, составляющих службу, и из-за этого в поде, пока проверка не будет выполнена, никакой трафик не маршрутизируется. Аналогичным образом Consul допускает несколько форм проверок (checks): на основе script, проверки на основе HTTP, направленные на конкретный URL, проверки на основе TTL или даже проверки псевдонимов. Если не выполняется она, идет перезапуск индивидуального контейнера в kubelet.

Например, в gRPC проверка работоспособности сама по себе становится вызовом RPC. Наиболее распространенный метод реализации проверки работоспособности на уровне службы — это определение оконечной точки проверки работоспособности. gRPC также допускает проверки работоспособности на уровне службы и общие проверки работоспособности сервера gRPC.

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

Работоспособность — это спектр, а не бинарная таксономия

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

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

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

В службах с высоким уровнем конкуренции «перегрузка» аккуратно сопоставляется с количеством параллельных запросов, обрабатываемых только одним процессом с избыточной очередизацией, который может привести к увеличению задержки для вызова RPC (хотя чаще всего служба нижнего уровня просто ставит запрос в режим ожидания и повторяет попытку по истечении заданного времени ожидания). Часто работу невозможно закончить из-за того, что процесс перегружен. В особенности это верно в том случае, если конечная точка проверки работоспособности настроена на автоматический возврат к коду состояния HTTP 200, в то время как реальная операция, выполняемая службой, предполагает сетевой ввод-вывод или вычисление.

image

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

Только самые тривиальные службы можно построить на предположении об отсутствии некоторой степени частичного отказа в любой период, где частичный отказ подразумевает, что некоторые функции работают, а другие отключаются, а не только «некоторые запросы выполняются, некоторые не выполняются». Вполне возможно, что процесс колеблется между различными степенями работоспособности в течение своего срока службы: от полной работоспособности (например, способность функционировать на ожидаемом уровне параллелизма) до грани неработоспособности (когда очереди начинают заполняться) и точки, где процесс полностью переходит в неработоспособную зону (ощущается снижение качества обслуживания). Если архитектура службы не позволяет корректно исправить частичный отказ, то задача по исправлению ошибок автоматически ложится на клиента.

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

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

Балансировка нагрузки (и, как следствие, сброс нагрузки) часто сводится к эффективному управлению параллелизмом и применению противодавления, не давая системе перегрузиться. Скажу по опыту: неограниченный параллелизм — это зачастую основной фактор, ведущий к деградации службы или постоянному снижению производительности.

Необходимость обратной связи при применении противодавления

Статья любопытна целиком, но главный вывод (по крайней мере для меня) заключался в необходимости обратной связи между процессом и его выходным блоком (обычно это балансировщик нагрузки, но иногда — и другая служба). Мэтт Ранни написал феноменальную статью о неограниченном параллелизме и необходимости противодавления в Node.js.

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

Некоторые балансировщики нагрузки (в частности, HAProxy) предоставляют множество статистических данных о длине внутренних очередей для каждого сервера и серверной части. Ограничение скорости и обрыв цепи на основе статических порогов и пределов могут оказаться ненадежными и нестабильными с точки зрения как корректности, так и масштабируемости. Ссылка на документы: Кроме того, HAProxy допускает выполнение проверки агента (agent-check) (вспомогательной проверки, независимой от обычной проверки работоспособности), что позволяет процессу предоставлять прокси-серверу более точную и динамическую обратную связь о работоспособности.

Строка состоит из ряда слов, разделенных пробелами, знаками табуляции или запятыми в любом порядке, необязательно заканчивающегося на /r и/или /n и включающего следующие элементы: Проверка работоспособности агента выполняется путем TCP-подключения к порту на основе заданного параметра agent-port и считывания строки ASCII.

Значения в этом формате определяют вес пропорционально начальному
весовому значению сервера, настроенному при запуске HAProxy. — Представление положительного целого процентного значения ASCII, например, 75%. Обратите внимание, что нулевое весовое значение указывается на странице статистики как DRAIN с момента аналогичного воздействия на сервер (оно удаляется из фермы LB).

Значения в
этом формате определяют параметр сервера maxconn.  — Параметр строки maxconn: за ним следует целое число (без пробела). Например: maxconn:30 Максимальное число
заявленных соединений необходимо умножить на число балансировщиков нагрузки и различных серверных частей, использующих эту проверку работоспособности, для получения общего числа соединений, которые может установить сервер.

Это переводит административное состояние сервера в
режим READY, отменяя состояние DRAIN или MAINT.  — Слово ready.

Это переводит административное состояние сервера в
режим DRAIN («сток»), после чего сервер не будет принимать новые соединения, за исключением соединений, которые принимаются через базу данных.  — Слово drain.

Это переводит административное состояние сервера в
режим MAINT («обслуживание»), после чего сервер не будет принимать никакие новые соединения, и проверки работоспособности останавливаются.  — Слово maint.

Все они указывают на рабочее состояние сервера DOWN («выкл»), но, поскольку само слово отображается на странице статистики, разница позволяет администратору определить, была ли ситуация ожидаемой: служба может быть намеренно остановлена, может появиться, но не пройти некоторые тесты подтверждения, или рассматриваться как отключенная (отсутствует процесс, нет ответа от порта).  — Слова down, failed или stopped, за которыми может следовать строка описания после символа диез (#).

 — Слово up указывает на рабочее состояние сервера UP («вкл»), если проверки работоспособности также подтверждают доступность службы.

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

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

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

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

С помощью простого URL API изображения извлекаются и преобразуются в режиме реального времени и затем используются в любой точке мира через CDN. Раньше я работала в imgix, стартап-компании по обработке изображений в реальном времени. Наш стек был довольно сложным (как описано выше), но если вкратце, наша инфраструктура включала уровень балансировки и распределения нагрузки (в тандеме с уровнем получения данных из источника), уровень кэширования источника, уровень обработки изображений и уровень доставки контента.

image

Это была сугубо внутренняя служба; на грани мы запускали nginx и HAProxy и Spillway, таким образом, не была рассчитана на завершение TLS или выполнение каких-либо иных функций из того бесчисленного множества, которое обычно в компетенции пограничного прокси. В основе уровня балансировки нагрузки находилась служба Spillway, выступавшая в качестве обратного прокси и брокера запросов.

Хотя изначально оба компонента находились в одном двоичном файле, в какой-то момент мы решили разделить их на отдельные двоичные файлы, которые разворачивались одновременно на одном хосте. Spillway состояла из двух компонентов: клиентской части (Spillway FE) и брокера. Задача клиентской части заключалась в выполнении предварительной обработки каждого запроса, включая предварительную проверку на уровне кэширования источника, чтобы убедиться в кэшировании изображения в нашем центре обработки данных перед направлением исполнителю запроса на преобразование изображения. Главным образом, потому, что эти два компонента имели различные профили производительности, а клиентская часть была почти полностью связана с процессором.

Исполнители отвечали за фактическое преобразование изображения (обрезка, изменение размера, обработка PDF, GIF-рендеринг и т.д.). В любой момент времени у нас был фиксированный пул (дюжина или около того, если память не изменяет) исполнителей, которые могли быть подключены к одному брокеру Spillway. Еще одна особенность исполнителя заключалась в том, что, хотя все сети были полностью асинхронными, фактические преобразования на самом GPU отсутствовали. Они обрабатывали все, от файлов PDF из сотен страниц и файлов GIF с сотнями кадров до простых файлов изображений. Наша инфраструктура должна была самоадаптироваться к различным формам входящего трафика — без ручного вмешательства оператора. Учитывая, что мы работали в режиме реального времени, было невозможно предсказать, как будет выглядеть наш трафик в определенный момент времени.

Каждый запрос к исполнителю содержал некоторый набор метаданных о характере запроса, что позволяло исполнителю определить, в состоянии ли он обслуживать этот запрос. Учитывая разрозненные и разнородные схемы трафика, с которыми мы часто сталкивались, для исполнителей необходимой стала возможность отказаться от принятия входящих запросов (даже при полной работоспособности), если принятие соединения грозило перегрузить исполнителя. Сотрудник использовал эти статистические данные в сочетании с метаданными запроса и другими эвристическими данными, например, данными о размере буфера сокета, чтобы определить, правильно ли он принял входящий запрос. Каждый исполнитель имел собственный набор статистических данных о запросах, с которыми он на данный момент работал. Если сотрудник определял, что не может принять запрос, то создавал ответ, не отличающийся от проверки агента HAProxy, информирующей его выходной блок (Spillway) о его работоспособности.

Сначала пыталась направить запрос три раза подряд различным исполнителям (предпочтение отдавалось тем, у кого в локальных базах данных имелось исходное изображение и кто не был перегружен), и если все три исполнителя отказывались принять запрос, запрос ставился в очередь в брокере внутри памяти. Spillway отслеживала работоспособность всех исполнителей пула. Если все три очереди заполнялись, брокер просто отклонял запрос, позволяя клиенту (HAProxy) повторить попытку по истечении периода отсрочки. Брокер поддерживал три формы очередей: очередь LIFO, очередь FIFO и очередь приоритета. Существуют определенные сложности, связанные с тем, чтобы присвоить запросу приоритет и решить, в какую из трех очередей (LIFO, FIFO, очереди на основе приоритета) следует его поместить, но это уже тема для отдельной статьи. Когда запрос ставился в одну из трех очередей, любой свободный исполнитель мог убрать его оттуда и обработать.

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

image

Изображение из моей презентации о системе мониторинга Prometheus на конференции Google NYC в ноябре 2016 года

image

Предупреждение взято из моей презентации о системе мониторинга Prometheus на конференции OSCON в мае 2017 года

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

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

Три наиболее распространенных типа сбоев были связаны со следующими факторами:

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

Для каждой включенной оконечной точки добавляется облегченный буфер запросов (реализуемый на основе гоурутины и каналов), чтобы отслеживать задержки между моментом получения запроса от источника вызова и началом обработки запроса в обработчике. Мы реализовали детектор перегрузки на основе алгоритма CoDel. Каждая очередь отслеживает минимальную задержку в скользящем временном интервале, активируя условие перегрузки, если задержка превышает установленное пороговое значение.

Еще в 2013 году Google опубликовала печально известную статью «The Tail at Scale», в которой затронула ряд причин изменчивости задержки в системах с большим числом выходных линий (важной линией является очередь), а также было описано несколько удачных методов (часто с избыточными запросами) для смягчения этой изменчивости. Тем не менее, важно помнить, что если противодавление не распространяется по всей цепочке вызовов, в каком-нибудь компоненте распределенной системы возникнет определенная очередь.

image

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

image

(Множество форм ограничения скорости и методов сброса нагрузки)

Тем, кто заинтересован в получении дополнительной информации о формальном моделировании производительности на основе теории очередизации, я бы рекомендовала ознакомиться со следующими материалами:

  1. Прикладная теория производительности, Кавья Джоши, QCon London 2018.
  2. Теория очередизации на практике: Моделирование производительности для инженера-разработчика, Эбен Фримен, из LISA 2017.
  3. Отмена ограничения скорости – мощность спланирована корректно, Джон Мур, Strangeloop 2017.
  4. Предиктивная балансировка нагрузки: Несправедливо, но быстрее и надежнее, Стив Гури, Strangeloop 2017.
  5. Главы по работе с перегрузкой и устранению каскадных сбоев из книги «Техника обеспечения надежности сайта».

Заключение

Контуры управления и противодавление уже являются решенной проблемой в таких протоколах, как TCP/IP (где алгоритмы управления перегрузкой зависят от вывода нагрузки), IP ECN (расширение IP для определения границ пропускной способности) и Ethernet, учитывая эффекты таких элементов, как фреймы паузы.

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

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть