English· Español· Deutsch· Nederlands· Français· 日本語· ქართული· 繁體中文· 简体中文· Português· Русский· العربية· हिन्दी· Italiano· 한국어· Polski· Svenska· Türkçe· Українська· Tiếng Việt· Bahasa Indonesia

un

гість
1 / ?
назад до уроків

Три концепції збоїв, які варто знати

Вітаємо

Розподілені системи дають збої за певними закономірностями. Щойно ви вивчите ці закономірності, кожен постмортем перетвориться на вправу на впізнавання, а не на загадку.

Три концепції покривають більшість того, що важливо в аналізі збоїв у продуктивному середовищі:

Одинарна точка збою (SPOF): компонент, збій якого виводить з ладу більшу систему. Часто прихований: DNS-сервер, від якого залежить усіх; сертифікат, за яким оновлюється все; одинарний майстер бази даних.

Каскадний збій: збій одного компонента спричиняє збій іншого, який, у свою чергу, спричиняє збій наступного. Повільна база даних викликає таймаути на рівні API, що призводить до повторних спроб, які ще більше навантажують базу даних, що спричиняє ще більше таймаутів. Хвиля руйнування поширюється.

Радіус ураження: обсяг системи, який виходить з ладу, коли зламана одна її частина. Архітектурні рішення або обмежують, або залишають цей радіус необмеженим. У SPOF (одинаковій точці відмови) радіус ураження є необмеженим. У сервісі з механізмом bulkhead (ізоляції відмов) радіус є обмеженим.

Наприкінці цього уроку ви зможете:

- Виявляти SPOF (одинакові точки відмови) в архітектурі шляхом візуального аналізу

- Розпізнавати патерни каскадного збою: «грім стада» (thundering herd), «шторм повторних спроб» (retry storm), «черга смерті» (queue of death)

- Читати реальну хронологію подій та розділяти тригер від латентного дефекту, який цей тригер виявив

- Формулювати дії без призначення винних (blameless action items), які спрямовані на системи, а не на людей, охоплюючи превенцію / виявлення / відновлення

- Розглядати bulkhead (ізоляцію відмов) та circuit breaker (автоматичний вимикач) як інструменти обмеження радіусу ураження

Знайдіть єдину точку відмови

Інспекція шарової архітектури

Розглянемо невелику веб-архітектуру:

- DNS: api.example.com -> один IP-адреса іменного сервера 203.0.113.10, хостинг у одного провайдера DNS

- CDN: один вендор CDN перед api.example.com

- Ingress: два зворотні проксі-сервери за балансером навантаження

- Backend: шість реплік API у двох зонах доступності (по три на зону)

- База даних: один основний сервер + одна репліка для читання, у тій самій зоні доступності

- Кеш: кластер Redis, три вузли, розташовані в одних і тих самих двох зонах доступності

Питання: які компоненти є SPOF? Підказка: SPOF — це не завжди очевидний тип «єдина машина». Кластер із трьох машин, усіх у одній зоні доступності, є SPOF для збою цієї зони.

Визначте принаймні три SPOF у цій архітектурі. Для кожного з них вкажіть, що виходить з ладу, коли він збивається, та запропонуйте конкретну зміну, яка усунула б SPOF (без переписування застосунку).

Три класичні моделі каскадного збою

Збої поширюються через залежності

Модель 1: Гуркіт натовпу (Thundering herd). Спільний ресурс (кеш, замок, база даних) збивається з ладу або перезапускається. Клієнт, який залежав від нього, одночасно повторює запити. Цей потік перевантажує те, що виходить із збою; повторні запити накопичуються швидше, ніж система встигає їх обробити під час відновлення; відновлення ніколи не завершується.

Модель 2: Шторм повторних запитів (Retry storm). Нижчестоящий сервіс сповільнюється. Замість того, щоб помилитися, вищестоящі виклики повторюють запити. Повторні запити множать початкове навантаження. Повільний сервіс сповільнюється ще більше, що викликає ще більше повторних запитів. Зрештою, навантаження перевищує можливості навіть здорової версії сервісу.

