Поиск неисправностей

Метод, инструменты и разбор инцидентов распределённой системы

Вячеслав Федоров

Лекция 20

Разработка и применение ПО

в физических исследованиях

500

Содержание

От симптома к причине и к уроку для системы

01

Отказы

02

Метод

03

Инструменты

04

Инцидент

05

Постмортем

06

Колесо неудачи

Цель лекции

Действовать при отказе по методу: восстановить работу, найти причину проверяемыми гипотезами и извлечь урок

Распределённая система отказывает частично, а часы её машин расходятся. Метод «гипотеза — проверка — вывод», вопросы к работающему процессу без перезапуска, явные роли участников и безобвинительный разбор позволяют сократить простой и не допустить повторения отказа.

Пять вопросов

при отказе

ВопросЧем отвечаютЧасть
Что видит пользователь?оповещение по симптому, сообщение пользователя1, 6
Где отказ?графики по зонам и экземплярам, запрос к каждому уровню2
С какого момента?хронология по журналам, трассам и графикам1
Что менялось последним?выкатка, конфигурация, нагрузка; git bisect2
Кто нужен?командир, исполнители, коммуникации4

Пояснение

Ответы записываются в документ инцидента по мере получения: они нужны и сменщику, и разбору.

Для физика

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

Отказы

Частичные отказы, расхождение часов и заблуждения о сети

01

Отказы

Метод

Инструменты

Инцидент

Постмортем

Колесо неудачи

Частичные отказы

и разные часы

01

Много экземпляров

отказывают по отдельности, не все сразу

02

Нет общего состояния

картину собирают из журналов и метрик разных машин

03

Часы расходятся

NTP: десятки мс через интернет, доли мс в локальной сети

04

Порядок событий

по идентификатору запроса и связям спанов

метка в журнале = время + смещение часов
 
gw        смещение   0.000 с
archive   смещение  -1.500 с   # отстают
db        смещение  +0.012 с   # спешат

Важно

Записи разных машин, разделённые миллисекундами, по меткам времени не упорядочить.

Для физика

Синхронизация ускорителя, например White Rabbit, даёт доли наносекунды; серверы архива обычно синхронизированы по NTP.

Демонстрация:

хронология

01

Запрос rid=7f3a: шлюз, архив, база — шесть событий

02

Часы архива отстают на 1,5 с, базы спешат на 12 мс

03

Сортировка по меткам журналов и с поправкой

04

Порядок по связям спанов, без часов

Что наблюдается

По меткам архив обращается к базе за 1,496 с до запроса, а ответ 500 оказывается раньше ошибки базы; с поправкой и по спанам — gw → archive → db → db → archive → gw.

root@lab — запись со стенда

Заблуждения

о сети

ЗаблуждениеСлед в коде или в эксплуатации
сеть надёжназапрос без тайм-аута, исчерпанный пул соединений
задержка нулеваясотни последовательных запросов вместо одного
полоса бесконечнапередача файла целиком ради его метаданных
сеть безопаснаслужба без аутентификации «за межсетевым экраном»
топология неизменнаадрес службы в программе без учёта TTL записи DNS
администратор одинработы на сети сужают полосу без предупреждения
передача бесплатнатрафик между зонами оплачивается, сериализация занимает процессор
сеть однороднаразные версии протоколов и оборудования на пути

Первые семь сформулировал П. Дойч (Sun Microsystems, 1994), восьмое добавил Дж. Гослинг (около 1997)

Демонстрация:

пул без тайм-аута

01

Архив на 127.0.0.1:8501: ответ за 50 мс

02

Запуск 13: ответ через 3 с — модель зависшей базы

03

Пул из 4 потоков: 12 запросов, 4 из них к запуску 13

04

Без тайм-аута и с тайм-аутом 0,5 с

Что наблюдается

Без тайм-аута за первую секунду завершено 4 из 12 запросов, все — за 3,2 с; с тайм-аутом 0,5 с — 12 из 12: 8 успешно и 4 с ошибкой, всё за 0,7 с.

root@lab — запись со стенда

Метод

Знание системы, гипотеза и проверка, ловушки рассуждения, последнее изменение, деление пополам

