Задание: применение мантры системного мышления к системе трейсинга распределенных запросов

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

Необходимый контекст

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

Современные программы не пишутся в виде одного бинарника - они слишком сложны. Представим главную страницу яндекса. Там есть поисковая строка, курс валют, прогноз погоды, количество непрочитанных сообщений в почте. За каждый отдельный пункт в этом перечислении будет отвечать отдельный сервис, выполненный в виде запущенных на разных компьютерах бинарников. Они общаются между собой, чтобы собрать такую вот главную страницу Яндекса из многих источников.

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

Итак, получается что один сервер принимает входящее соединение и чтобы сформировать на него ответ, шлет запросы в другие серверы. Из этих взаимных запросов одних запущенных программ в другие получается даже не дерево, а граф. Это хорошая современная масштабируемая архитектура, имеющая ряд огромных плюсов. Но что делать, если что-то пошло не так? Если на каком-то этапе этого графа запросов возникла ошибка. Как понять, где именно? Как проследить среди миллионов приходящих запросов тот один, на который жалуется пользователь? Для ответа на этот вопрос существует система трейсинга, разработчиком которой я являюсь.

Мантра

Что надо изменить в окружении целевой системы?

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

Каким способом/методом меняем окружение целевой системы.

В мире существуют несколько стандартов трейсинга запросов. Все они оперируют примерно похожими сущностями. Две основные - спан и трейс

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

Трейс - дерево спанов, собранное по ссылкам на родительский спан

Допустим, сервер принял, обработал и ответил на запрос. Эту работу можно представить в виде одного спана. Выдадим ему какой-то номер, допустим 42


{id=42, start_time=22, end_time=32}

Ответственному за этот сервер может быть достаточно такого описания. А может он заинтересуется, почему запрос обрабатывался end_time - start_time = 10 единиц времени? На что ушло это время? Он пойдет в код и увидит, что в процессе обработки есть вызов базы данных. Он может описать этот вызов, эту единицу работы, другим спаном и указать, что причиной запуска этой единицы работы была другая единица работы - процесс обработки запроса, с id=42. Получится такое описание


{id=69, start_time=25, end_time=31, parent_span=42}

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

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

Какова роль целевой системы

Вообще в целом, роль можно назвать как “система трейсинга по стандарту OpenTelemetry”, но такая роль очень общая и не дает полезной информации. Поэтому я разобью ее на подроли.

Исходя из описанного выше, моя система должна:

  • принять запись спанов от запущенных бинарников со всех машин всех дата центров
  • хранить эти данные
  • по запросу пользователя показать ему дерево вызовов интересующего его трейса

Значит выделим три основные роли системы:

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

Из чего будем делать целевую систему

  • Ресивер данных
    OpenTelemetry - это мировой стандарт, существует несколько его opensource реализаций. В этом месте нет смысла изобретать велосипед, возьмем готовую реализацию и немного допилим ее под нужды Яндекса (добавим только общеяндексовю авторизацию и все)

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

  • Вьюер данных
    Хотя есть множество реализаций веб сервисов, отрисовывающих данные OpenTelemetry, тут мы полностью пишем своё. Тут нам важно, чтобы разработчик яндекса не пользовался миллионом разных инструментов, а имел возможность посмотреть все свои данные телеметрии в одном месте. В яндексе есть свой вьюер метрик (система сбора метрик Яндекса не похожа на опенсорсные решения. Она заточена под гигантские объёмы Яндекса и появилась раньше чем распространенные опенсорсные стандарты), свой вьюер логов. Вот в том же месте и в том же стиле мы делаем вьюер трейсов

Какими методами работы делаем целевую систему

  • Opensource реализация ресивера данных
    Тут необходимо взять опенсорс решение, дописать к нему плагины на языке GO и развернуть это в Яндесовых датацентрах

  • Хранилище данных в виде кластера Clickhouse
    Необходимо настроить и развернуть кластер базы данных Clickhouse

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

Какова роль создателя

  • Opensource реализация ресивера данных
    Для того чтобы взять опенсорс решение и развернуть его в датацентре яндеса хватит роли devops инженера. Чтобы дописать плагин - нужен программист

  • Хранилище данных в виде кластера Clickhouse
    Администратор базы данных

  • Самописный веб-вьюер данных
    Фронтенд разработчик, бэкенд разработчик