Модель 3: Черга смерті (Queue of death). Черга обробки без механізму зворотного тиску (backpressure) отримує дані швидше, ніж встигає їх обробити. Черга зростає без меж. Пам'ять вичерпується; споживач (consumer) збивається з ладу; перезапускається; виявляє ще більшу чергу; збивається з ладу знову.

Спільна риса: невелике початкове збурення запускає цикл позитивного зворотного зв'язку. Сама реакція системи підсилює збій, замість того щоб його гасити.

Механізми гасіння

Експоненційне затримання з випадковим зсувом (jitter). Клієнти, які повторюють спроби, чекають дедалі довше, додаючи випадковий зсув. Це запобігає синхронізованим хвилям повторних спроб.

Обмежувач (Circuit breaker). Викликуюча сторона відстежує частоту збоїв у нижчорівневій системі. Після досягнення порогу викликуюча сторона припиняє виклики на період охолодження та миттєво повертає помилки для власних запитів. Це запобігає марній роботі та дає змогу нижчорівневій системі відновитися.

Ізоляція ресурсів (Bulkhead). Ізольовуйте ресурси для кожної залежності. Пул з'єднань A для бази даних, окремий пул з'єднань B для кешу. Повільна база даних не зможе виснажити всі з'єднання; виклики до кешу продовжаться.

Скидання навантаження (Load shedding). При перевантаженні відхиляйте запити на межі системи, замість того щоб приймати їх і повільно збоїти. Повернення коду 429 за 1 мс краще, ніж коду 500 за 30 секунд.

Зворотний тиск (Backpressure). Уповільнюйте продуцентів, якщо споживачі не встигають. Черги стають обмеженими; відправники блокуються; початкове джерело роботи відчуває опір.

Каскадний збій: тригер -> ампліфікація -> колапс, з механізмами демпфування

Діагностика каскадного збою

API-шар команди збоїв під час рутинного перемикання бази даних. Хронологія:

- 14:00:00 — оператор підвищує статус резервної бази даних до основної. Очікувана недоступність: ~10 секунд.

- 14:00:08 — основна база даних недоступна. Запити до API-шару починають зупинятися з помилками з'єднання з базою даних.

- 14:00:08 — API-шар виконує повторні спроби (типовий конфіг: 5 повторів, без затримки, інтервал 100 мс).

- 14:00:11 — резервна база даних підвищена до основної, починає приймати нові з'єднання.

- 14:00:11 — API-шар одночасно відкриває тисячі нових з'єднань із базою даних (кожна репліка × кожен паралельний запит × кожна повторна спроба).

- 14:00:13 — пул з'єднань нової основної бази даних виснажений; нові з'єднання відхиляються.

- 14:00:13-14:05:00 — репліки API-шару виснажують пули з'єднань, генерують винятки, зупиняються, перезапускаються, і цикл повторюється.

- 14:05:00 — оператор вручну зупиняє трафік до API-шару; база даних стабілізується.

- 14:10:00 — завершено поступове відновлення трафіку. Загальний час простою: ~10 хвилин (проти очікуваних ~10 секунд).

Визначте діючий каскадний патерн, назвіть механізми демпфування, які б могли його запобігти (принаймні два), та поясніть, чому фолбек з основного на резервне вузли (який мав бути 10-секундним збоям) замість цього спричинив 10-хвилинний простоїв.

DNS SERVFAIL: Два взаємопосилюючі дефекти

Постмортем реального інциденту

Нижче наведено дезідентифіковану версію реального інциденту. Назви вендорів змінено, IP-адреси анонімізовано; структура, хронологія та уроки є реальними.

Зведення

Сайт example.com повертав SERVFAIL від усіх публічних DNS-розв'язувачів протягом приблизно 3–4 годин. Усі інші 46 зони на тому ж основному DNS-сервері не постраждали. Коренева причина: два взаємопосилюючі дефекти.

1. Вендор A (постачальник вторинного DNS) додав новий внутрішній IP для синхронізації, якого не було в списку дозволів allow-axfr-ips основного сервера.

2. У зоні example.com існував конфлікт CNAME, що порушував RFC і мався з часів минулих років (demo.example.com мав одночасно записи CNAME та MX/TXT на тому ж етикетці), що спричинило відхилення зони Вендором A під час нового AXFR.

Хронологія (UTC)