02

Отказы

Метод

Инструменты

Инцидент

Постмортем

Колесо неудачи

Знание

системы

пользователи

L4

соединения TCP

L7

запросы HTTP

зона A

служба ×3

зона B

служба ×3

зона C

служба ×3

база данных

ведущая и две реплики

01

Схема

все части и интерфейсы между ними

02

Норма

графики «как обычно» для каждой части

03

Гипотезы отказов

что увидит пользователь при отказе реплики

04

Учения

проверка гипотез на стенде, пока отказа нет

Для физика

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

Гипотеза → проверка →

вывод

отчёт

ожидаемое и фактическое

сортировка

восстановить или искать

исследование

метрики, журналы, трассы

диагностика

гипотеза о причине

проверка

подтвердить или отвергнуть

повтор, пока гипотеза не подтверждена; затем устранение

гипотеза:   обработка медленная из-за диска
проверка:   time python3 slow.py
ожидание:   user заметно меньше real
результат:  user ≈ real — гипотеза отвергнута

01

Метод

из гипотезы — следствие, следствие — в опыт

02

До проверки

инструмент и ожидаемый результат

03

Отрицательный результат

тоже результат: поиск сужается

Ловушки

рассуждения

01

Несущественные симптомы

необычный график не обязательно связан с отказом

02

Негодная проверка

изменение, которое опасно или ничего не доказывает

03

Невероятные теории

и привязка к причине прошлого отказа

04

Ложные корреляции

совпадение во времени, общая третья причина

05

Два вмешательства

нельзя установить, какое подействовало

06

Без записей

те же гипотезы проверяются повторно

Пояснение

Те же ловушки применительно к графикам дашбордов рассмотрены в лекции 19 на слайде «Ловушки восприятия».

Последнее

изменение

01

Инерция

система работает, пока её не изменят: выпуск, конфигурация, нагрузка

02

Около 70 % отказов

вызваны изменениями в работающей системе

03

Первое действие

откат последнего изменения

04

Много изменений

деление пополам: около log₂ N проверок

git bisect start ПЛОХАЯ ХОРОШАЯ
git bisect run ./check.sh
#   код 0 — версия исправна
#   1…127, кроме 125, — неисправна
#   125 — пропустить версию
git bisect reset

Для физика

Журнал изменений установки — уставки, прошивки, версии программ — служит тем же списком кандидатов.

Демонстрация:

git bisect

01

mkrepo.sh: 32 выпуска конфигурации reco.toml

02

check_config.py: workers от 1 до 64, код 0 или 1

03

git bisect start: 1.32 неисправен, 1.1 исправен

04

git bisect run и последние 11 строк вывода

Что наблюдается

Последний выпуск отклонён: workers = 4000, код 1; осталось 15 версий, около 4 шагов; первый неисправный коммит — 641fd25 «Better», найден за 5 запусков проверки вместо 30.

root@lab — запись со стенда

Сверху вниз

и пополам

01

Части и интерфейсы

HTTP, вызов процедуры, запрос к базе, библиотека

02

Ожидаемое поведение

цель уровня обслуживания интерфейса

03

Сверху вниз или пополам

середина цепочки отбрасывает половину частей

04

По зонам и экземплярам

разбивка графиков сужает поиск быстрее всего

curl -sS -o /dev/null \
     -w '%{http_code} %{time_total}\n' URL
 
200 0.004133   уровень работает
404 0.000965   служба работает, объекта нет
000 0.000224   соединения нет: искать ниже

Важно

ping показывает лишь ответ узла на ICMP: балансировщик, отдающий 500, на ping отвечает исправно.

Инструменты

Профиль, стеки потоков, снимки памяти, ожидание в ядре и журнал службы

03

Отказы

Метод

Инструменты

Инцидент

Постмортем

Колесо неудачи

Вопросы

к процессу

СимптомГипотезаИнструмент
медленная работа при занятом процессореалгоритм, лишняя работаtime, cProfile, py-spy, perf
зависание при свободном процессореблокировка, ожидание сети или дискаfaulthandler, py-spy dump, /proc/PID/wchan, strace
рост памятиудержание объектов ссылкамиtracemalloc, VmRSS в /proc/PID/status
отказ при запускеконфигурация, окружение, правакод возврата, systemctl, journalctl

