Всем привет! Меня зовут Александр Пряхин, я технический руководитель в Авито Подработке. В свободное от руководства в Авито время я преподаю и руковожу курсами, консультирую людей и компании в области IT. За 17 лет опыта в IT я успел поработать в самых разных командах – и там, где наблюдаемость была выстроена от и до, и там, где о ней никто даже не слышал.

Когда мы говорим о стабильности систем, чаще всего вспоминаются метрики и мониторинг: что упало, где горит, сколько минут сервис был недоступен. Но достаточно ли этого, чтобы сказать, что система работает хорошо? На практике – нет. Даже идеально «обложенные» алертами сервисы продолжают сбоить, а тимлиды и разработчики всё равно сталкиваются с жалобами пользователей.

В этой статье я хотел бы рассмотреть подходы к измерению качества стабильной работы системы в Авито, а не только её отказов. Я постараюсь описать, что такое бюджет качества и почему его стоит считать; как SLA, SLO и SLI помогают наводить порядок и принимать решения; и главное – почему техническое качество напрямую становится зоной ответственности тимлида и как через метрики команда может начать говорить с бизнесом на одном языке.

Узнать больше про то, как управлять процессами, продуктами и командами, вы сможете на конференции для лидов и менеджеров 17 октября. Avito.tech.conf – это уникальный случай, когда на одной площадке соберутся сильные менеджеры, чтобы обсудить самые актуальные темы. Хотите присоединиться? Регистрация и все подробности – по ссылке.

Оглавление

Низковисящие фрукты

В начале своей карьеры (и инженерной, и менеджерской) я нередко приходил в команды, где технических метрик и мониторинга просто не было. Вроде что-то работает, но наблюдаемости нет. Как ни странно, но до Авито с такими системами работать было легче за счёт эффекта низкой базы. Ты приходишь, ставишь Zabbix – и уже молодец, ведь ты организовал наблюдаемость. Теперь команда хотя бы понимает, где и что отваливается, где начинает «гореть» или «взрываться».

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

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

Рано или поздно всё вокруг перестает гореть и даже тлеть. Можно спокойно поужинать за столом в восемь вечера, а не в полночь, когда вспомнил, что было бы неплохо поесть. И ты, как Сэм из Властелина колец, думаешь: «Так далеко я еще не заходил». Но что дальше делать? Получается, оно перестало гореть, и я молодец? Все хорошо, все работает. Я выиграл и можно больше ничего не делать?

А вот и нет.

Тут еще больше контента

Уходим глубже

Наступает следующий этап.

Пример с одной из моих давних прошлых работ. Пятница, всё стабильно, но вдруг раздается звонок от руководителя E-Com направления. Он говорит: «Слушай, у нас пользователи жалуются – у них что-то притормаживает». Смотришь по метрикам – все нормально: ресурсов полно, ничего не взрывается и не горит. Почему тогда тормозит и жалуются пользователи? Начинаете спорить, разбираться и только к 23:00 находите: корзина действительно лагает, там что-то долго работает. То есть часть сервиса может не жрать много ресурсов, но тормозить из-за долгой обработки. 

Становится понятно: надо идти глубже. Мониторинга уже недостаточно. Начинаем прикручивать профилирование; а если уж совсем красавчики, то APM (Application Performance Monitoring). Это системы мониторинга производительности, которых тоже достаточно много (от платных типа New Relic или Elastic APN до self-service типа Pinba и встроенных профайлеров). Все они интегрируются в среду исполнения и позволяют отслеживать, как отрабатывают скрипты, где собирают слишком много данных, и т.д.

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

Таким образом, если нам снова звонит E-Com и говорит: «Тормозит», то мы уже идем в профайлер, который подсказывает, куда смотреть и как быстро это надо починить. 

И вот мы посмотрели на железо, профилировщик – теперь-то все хорошо? Спустя какое-то время мы снова потушим все пожары (скорее, даже возгорания и задымления). Но встаёт новый вопрос: а теперь-то система стабильна? Она работает хорошо? И что такое «хорошо»?

Что такое «хорошо»?

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

Немного теории из SRE

То, что ошибки случаются, мы зафиксировали. В стабильной системе с ростом объема пользователей процент ошибок примерно остается таким же, но объем в числах будет подрастать. Тут появляется понятие SLA (Service Level Agreement – соглашение об уровне предоставляемого сервиса). 

В SRE методологии есть:

  • SLA – это соглашение с пользователем;

  • SLO (Service Level Objective) – наша внутренняя цель, обычно строже SLA, чтобы был запас;

  • SLI (Service Level Indicator) – показатель, насколько мы «хорошо» работаем и выполняем ли цели.

SLA измеряется в процентах и может принимать разные значения: 99%, 99,5%, 99,9%. Но что скрывается за девятками? SLA в 99,9% (три девятки) означает, что за год допускается 8 часов 46 минут и 12 секунд простоя. На уровне года уже начинаешь задумываться. Правильный джун в неправильном месте может «сжечь» этот бюджет за день. Если израсходовать весь этот бюджет в январе, то потом будет туго: либо без релизов жить, либо надо что-то менять (или SLA, или настройки). 

Регистрация тут

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

Надежность системы – результат отстроенного процесса

Тот же SLA – это не разовая история. Нельзя один раз выполнить цель и забыть, переключившись дальше, например, изучать новые фреймворки. Каждый релиз, каждый день мы должны смотреть, что происходит. Это системная работа. Ведь каждый день у нас катятся релизы (и не по одному), что также влияет на стабильность, – и мы должны пристально наблюдать именно с точки зрения «хорошо». 