- ~15:00 — Постачальник A додає нову IP-адресу синхронізації 198.51.100.42 до своєї інфраструктури

- 15:02 — у логах основного DNS-сервера з'являється перше повідомлення AXFR-out denied для 198.51.100.42 (оповіщення про цей сигнал не налаштовано)

- ~18:00 — досягнуто межі терміну дії SOA; Постачальник A видаляє зону example.com з кешу

- ~18:30 — зовні детектується SERVFAIL

- ~19:45 — встановлено кореневу причину

- 20:00 — 198.51.100.42 додано до allow-axfr-ips; основний сервер перезапущено

- 20:05 — надіслано NOTIFY; ініційовано AXFR; зона ВСЕЩЕ ПОВЕРТАЄ SERVFAIL (конфлікт CNAME)

- 20:07 — check-zone виявляє 1 помилку: конфлікт CNAME для demo.example.com

- 20:09 — CNAME замінено на A-запис; перевірка зони чиста (0 помилок)

- 20:10 — Надіслано NOTIFY; завершено AXFR; Постачальник A починає обслуговувати зону

- 20:11 — dig @8.8.8.8 example.com A повертає правильний IP — ВИВІРЕНО

Чому лише example.com?

Усі 47 зони мають однаковий основний DNS-сервер. Блокування IP для AXFR вплинуло на всі зони. Але лише example.com мав конфлікт CNAME, і лише example.com потребував свіжого AXFR у момент, коли було введено заборону. Інші зони вже оновилися до заборони або ще не потребували оновлення.

Латентний дефект

Конфлікт CNAME на demo.example.com існував роками. Він працював, тому що основний сервер надавав зону з його бази даних (був толерантним до порушень RFC), а Постачальник A обслуговував застарілі кешовані дані з моменту до появи порушення. Коли Постачальник A скинув кеш і потребував свіжих даних, порушення виявилося.

Тригер

Постачальник A мовчки додав новий IP-адресу синхронізації. У білому списку основної системи його не було. AXFR було відхилено. Три години пізніше (після завершення строку дії SOA) Постачальник A видалив зону. Латентний дефект проявився, коли система намагалася відновитися.

Написати бездоганні (blameless) пункти дій

Бездоганність: ціль — системи, а не люди

Бездоганний пункт дій вказує на те, що система має робити інакше, а не на те, що людина має робити інакше. «Навчити оператора» — це обвинувальний підхід. «Додати автоматичну перевірку, яка виявить це до розгортання» — це бездоганний підхід.

Гарні бездоганні пункти дій згруповуються в три виміри:

- Превенція: зробити небажане явище складнішим або неможливим

- Виявлення: помітити це раніше, якщо воно все ж станеться

- Відновлення: зменшити шкоду, коли це станеться

Кожен пункт має вказувати (1) конкретну зміну в системі, (2) відповідальну команду та (3) вимір, якому вона відповідає.

Напишіть три бездоганні (blameless) дії, що відповідають описаній вище постмортему щодо DNS-SERVFAIL. Розподіліть їх між протидією / виявленням / відновленням (по одному на кожен вимір). Кожен пункт має вказувати на конкретну зміну в системі та відповідальну команду. НЕ вказуйте на жодну людину як на причину.

Компартименти, що тонуть без корабля

Запозичено з корабельної інженерії

Кораблі мають водонепроникні переборки: вертикальні стіни, що ділять корпус на відсіки. Один відсік може затонув, не потопивши весь корабель; інший може зламатися, не вплинувши на решту.

Розподілені системи запозичують те саме слово та ту саму ідею.

Візерунок перебірки: ізолюйте ресурси для кожної залежності окремо. Сервіс, який викликає три нижчорівневі API, використовує три окремі пули з'єднань, три окремі пули потоків та три окремі бюджети повторних спроб. Повільна або зламана нижчорівнева залежність не може витратити ресурси, виділені для двох інших.

Без перебірок: одна повільна залежність виснажує спільний пул потоків; виклики до інших залежностей блокуються, чекаючи на потоки; весь сервіс стає невідповідним.

З перебірками: одна повільна залежність виснажує власний пул; виклики до неї швидко завершуються з помилкою; виклики до інших залежностей продовжуються нормально; радіус вибуху залишається обмеженим межами зламаної залежності.