Пояснение

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

Для физика

Расчёт, зависший на третьи сутки, исследуют теми же средствами: стеки, /proc, strace.

Демонстрация:

медленный скрипт

01

Гипотеза 1, медленный диск: time

02

Гипотеза 2, алгоритм: cProfile по tottime

03

Исправление: множество вместо списка

Что наблюдается

user 0,903 с при real 0,916 с — программа вычисляет, а не ждёт; профиль: 0,868 с из 0,891 — в dedup_slow; с множеством — 0,006 с.

root@lab — запись со стенда

py-spy и perf:

профиль снаружи

py-spy top --pid PID          # функции, как в top
py-spy dump --pid PID         # стеки всех потоков
py-spy record -o p.svg --pid PID
 
python3 -m sysconfig | grep PERF_TRAMPOLINE
python3 -X perf app.py        # или PYTHONPERFSUPPORT=1
perf record -g -p PID
perf report --stdio

01

py-spy

стеки из памяти процесса, без перезапуска и правки кода

02

Права

root или разрешение ptrace, в контейнере — SYS_PTRACE

03

perf

функции Python — с версии 3.12 при -X perf

04

Python 3.15

встроенный profiling.sampling, PEP 799

Важно

Без поддержки в интерпретаторе perf видит только функции на C, но не функции Python.

Демонстрация:

py-spy снаружи

01

py-spy dump: стек slow.py через 0,3 с после запуска

02

py-spy record: 200 выборок в секунду, flame graph

03

perf record: python3 с ключом -X perf

04

perf report: строки py:: с функциями Python

Что наблюдается

dump: dedup_slow, строка 25 — проверка e not in seen; record: 171 выборка без ошибок, файл slow.svg; perf: py::dedup_slow и py::<module>.

root@lab — запись со стенда

Демонстрация:

взаимная блокировка

01

deadlock.py в фоне, через 1 с — сигнал USR1

02

Стеки faulthandler в файле stacks.txt

03

py-spy dump: имена потоков и их состояние

04

Исправление: единый порядок захвата

Что наблюдается

archiver ждёт в строке 30, calibrator — в строке 39, главный поток — в join, строка 48; все три idle. С единым порядком — «обе задачи завершены».

root@lab — запись со стенда

Демонстрация:

утечка памяти

01

Служба графиков: кеш-словарь без ограничения

02

tracemalloc: снимок, 8000 запросов, второй снимок

03

compare_to по стекам глубиной 3

04

Исправление: lru_cache(maxsize=1024)

Что наблюдается

6,9 → 10,3 → 13,7 → 17,0 МиБ; прирост +12,6 МиБ в 412 100 блоках: render, строка 23 ← кеш, строка 29. С lru_cache — 1,9 МиБ во всех раундах.

root@lab — запись со стенда

Ожидание:

/proc и strace

grep State /proc/PID/status  # S — событие, D — ввод-вывод
cat /proc/PID/wchan; echo    # функция ядра, где спит
cat /proc/PID/syscall        # номер вызова, аргументы
ls -l /proc/PID/fd           # файлы и сокеты
ss -tnp                      # соединения TCP процессов
strace -p PID                # текущий системный вызов
py-spy dump --pid PID        # строка Python

01

S и D

прерываемое ожидание события и непрерываемое — ввода-вывода

02

load average

в Linux учитывает и процессы в состоянии D

03

Лекции 2 и 3

strace, /proc, состояния процесса, состояние D

Пояснение

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

Демонстрация:

ожидание recv

01

blocked.py в фоне: запрос отправлен, ответа нет

02

/proc: состояние, wchan, системный вызов, сокеты

03

ss -tnp: соединения с портом 8502

04

strace -p на 2 с и py-spy dump

Что наблюдается