На данном этапе мы вводим понятие «бюджета ошибок» – допустимый объём отказов, простоев, при котором система всё ещё считается стабильной. 

По правую сторону границы: боль. Если бюджет исчерпан – всё начинает «гореть», бизнес и пользователи недовольны. Но можно смотреть на бюджет и с другой стороны: как на возможность. 

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

Устраняем пороки через качество

Чтобы команда и бизнес говорили на одном языке, важно убрать «стену» между ними. И здесь можно вспомнить концепцию из книги «Пять пороков команды» Патрика Ленсиони. Из книги мы узнаем о том, что у любой команды может быть разный набор «пороков» (в оригинале dysfunction). Они выстраиваются от фундамента наверх в такой последовательности:

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

  • уход от конфликтов – люди не хотят спорить и, соответственно, хуже находят новые решения и обмениваются мнениями;

  • необязательность – вырастает из ухода от конфликтов. Команда не сделала что-то: и ладно; 

  • нетребовательность – проявляется в меньшей вовлеченности в состояние сервиса. Что-то упало, и ладно. Проснусь, выпью кофейку и починю как-нибудь;

  • безразличие к результату – команда не разделяет с бизнесом ценность, приносимую продуктом. 

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

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

Жми сюда!

Методика измерений

Теперь от теории и рассуждений перейдем к практическому применению.

Мы уже поговорили как измерять «в лоб». Недоступность системы измерить просто: фиксируем начало и конец сбоя, вычисляем время простоя. Zabbix или любая система мониторинга справится. Но мы помним, что нужно ответить на вопрос: «Что такое хорошо?».

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

Для того, чтобы ответить на вопрос «Что такое хорошо?», нужны дополнительные критерии. Например, такой список вопросов: 

  • за какое время пользователи получают ответ в ожидаемое время? 

  • при какой нагрузке ожидаемое время будет оставаться ожидаемым? 

  • какой при этом уровень доступности должен сохраняться?

Посмотрим на примере.

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

  1. Ручка 1 используется часто и должна быстро отдавать ответ. 

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

  3. Ручка 3 не требует моментального отклика. 

Вспомним вопросы-критерии по ожиданиям.

Например, мы ожидаем, что ручка должна отвечать за 10 мс в 99,9% случаев при 1000 RPM (requests per minute - количество запросов в минуту). Теперь пытаемся наложить эти критерии на ручки и расписываем ожидания. 

Теперь нужно понять: а что делать дальше с этими критериями? Разберем на примере одной из ручек. 

За час при RPM = 1000 мы получаем 60000 ожидаемых запросов. При надежности 99,9% за час только 60 запросов могут разметиться как «плохие» (например, они выдают 500-е ошибки, выходят за latency и т.д). Наша задача их помечать и отслеживать. Это несложно: ошибки, задержки, превышение latency – всё можно ловить и писать в хранилище, визуализировать в Grafana.

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

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

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

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

Как это настроить?

Сначала лимиты и ожидания выставляем предположительно, потом корректируем по факту работы на продакшне. Самое главное в начале использования методики – хорошее покрытие. Именно покрытие позволит точно отследить происходящее. Мы считаем нормальным, что минимум у 80% ручек есть покрытие данными метриками. 

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

Со временем можно усложнить расчёт: учитывать, насколько сильно запрос превысил лимит – на 1 мс или на 99 мс. Разные просадки будут по-разному съедать бюджет. 

Главное – начать с хорошего покрытия, беспристрастного ревью и отслеживания. Дальше, конечно, можно накручивать и нелинейные метрики.

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

Так что же такое «хорошо»?

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

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

  • почему так произошло?

  • почему мы допустили просадку?

  • что мы сделали не так? 

  • как сделать лучше в будущем?

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

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

Как мы работаем с этим? 

Для каждой ручки мы стремимся расписывать требования. У нас есть отслеживание – дашборд, который показывает, где у нас, например, недостаточное покрытие. Когда я рассказывал доклад на TeamLead Conf, в зале был мой коллега, с которым мы вместе запускали новый продукт и работали над тем, чтобы набрать необходимое покрытие, ведь на старте на новом сервисе этого «из коробки» не будет. Будут какие-то решения по умолчанию, но они не всегда релевантны: они могут не соответствовать реальным ожиданиям, значит, это нужно настроить. 

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

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

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

Кликни здесь и узнаешь

Итоги

Что важно помнить:

  • базовые метрики помогают тушить пожары, но не помогают растить качество;

  • SLA, SLO, SLI и бюджет ошибок помогают смотреть на систему с позиции бизнеса;

  • бюджет ошибок – это не только ограничения, но и пространство для экспериментов;

  • тимлид вместе с командой отвечает за метрики и может принимать более рискованные решения;

  • начать просто: важно соблюдать хорошее покрытие метриками и беспристрастное ревью.

При этом методология, о которой я рассказал, не rocket science: вы уже можете начать её применять и экспериментировать. Методология расчета у вас уже есть, а инструменты я сознательно не навязывал – можно использовать все, что угодно. Так вы даёте команде возможность ориентироваться через качество на бизнес-результаты.

А как в ваших командах считается качество? Используете ли SLA и SLO? Делитесь опытом в комментариях!

И обязательно приходите на Avito.tech.conf, чтобы узнать о лучших практиках управления процессами, продуктами и командами. Регистрация и программа вот здесь.

Комментарии (0)