Розривачі ланцюга

Візерунок обривника ланцюга (Circuit breaker pattern): обгортка зі станом навколо нижньорівневої залежності, яка відстежує частоту збоїв. Три стани:

- Закритий (нормальний): виклики проходять. Збої рахуються.

- Відкритий (спрацював): коли перевищується поріг збоїв (наприклад, 50% збоїв за останні 30 секунд), обривник відкривається. Виклики зупиняються миттєво без спроби звернутися до залежності. Це економить ресурси викликаючої сторони та захищає залежність від навантаження, поки вона непрацездатна.

- Напіввідкритий (тестування): після періоду охолодження обривник пропускає невелику частку викликів. Якщо вони успішні, він закривається і повертається до нормального стану. Якщо вони зупиняються, він знову відкривається для наступного періоду охолодження.

Ключовий інсайт: обривник ланцюга запобігає марним зусиллям у періоди, коли відомо про непрацездатність, та дає нижньорівневій системі шанс на відновлення без постійного навантаження.

Бастіони обмежують радіус вибуху. Обривники ланцюга запобігають самозбереженню вибуху.

Обмежте радіус вибуху

Ваш API-сервіс викликає чотири нижньорівневі сервіси: Сервіс користувачів, Сервіс рекомендацій, Сервіс сповіщень та сторонній API оплат. Команда чула, що «Сервіс рекомендацій трохи нестабільний», і хоче переконатися, що коли він зупиниться, решта системи залишиться здоровою.

Сьогодні сервіс використовує єдиний спільний пул із 200 потоків та єдиний спільний пул HTTP-з'єднань. Усі чотири нижні рівні (downstreams) змагаються за ці ресурси. Обмежувачів (circuit breakers) немає.

Запропонуйте проєкт із використанням «перегородок» (bulkhead) та обмежувачів (circuit breakers) для цього API-сервісу. Будьте конкретні: як ви розділите пули потоків/з'єднань між чотирма залежностями, які пороги для обмежувачів є доречними для нестабільного сервісу рекомендацій (Recommendation Service) та що має робити API, орієнтований на користувача, коли сервіс рекомендацій перебуває у стані «відкритого кола» (open-circuited)?

Розробіть огляд режимів відмов

Синтез

Ви навчилися виявляти SPOF (одноточкові відмови) візуально, розпізнавати патерни каскадних відмов, розрізняти тригер та латентний дефект при читанні постмортему, писати безвинні дії з запобігання / виявлення / відновлення та обмежувати радіус вибуху за допомогою bulkheads (розділення ресурсів) + circuit breakers (розривачів ланцюгів) + graceful degradation (м'якого погіршення).

Застосуйте всі п'ять.

Ваша команда запускає новий сервіс search.example.com, який залежить від трьох нижчележачих сервісів: основного пошукового індексу (index.example.com), аналітичного сервісу (analytics.example.com) та сервісу рекомендацій (recs.example.com). Команда просить вас провести «огляд режимів відмов» перед запуском.

Намалюйте план огляду режимів відмов, який ви б провели. Включіть: як ви виявите SPOF (один метод), як запобіжете каскадним відовам між пошуковим сервісом та його трьома нижчележачими (два патерни), одну конкретну дію для сервісу рекомендацій (який команда позначила як найменш надійний) та який моніторинг має бути налаштовано на момент запуску.

Куди веде цей курс далі

Куди веде цей курс далі

Тепер ви можете виявляти SPOF, розпізнавати каскади, продуктивно читати постмортеми, писати безвинні пункти дій та обмежувати радіус вибуху за допомогою проєктування.

Останній урок цього курсу (cs_distsys_observability_and_capacity) пояснює, що вимірювати, щоб дізнатися про проблему до того, як про неї дізнаються користувачі. Перевірки стану, версійні ендпоінти, чотири золоті сигнали на рівні проксі та як рішення щодо резервних потужностей пов'язані з отриманими даними.

Супутній урок: geometry_of_failure_modes_and_blast_radius виводить центральність проміжності (який вузол графа є вузьким місцем) та мінімальний розріз (обмеження радіусу вибуху).

Молодці. Далі.