Кто/что будет создателем

  • Opensource реализация ресивера данных
    Роль девопса и программиста будет выполнять моё оргзвено - команда из четырех человек

  • Хранилище данных в виде кластера Clickhouse
    Роль Администратор базы данных будет выполнять команда администрируемых баз данных, которая есть в Яндексе

  • Самописный веб-вьюер данных
    Роль фронтенд разработчика будет выполнять команда/оргзвено “фронтенд разработка Observability platform”. Роль бэкенд разработчика будет выполнять моя команда


Новые для меня мысли

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

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

А вот агентом, играющим роль разработчик бэкенда, я всегда считал только себя, хотя по честному - это тоже оргзвено (частью которого я являюсь). Уволюсь я - роль продолжит исполняться.

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

3 лайка

Спасибо за пост!

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

Кажется типизация поплыла. Реализация ресивера данных - это вы работы описали. Это что нужно сделать. А не метод. А дальше вроде пишите, как нужно сделать. Но как будто тоже работы по методу. Вот это нужно сделать, а ещё вот это. А метод-то какой?

Я по-бытовому понимаю, что методы там программирование плагинов к конкретному решению на Go, а дальше ещё деплой в дата центры Яндекса, тем методом, который у вас принят (тоже там какой-то конкретный метод разворачивания используется).

2 лайка

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

В курсе системного мышления много раз будет сказано про сверхобобщения. Вот так незаметно они и приходят.

Конечно, если вы собираетесь на уровне мета-мета-модели поговорить о передаче и приёме данных “вообще”, то это хорошо. Но тогда надо всё одно попробовать переформулировать в терминах предметной области, в терминах мета-модели.

Забавно, тут трейсеры обсуждаются, а в соседнем треде рядом – трассеры: Задание по следованию «мантре системного мышления» из курса 2.1. Cистемное мышление

1 лайк

А ещё трейсеры - это парни, которые прыгают по крышам (занимаются паркуром)))

1 лайк

Наталья, доброго дня! Спасибо, что так внимательно читаете все мои посты :grinning:

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

Нет, “Opensource реализация ресивера данных” - это название моего аффрданса под роль “ресивера данных” из предыдущего пункта. Согласен, что неочевидное место

Грешна, до аффордансов не дошла ещё. А поясните “opensource реализация” это что? Опенсорс это решения, которые делают люди и не просят за них деньги, выкладывают публично. Вы хотите сами запилить ресивер и выложить его на гитхаб? Или более частая история, взять опенсорсное решение и напильником его под себя подправить?

Ровно это. В этом (наверное) и суть выделения ролей. Есть у меня роль “ресивер данных”. Она описывает только поведение, больше ничего. Это поведение может быть исполнено разными объектами. В моем случае я могу взять готовый код, могу написать свой (это раздел про “Из чего будем делать целевую систему”). И в зависимости от того, что я выберу на этом шаге, я буду производить рессивер разными методами - для готового решения одни методы (скачать с гитхаба и развернуть), для своей реализации другие (написать код на каком-то языке и развернуть). А для исполнения двух разных методов мне могут понадобиться два разных исполнителя

1 лайк

А как же мне не выходя из нее подбирать аффордансы? Если бы я из нее не выходил, то выделил бы из роли “система трейсинга по стандарту OpenTelemetry” подроль “коллектор спанов”. Звучит сложно, наверное нужно искать готовые реализации “коллектора”, других вариантов нет. А когда копаешь дальше и понимаешь, что “коллектор спанов” всего-лишь сервер, принимающий данные в определенном формате, то выясняется что и свой “коллектор спанов” написать не сложно (как вариант). А еще что соседняя команда делает что-то очень похожее, нужно взять их решение и доработать (как и было сделано в реальности в моем случае).

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

Это, скорее, методологическая работа – и ход на обобщение у вас при поиске, а когда вы описываете проблему, то надо так и писать “коллектор спанов”::“приёмник данных”. Фишка ровно в этом: удерживать мышление и в типах мета-модели, и в типах мета-мета-модели, искать решения прежде всего в типах мета-мета-модели, но потом таки спускать в свою предметную область. И коммуникация в проекте с сотрудниками – в терминах мета-модели, говорить будете о коллекторе спанов (хотя вы в уме, конечно, понимаете, что это приёмник данных). Если говорить будете о приёмнике данных – будет огромное непонимание. Для продвинутых можете приговаривать тип (“коллектр спанов – это приёмник данных”).

Более подробно методологическая работа будет описана в “Методологии”. А конструктивы потом, под чутким руководством ещё и архитектора.

1 лайк