Я прохожу курс системного мышления и в разделе 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”. Роль бэкенд разработчика будет выполнять моя команда
Новые для меня мысли
До того как расписать роли создателей у меня в голове было намешано сразу все. Я всегда понимал, что роль администратора нашей базы данных выполняет некоторая команда, оргзвено. Общение с ними было формализовано через очереди поддержки, на помощь приходили разные люди. Это не стало для меня открытием.
Роль фронтендера в моем понимании всегда играли конкретные люди, хотя сейчас я понимаю, что агентом, играющим эту роль, тоже является оргзвено. Просто из этого оргзвена со мной общались только конкретные два человека и общение с ними было менее формальным.
А вот агентом, играющим роль разработчик бэкенда, я всегда считал только себя, хотя по честному - это тоже оргзвено (частью которого я являюсь). Уволюсь я - роль продолжит исполняться.
Хотя для меня совершенно пока непонятно, как разглядеть за деревьями лес - как отличить, выполняет ли роль агент::человек или агент::оргзвено. Коммуницирую то я всегда с агентом::человеком, хотя в тот момент он выступает как представитель оргзвена.