S (sleeping), wait_woken; вызов 207 — recvfrom на дескрипторе 5, клиентском сокете к 8502; strace: recvfrom(5, без завершения; py-spy: строка 32.

root@lab — запись со стенда

Демонстрация:

служба не стартует

01

svc.sh archve: юнит l20-archive с опечаткой

02

systemctl show: состояние, результат, код

03

journalctl -u: три последние строки

04

svc.sh archive и запрос /runs

Что наблюдается

ActiveState=failed, Result=exit-code, ExecMainStatus=78; в журнале — «каталог не найден» и status=78/CONFIG; с верным каталогом — два запуска.

root@lab — запись со стенда

Результат:

вопросы к процессу

ДемонстрацияСимптомГипотезаИнструментВывод
slowмедленная работа при занятом процессоредиск или алгоритмtime, cProfileuser ≈ real; 0,868 с из 0,891 — dedup_slow
pyspyто же без перезапускакакая строкаpy-spy, perfстрока 25: 170 из 171 выборки
deadlockзависание при свободном процессореблокировкаfaulthandler, py-spy dumpстроки 30 и 39: разный порядок захвата
leakрост памятикеш без пределаtracemalloc+12,6 МиБ: render ← кеш, строка 29
blockedзависание при свободном процессореожидание сети/proc, ss, stracerecvfrom(5) без тайм-аута
misconfigотказ при запускеконфигурацияsystemctl, journalctlкод 78: каталог не найден

Пояснение

py-spy, /proc, ss, strace и journalctl не требуют подготовки; обработчик faulthandler и снимки tracemalloc включаются в программе заранее.

Инцидент

Первые минуты, масштаб последствий, роли участников, документ инцидента

04

Отказы

Метод

Инструменты

Инцидент

Постмортем

Колесо неудачи

Первые минуты:

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

01

Спокойные действия

поспешные решения в тревоге порождают новые ошибки

02

Сначала работа системы

причина — потом: поиск может занять часы

03

Откат

первое действие после выкатки

04

Необратимое

удаление данных — только после создания копии

горячая фаза

восстановить работу, снять последствия

холодная фаза

найти причину, исправить, разобрать

Важно

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

Масштаб

последствий

01

Кто затронут

сколько пользователей, какие функции

02

Где

все зоны или одна, все экземпляры или часть

03

Ранжирование

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

Для физика

На установке аналог — перевести участок в безопасное состояние и продолжить сеанс на остальных.

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

Роли

при инциденте

01

Командир

общая картина, решения, задачи, документ; командование передаётся явно

02

Операционная работа

изменения в системе вносит только эта группа

03

Коммуникации

регулярные сообщения участникам и заинтересованным лицам

04

Планирование

заявки, смена дежурных, учёт отклонений системы

Пояснение

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

Важно

В группе из трёх человек роли совмещаются явно: известно, кто решает и кто сообщает.

Документ

инцидента

Инцидент: архив отвечает 500 на выдачу запусков
Командир: А.  Изменения: Б.  Сообщения: В.
Начало 02:00 по графику, объявлен в 02:43
Последствия: 2 % запросов с ошибкой
Критерий завершения: ошибок < 0,1 % 30 мин подряд
Хронология:
02:43  ArchiveErrorBudgetBurnFast, принят
02:47  ошибки только в зоне B; гипотеза:
       отказ экземпляров зоны B
03:00  зона B выведена, ошибок нет
03:01  оповещение снято
Гипотезы: реплика зоны B отстала — проверяется

01

Записи

время, кто, что сделал, что увидел

02

Гипотезы

и результаты их проверки

03

Зачем

сменщик входит без вопросов, проверки не повторяются

04

Форма

документ, канал, доска: любая запись лучше никакой

Инцидент

на установке

Роль при инцидентеНа установке
командир инцидентаначальник смены
операционная работадежурные специалисты систем
коммуникациисообщения экспериментаторам
документ инцидентаэлектронный журнал смены
постмортемразбор отказа после сеанса

Для физика

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

Пояснение

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

Постмортем

Безобвинительный разбор, шаблон отчёта, измерение инцидентов

05

Отказы

Метод

Инструменты

Инцидент

Постмортем

Колесо неудачи

Безобвинительный

разбор

01

Постмортем

отчёт: последствия, действия, причины, задачи

02

Без виноватых

способствующие причины, а не человек или команда

03

Ошибка человека

следствие устройства системы: нет проверки, неудобный интерфейс

Важно

Наказание за ошибку ведёт к сокрытию подробностей, и организация теряет сведения для исправления системы.

Поводы для разбора:

01

простой или ухудшение сверх порога

02

потеря данных любого объёма

03

вмешательство дежурного: откат, переключение

04

время устранения сверх порога

05

отказ мониторинга: обнаружено вручную

Шаблон

постмортема

РазделСодержание
Описаниечто произошло, одним абзацем
Последствияпользователи, функции, потери
Корневые причиныдефекты, существовавшие до инцидента
Триггерсобытие, которое их проявило
Устранениекак восстановлена работа
Обнаружениекто и как обнаружил отказ
Задачитип, владелец, номер заявки
Урокичто хорошо, что плохо, где повезло
Хронологияпо документу инцидента

Триггер:  рост трафика в 88 раз
Причина:  утечка ресурса при неудачных
          запросах, существовавшая
          до инцидента
Задачи:   исправить утечку;
          оповещение по ресурсу;
          у каждой — владелец и заявка

Пояснение

Устранение триггера ничего не исправляет: следующий рост нагрузки приведёт к тому же отказу.

Измерение

инцидентов

01

Обнаружение

от начала отказа до первого сигнала

02

Локализация

до найденного компонента

03

Источник

оповещение или сообщение пользователя

04

Разбор каждого случая

вместо средних по кварталу

СлучайОбнаруженЗадача разбора
1. ответы 500оповещениепроверка конфигурации
2. ответы 403сообщения пользователейсквозная проверка сценария
3. поиск событийсообщение сменыоповещение об отставании

Важно

Среднее время восстановления при десятке инцидентов в квартал — шум: демонстрация incidents.py в лекции 19.

Колесо неудачи

Упражнение и три учебных случая: пул потоков, деградирующий диск, отравленное сообщение

06

Отказы

Метод

Инструменты

Инцидент

Постмортем

Колесо неудачи

Колесо неудачи:

правила

01

Ведущий

сценарий прошлого отказа, ответы на запросы

02

Основной дежурный

действует, как при настоящем отказе

03

Запасной дежурный

ищет причину вместе с основным

04

30–60 минут

проверяется порядок действий, а не ответ

дежурный: доля ответов 500 по зонам?
ведущий:  зона A — 12 %, B и C — 0,1 %
дежурный: что выкатывалось в зону A?
ведущий:  выпуск 2.4, два часа назад
дежурный: откат; ожидаю в A меньше 0,1 %
ведущий:  после отката в A — 3 %

Пояснение

Упражнение для группы: каждый готовит сценарий отказа своей установки или программы.

Случай 1:

ответы 500

Служба реконструкции событий: вычисления по запросам, поток на запрос, 16 ядер на машину, три зоны

01

Симптом

ответы 500 от балансировщика L7, оповещение

02

Где

ошибки отдают экземпляры зоны A

03

Что менялось

выпуск в зоне A: откат — ошибок меньше, но выше SLO

04

Пул потоков

заняты все 4000; зона A закрыта — ошибки в B и C

ДействиеРезультат
пул 4000 → 16 000хуже: растут load average и время ядра
пул → 8000лучше, но выше SLO
пул → 16, по числу ядердоля ошибок ниже SLO

Важно

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

Почему больше потоков —

хуже

01

Ядра те же

16 ядер делят тысячи готовых к работе потоков

02

Запрос дольше

каждый получает долю процессора, задержка растёт

03

Время ядра

планировщик, переключения, блокировки futex, промахи кешей

04

load average

в Linux — задачи в состояниях R и D, а не только очередь к процессору

работа ограничена процессором:
    потоков ≈ ядер
 
работа ждёт сеть или диск, закон Литтла:
    потоков ≈ λ · W
    λ — запросов в секунду
    W — время запроса вместе с ожиданием
    200 запросов/с · 0.05 с = 10 потоков

Пояснение

Измерение: vmstat 1 — столбцы r, cs, sy; pidstat -w; nonvoluntary_ctxt_switches в /proc/PID/status.

Демонстрация:

размер пула

01

Запрос — 16 хешей SHA-256 по 4 МиБ, около 20 мс

02

Пачка из 40 запросов на 2 ядрах стенда

03

Пулы из 2, 8 и 40 потоков

04

p50, p99 и переключения контекста

Что наблюдается

Пачка — 0,41–0,42 с при любом пуле; p50 растёт 218 → 252 → 365 мс, p99 — 415–424 мс; вынужденных переключений 146 → 506 → 537.

root@lab — запись со стенда

Результат:

задержка и размер пула

Слева задержка запроса p50 и p99 против числа потоков 2, 8 и 40, справа число вынужденных переключений контекста; запись threads.py на стенде

Что наблюдается

Пачка обработана за то же время, медиана задержки выросла с 218 до 365 мс, а вынужденных переключений стало в 3,7 раза больше: ядер два, и лишние потоки только делят их.

Уроки

случая 1

Триггер:     небольшой рост нагрузки
Причина:     workers = 4000 вместо 40
             в конфигурации двухнедельной
             давности, «Better», без ревью
Последствия: ответы 500 в зоне A,
             затем в зонах B и C
Обнаружение: оповещение по ответам 500

Пояснение

Откат выпуска в зоне A отказа не устранил: выпуск совпал с отказом по времени, но не был его причиной.

01

Проверка конфигурации

пределы перед выкаткой: check_config.py

02

Ревью

изменение одобряет не автор

03

Постепенная выкатка

канарейка: рост времени ядра виден на одной машине

04

Оповещения и графики

вызов — по доле 500, время ядра — на дашборд, всё по зонам

Случай 2:

ответы 403

Выдача доступа к данным эксперимента: короткоживущие токены в базе с репликами

01

Симптом

403 у части пользователей; выпусков нет, сеть исправна

02

Где

по зонам: 403 растут в одной, зона выведена

03

Реплика

отставание WAL растёт линейно, у соседней — около нуля

04

Диск

чтение быстрое, запись медленная; w_await 800 мс против 5–10

RAID 10 из четырёх дисков:
  две зеркальные пары
  запись — на оба диска пары,
           ждёт медленного
  чтение — с любого диска пары
 
iostat -x 1:  w_await — время записи
              с ожиданием в очереди, мс

Важно

Столбец %util для RAID-массива и SSD, обслуживающих запросы параллельно, предела не показывает.

Демонстрация:

аномалия диска

01

Сутки w_await четырёх дисков, раз в минуту

02

Правило 1: больше 20 мс пять минут подряд

03

Правило 2: больше трёх медиан соседей

04

Пересечения порога на исправных дисках

Что наблюдается

Порог срабатывает на всех дисках с 12:46 до 13:50, на исправных — 262 пересечения за сутки; правило соседей — только sdc в 15:02; в 15:00 у него 80,9 мс против 17–21 мс.

root@lab — запись со стенда

Результат:

await четырёх дисков

Время записи w_await четырёх дисков массива за сутки в логарифмической шкале: порог 20 мс и срабатывание правила соседей для sdc в 15:02

Что наблюдается

Днём все диски пересекают порог 20 мс; sdc отклоняется от соседей после 13:00, правило «три медианы» срабатывает в 15:02, к концу суток — сотни миллисекунд.

Уроки

случая 2

01

Отставание реплики

оповещение о главном симптоме этой базы

02

Соседи по массиву

аномалия относительно медианы, а не абсолютный порог

03

Сквозная проверка

вход и получение данных, как у пользователя

04

Чтение реплик

приложение не читает из отставшей реплики

Важно

SMART часто не предупреждает: у более чем 36 % отказавших дисков все показатели, кроме температуры, были нулевыми (Google, 2007).

Для физика

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

Случай 3:

отравленное сообщение

оцифровщики

100 событий/с

очередь

шарды, смещение offset

потребитель

разбор заголовка

индекс

поиск событий

запись

чтение

запись

01

Симптом

новые события не находятся в индексе

02

Где

запись в индекс — почти ноль, в очередь — идёт

03

Потребитель

занят на 100 %: аварийное завершение и перезапуск

04

Журнал

ошибка разбора поля заголовка новой прошивки

05

Мера

событие сохранить, смещение сдвинуть вручную

while True:
    event = read(offset)
    parse(event)          # ValueError
    index(event)
    offset += 1
    commit(offset)
 
# перезапуск — с последней
# зафиксированного смещения

Демонстрация:

очередь и DLQ

01

Очередь: 100 событий/с, разбор до 150 событий/с

02

20-я секунда: событие прошивки fw=3.1

03

Без защиты: перезапуск через 2 с с того же смещения

04

DLQ: событие отложено после трёх попыток

Что наблюдается

Без защиты — обработано 2000 событий до конца прогона, отставание 4000 к 60-й секунде, 20 перезапусков; с DLQ — 2 перезапуска, 1 событие в DLQ, к 40-й секунде отставание 0.

root@lab — запись со стенда

Результат:

отставание потребителя

Отставание потребителя очереди в событиях по времени от 0 до 60 с без защиты и с dead letter queue после трёх попыток; вертикаль — 20-я секунда, событие новой прошивки

Что наблюдается

Без защиты отставание растёт линейно с 20-й секунды до 4000 событий; с dead letter queue оно достигает 420 событий на 24-й секунде и обнуляется к 33-й.

Уроки

случая 3

01

Dead letter queue

после N попыток — в отдельную очередь, оповещение о её росте

02

Отставание потребителя

длина необработанной части очереди

03

Свежесть

время последней записи в индекс по шардам

04

План Б

проверяется учениями, иначе не сработает

Пояснение

Amazon SQS переносит сообщение в DLQ после заданного числа получений; в Kafka её реализует код потребителя или Kafka Connect, а share groups в Kafka 4.2 сами считают доставки.

Для физика

Событие с новым заголовком от одного оцифровщика не должно останавливать запись всех остальных.

Итоги

лекции

01

Сначала восстановить работу, затем искать причину.

02

Гипотеза — вместе с проверкой и ожидаемым результатом.

03

Хронология — по запросам и спанам, а не по часам машин.

04

Около 70 % отказов вызваны изменениями: откат и git bisect.

05

Профиль, стеки, снимки памяти, /proc — без перезапуска.

06

Явные роли, документ инцидента, разбор без виноватых.

07

Порядок действий отрабатывается на учениях.

Упражнения

и задание

01

Колесо неудачи

сценарий своей установки: ведущий, два дежурных, 30 минут

02

Три ошибки

deadlock.py, leak.py, blocked.py: гипотеза, проверка, исправление

03

Постмортем

по шаблону для хронологии timeline.py

04

Своя служба

faulthandler по SIGUSR1, проверка конфигурации, код 78

Пояснение

Исправление проверяется той же командой, что и гипотеза: после правки ожидаемый результат меняется.

Для физика

Сценарий для колеса неудачи — последний отказ своей установки из журнала смены.

Источники

1

Книга курса: главы «Поиск неисправностей», «Наблюдаемость», «Инструменты Linux», «Внутреннее устройство Linux», «Причины низкой скорости Python»

phys-dev.github.io/soft-dev-book

2

Beyer B., Jones C., Petoff J., Murphy N. R. Site Reliability Engineering. – O'Reilly, 2016: главы «Effective Troubleshooting», «Managing Incidents», «Postmortem Culture», «Accelerating SREs to On-Call and Beyond»

sre.google/sre-book

3

Beyer B. et al. The Site Reliability Workbook. – O'Reilly, 2018; Davidovič Š. Incident Metrics in SRE. – O'Reilly, 2021

4

Документация Python: faulthandler, tracemalloc, профилировщики, поддержка perf; py-spy; git bisect

docs.python.org/3/howto/perf_profiling.html

5

Pinheiro E., Weber W.-D., Barroso L. A. Failure Trends in a Large Disk Drive Population // FAST'07. – USENIX, 2007; Gregg B. Linux Load Averages: Solving the Mystery. – 2017

6

Справочные страницы iostat(1), mdadm(8), strace(1), proc(5); заблуждения о распределённых вычислениях (П. Дойч, 1994; Дж. Гослинг, около 1997)

man7.org/linux/man-pages

Спасибо за внимание

Вопросы

phys-dev.github.io/soft-dev-book

20