Add week 6 materials

This commit is contained in:
2026-10-14 20:13:57 +03:00
parent 991a0c8b56
commit 1ee860859a
17 changed files with 490 additions and 0 deletions
+103
View File
@@ -0,0 +1,103 @@
# Семинар 6. Обнаружение отказов и group membership
На семинаре разбираем, как детекторы отказов используются в готовых системах: какие сигналы они получают, почему могут ошибаться и что происходит после обнаружения проблемы. Эти заметки дополняют [лекцию 6](../06-failures.pdf) и запись семинара. Команды для запуска примеров вынесены в README [Envoy](envoy-outlier-detection/readme.md) и [Consul](consul-demo/readme.md).
## Наблюдение и реакция
Отсутствие ответа может быть вызвано остановкой процесса, потерей связи или задержкой обработки — в том числе у самого наблюдателя. Например, перегруженный процесс может не успевать читать пришедшие ответы и начать подозревать исправных соседей.
Детектор сообщает о подозрении, а приложение решает, как на него реагировать. В примерах ниже это приводит к разным действиям: исключению бэкенда из балансировки, изменению статуса участника или обновлению состояния узла в управляющей системе.
## Envoy: ошибки при обработке запросов
В [демо Envoy](envoy-outlier-detection/readme.md) один из двух бэкендов случайно возвращает HTTP 500. Прокси учитывает ошибки и временно исключает этот бэкенд из балансировки. Это пассивная проверка: наблюдения появляются только при поступлении обычных запросов.
Прямой запрос к исключённому бэкенду по-прежнему может получить ответ. Envoy меняет маршрутизацию, а процесс продолжает работать. Возврат в балансировку по таймеру также не означает, что причина ошибок устранена.
## Consul и Serf
В [демо Consul](consul-demo/readme.md) можно сопоставить три вида сведений:
| Источник | Что показывает |
| --- | --- |
| `consul members` | Известных данному агенту участников группы и их статусы |
| Каталог сервисов | Зарегистрированные сервисы |
| Проверки здоровья | Результаты проверок экземпляров приложения |
При остановке контейнера приложения его HTTP-проверка перестаёт проходить, хотя агент Consul остаётся доступным. При этом регистрация сервиса ещё может сохраняться в каталоге. Поэтому список зарегистрированных экземпляров и список экземпляров с успешными проверками могут различаться.
Есть и ограничение самой проверки. Обработчик `/health` в этом примере просто возвращает `OK`. Он позволяет заметить недоступность HTTP-сервера, но если бы приложение работало с базой данных, потеря доступа к ней осталась бы незамеченной.
За членство самих агентов отвечает другой механизм. Consul использует **Serf**, который опирается на библиотеку **memberlist**. Её протокол основан на SWIM с расширениями, включая Lifeguard. Подробнее: [gossip в Consul](https://developer.hashicorp.com/consul/docs/concept/gossip), [протокол memberlist](https://github.com/hashicorp/memberlist#protocol).
### SWIM и его реализация в Serf
В [SWIM](https://www.cs.cornell.edu/projects/quicksilver/public_pdfs/SWIM.pdf) каждый участник проверяет других и распространяет изменения состава группы. Узел A отправляет выбранному B сообщение `ping`. Если ответ не пришёл вовремя, A просит нескольких посредников проверить B и передать ответ. Это помогает, когда прямой обмен A и B нарушен, но через других участников связь ещё работает.
После неудачных проверок B сначала подозревают. У него есть время опровергнуть подозрение, прежде чем наблюдатель пометит его как отказавшего. Такое ожидание уменьшает риск ложного срабатывания, но откладывает обнаружение настоящего отказа.
Сведения об изменениях состава группы передаются вместе с проверочными сообщениями — этот приём называется piggybacking. В сообщение добавляют ограниченное число обновлений, а не всю таблицу участников. Номера версий состояния помогают отличать новые сведения от запоздавших. Пока обновления доходят до остальных, представления участников могут различаться.
В [Serf](https://github.com/hashicorp/serf/blob/master/docs/internals/gossip.html.markdown) к этому добавлены отдельные gossip-сообщения и периодическая полная синхронизация состояния по TCP. Частоту распространения изменений можно настраивать независимо от частоты проверок, а полная синхронизация помогает восстановить согласованность списков после разделения сети. Эти обмены тоже нужно учитывать при оценке трафика.
Для знакомства с агентами есть [Quick Start Serf](https://github.com/hashicorp/serf#quick-start). Команда [`serf members`](https://github.com/hashicorp/serf/blob/master/docs/commands/members.html.markdown) показывает `alive`, `left`, `failed`: доступность, штатный выход и обнаруженную недоступность. Запись с `failed` некоторое время сохраняется в списке. Промежуточное подозрение можно увидеть в логах.
### Lifeguard: задержки у наблюдателя
Если A перегружен, он может не успевать обрабатывать ответы здорового B. Ожидание опровержения не всегда спасает от ошибки: пришедшее опровержение тоже может задержаться в обработке. Lifeguard учитывает такие ситуации с помощью трёх расширений SWIM.
| Механизм | Как работает |
| --- | --- |
| **Local Health Aware Probe** | По результатам обмена сообщениями узел оценивает признаки собственных проблем. При их накоплении увеличивает период проверок и таймаут ответа; успешные проверки постепенно уменьшают их. |
| **Local Health Aware Suspicion** | Начальный таймаут подозрения увеличен. Подтверждения того же подозрения от разных участников сокращают его; их поступление также свидетельствует, что наблюдатель успевает получать и обрабатывать сообщения. |
| **Buddy System** | При отправке `ping` подозреваемому в сообщение приоритетно добавляется подозрение о нём. Так он раньше узнаёт о необходимости опровержения. |
Lifeguard уже используется в Serf/Consul. В [статье](https://arxiv.org/html/1707.00788v2) его оценивают при задержках обработки сообщений: измеряют ложные срабатывания, время обнаружения настоящих отказов и дополнительный трафик. Увеличение ожидания может замедлить обнаружение, поэтому уменьшение числа ошибок нужно рассматривать вместе с задержками.
### Consul и балансировщик
Список здоровых экземпляров нужен, чтобы направлять к ним запросы. Балансировщик или клиентская библиотека может получать его из Consul и обновлять набор доступных адресов. Для этого требуется настроить интеграцию между компонентами. В наших стендах её нет: адреса бэкендов Envoy заданы в конфигурации.
Между проверкой и запросом экземпляр может стать недоступен. Поэтому сведения из Consul дополняют таймауты и обработку ошибок на стороне клиента или прокси.
## Примеры из документации и кода
### Cassandra: пауза самого наблюдателя
В Cassandra сведения о heartbeat распространяются через gossip, а решение `UP`/`DOWN` каждый узел принимает локально. Если процесс надолго потерял процессорное время, после возобновления работы он увидит большие интервалы без heartbeat сразу от многих соседей.
В [`FailureDetector.interpret()`](https://github.com/apache/cassandra/blob/9d3283a80ab7d8e3c2b290b222c794e8d41b2f70/src/java/org/apache/cassandra/gms/FailureDetector.java#L335-L367) перед вычислением подозрения проверяется время с предыдущего вызова. После долгой локальной паузы детектор временно воздерживается от признания соседей недоступными. [Документация Cassandra](https://cassandra.apache.org/doc/latest/cassandra/architecture/dynamo.html#ring-membership-and-failure-detection).
### Akka: какие интервалы включать в историю
φ-детектор использует историю интервалов между heartbeat. Если узел долго отсутствовал и вернулся, включение этой паузы в историю изменило бы представление детектора о нормальных задержках.
В [`heartbeat()`](https://github.com/akka/akka/blob/164b3c6615b0ac1957c4ef13b0cff6e2a43a79c0/akka-remote/src/main/scala/akka/remote/PhiAccrualFailureDetector.scala#L141-L155) φ-детектора новый интервал добавляется в историю только в том случае, если к моменту получения heartbeat узел ещё считается доступным. Так длительный перерыв не искажает статистику обычных интервалов.
Статус `unreachable` может смениться на `reachable`, когда связь восстановится. Решение об окончательном исключении участника принимает отдельный механизм. Подробнее: [φ-детектор](https://doc.akka.io/libraries/akka-core/current/typed/failure-detector.html), [Split Brain Resolver](https://doc.akka.io/libraries/akka-core/current/split-brain-resolver.html).
### Kubernetes: heartbeat через API
Kubelet сообщает о себе обновлениями статуса узла и объекта `Lease`. Здесь heartbeat имеет форму обновления ресурса в API Kubernetes. [Контроллер](https://github.com/kubernetes/kubernetes/blob/f28b4c9efbca5c5c0af716d9f2d5702667ee8a45/pkg/controller/nodelifecycle/node_lifecycle_controller.go#L919-L927) при новом `Lease` запоминает локальное время наблюдения.
Если обновления долго не поступают, условию `Ready` присваивается значение `Unknown`: у контроллера нет свежих сведений об узле. Например, связь с управляющей частью могла пропасть, хотя приложения на узле продолжают работать.
Этот механизм следит за узлом в целом. Проверки [readiness и liveness](https://kubernetes.io/docs/concepts/workloads/pods/probes/) относятся к контейнерам приложения: они позволяют отдельно оценивать готовность принимать трафик и необходимость перезапуска. [Документация heartbeat узлов](https://kubernetes.io/docs/concepts/architecture/nodes/#heartbeats).
## Цена обнаружения и распространения сведений
В дополнение к оценкам на слайдах 21–22 и 29 [лекции](../06-failures.pdf) посчитаем расходы для условной группы из 1000 процессов. Запись занимает 32 байта, служебная часть сообщения — 64 байта, период отправки — одну секунду.
| Во всей группе за секунду | All-to-all: своя запись каждому | Gossip: полная таблица одному соседу |
| --- | ---: | ---: |
| Сообщений | 999 000 | 1000 |
| Объём отправок | ≈95,9 МБ | ≈32,1 МБ |
МБ здесь — миллион байт; транспортные заголовки, фрагментация и повторные передачи не учтены. Сообщений стало почти в тысячу раз меньше, а объём сократился лишь втрое: обе схемы передают порядка миллиона записей. Получатель обрабатывает либо много маленьких сообщений, либо большие таблицы.
Одинаковый период не обеспечивает одинаковую задержку распространения сведений. Расходы сравнивают при сопоставимых требованиях к задержке и ложным подозрениям. Помимо сообщений и байтов, измеряют CPU и нагрузку на отдельных участников. Нужны и замеры при отказах: например, в SWIM отсутствие прямого ответа вызывает дополнительные проверки через посредников.
## Дополнительное чтение
- [Why Does the Cloud Stop Computing?](https://ucare.cs.uchicago.edu/pdf/socc16-cos.pdf) — причины реальных аварий облачных сервисов.
- [Gray Failure: The Achilles’ Heel of Cloud-Scale Systems](https://www.microsoft.com/en-us/research/wp-content/uploads/2017/06/paper-1.pdf) — случаи, когда разные наблюдатели по-разному видят работоспособность системы.