Хабрахабр

Концепция персистентной ткани для контроля IT-инфраструктуры

Расскажу вам про микросервисы, но немного с другой точки зрения, чем Вадим Мадисон в посте «Что мы знаем о микросервисах». Всем привет. При чем же тут микросервисы? Вообще я считаю себя разработчиком баз данных. И с этим как-то нужно жить. В Авито используются: Vertica, PostgreSQL, Redis, MongoDB, Tarantool, VoltDB, SQLite… Всего у нас 456+ баз для 849+ сервисов.

Этот пост — вольная расшифровка моего доклада с Highload++ 2018, видео можно посмотреть тут. В этом посте я расскажу вам про то, как мы реализовали data discovery в микросервисной архитектуре.

Вот паттерн, с которого все обычно начинают. Как должна быть построена микросервисной архитектура с точки зрения баз, все неплохо знают. На слайде оранжевые прямоугольники — это сервисы, между ними общая база. Есть общая база между сервисами.

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

Если в базе много шардов, то база должна быть общая, чтобы они синхронизировались. С точки зрения работы с базами для микросервисной архитектуры должен применяться паттерн DataBase-per-Service — у каждого сервиса своя база. Это теория, а в реальности все не так.

Есть сервисы, написанные правильно. В реальных компаниях используют не только микросервисы, но и монолит. А есть старые сервисы, которые до сих пор используют паттерн с общей базой.

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

Конечно, это доменное моделирование. С чего начинается переписывание монолита на микросервисную архитектуру на уровне планирования? Но, к примеру, мы в Авито несколько лет создавали микросервисы без доменного моделирования. Везде написано, что нужно сделать доменное моделирование. Мы имеем представление о полных потоков данных. Потом им занялся я и разработчики баз данных. Эти знания неплохо помогают спроектировать доменную модель.

На самом деле это все маркетинговый bullshit. У data discovery есть классическая интерпретация — это то, как работать с данными, разбросанными по разным хранилищам, чтобы приводить к совокупным выводам и делать какие-нибудь правильные выводы. Про это у меня были доклады несколько лет назад, останавливаться на этом не будем. Эти определения про то, как все данные с микросервисов загрузить в хранилище.

Хочу показать способ, как вы можете осознать сложность непрерывно эволюционирующей системы с точки зрения данных, с точки зрения микросервисов. Я вам расскажу про другой процесс, который ближе к процессу перехода на микросервисы. Фактически этот вопрос является основной идеей доклада. Где посмотреть цельную картину сотен сервисов, баз, команд, людей?

Ваша компания — это совокупность всего, что обеспечивает технологическую инфраструктуру. Чтобы в этой микросервисной архитектуре не умереть, вам нужен digital twin. И это не аналитическое хранилище. Вам нужно создать адекватный образ всех этих сложностей, на основе которых можно быстро решать задачи.

Ведь все начиналось с простейшего data discovery. Какие задачи мы можем поставить такому digital twin?

Вопросы:

  • В каких сервисах хранятся важные данные?
  • В каких не хранятся персональные данные?
  • У вас сотни баз. В каких персональные данные есть? А в каких нет?
  • Как важные данные ходят между сервисами?
  • Например, в сервисе не было персональных данных, а потом он начал слушать шину, и они появились. Куда данные копируются, когда стираются?
  • Кто с какими данными может работать?
  • Кто может получить доступ напрямую через сервис, кто через базу, кто через шину?
  • Кто через другой сервис может дернуть API ручку (запрос) и себе что-то скачать?

Этот граф нужно наполнить, актуализировать и поддерживать свежими данными. Ответом на эти вопросы практически всегда является граф элементов, граф связей. Вот его визуализация. Мы этот граф решили назвать Persistent Fabric (в переводе — помнящая ткань).

Посмотрим, что в этой помнящей ткани может быть.

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

UI points дергают Endpoints. Endpoints. Ручки сервисов. В русской традиции это называется ручками. Endpoints дергают сервисы.

Сервисов сотни. Сервисы. Мы понимаем, какой сервис может дернуть сервис. Сервисы связаны друг с другом. Мы понимаем, какой вызов UI points может вызвать какие сервисы по цепочке.

База как термин хранилища плохо звучит, потому что под этим термином понимается что-то аналитическое. Базы (в логическом смысле). Например, Redis, PostgreSQL, Tarantool. Сейчас мы рассматриваем базу как storage. Если сервис использует базу, то обычно использует несколько баз.

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

У базы есть развертывание на хосты. Хосты. Это даёт понимание того, к каким серверам нужно ограничивать доступ, чтобы какие-то важные данные не утекли. Одна база, один Redis на самом деле может жить на 16 машинах (мастер кольцо) и еще на 16 живут slave.

В базах хранятся сущности. Сущности. Сущности могут храниться в нескольких базах. Примеры сущностей: пользователь, объявление, платёж. Важно знать, что у этой сущности один storage является Golden Source. И тут важно не просто знать, что эта сущность там есть. Все остальные базы являются функциональными кэшами. Golden Source — это база, где сущность создается и редактируется. Если не дай бог у какой-то сущности два Golden Source, то необходимо трудоемкое согласование разделенных источников. Важный момент. Сущностям, лежащим в базе, нужно выдать доступ для сервиса, если хотим этот сервис обогатить новой функциональностью.

Команды, которые владеют сервисами. Команды. Для него трудно найти ответственного. Сервис, который не принадлежит командам — это плохой сервис.

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

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

Чтобы этой Persistent Fabric жить, он должна не просто один раз наполниться. Ключевой момент. Сущности появляются новые, добавляются в новые сервисы, удаляются. Сервисы создаются, умирают, storage выделяются, перемещаются по серверам, команды создаются, разбиваются, люди переходят в другие команды. Самое важное — не то что где-то надо это технически хранить. Endpoints создаются, регистрируется, пользовательские траектории с точки зрения GUI тоже переделываются. Чтобы он актуализировался. Самое важное — сделать так, чтобы каждый слой Persistent Fabric был свежим и актуальным.

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

Здесь я хочу проиллюстрировать, что для заполнения Persistent Fabric не нужно заполнять весь гигантский граф. Информацию про команды можно взять из организационной структуры 1С. Нужно чтобы каждый слой правильно заполнялся.

Один человек может в разных командах занимать разные роли. Информацию про людей можно взять из LDAP. Сейчас мы сделали систему Авито People и из нее берем привязку людей к командам и занимаемые роли. Это абсолютно нормально. Самое главное — чтобы такие простейшие данные шли, чтобы хотя бы сохраняли ссылки на концы связей, чтобы названия команд соответствовали командам из организационной структуры 1С.

Для сервиса нужно получить название и команду, которая им владеет. Сервисы. Это система, которую упоминал Вадим Мадисон под названием Atlas. Источник — это Service Discovery. Atlas — общий реестр сервисов.

5% сервисов в таких системах отсутствуют, т.к. Полезно понимать, что почти все такие системы типа Atlas хранят информацию про 95% сервисов. И когда вы начинаете с этой схемой работать, вы чувствуете, чего вам не хватает. сервисы старые, созданные без регистрации в Atlas.

Это может быть PostgreSQL, MongoDB, Memcache, Vertica. Storages — это обобщенные хранилища. Для NoSQL-баз используется своя «половинка» Атласа. У нас есть несколько источников Storage Discovery. Но они хотят сделать свой Storage Discovery более правильным. Для информации о PostgreSQL-базах применяется парсинг yaml.

Смотрите, все, что я описал, в принципе, довольно просто, это можно заполнить даже в Google Sheets. Итак, storages и информация о том, что сервис использует, ну или владеет (это разные типы) storage.

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

Нам нужно выдавать права на базу. Первый сценарий. Туда должен входить только этот сервис и только члены команды, которая сервисом владеет. База должна быть строго в сервисе. Довольно часто другим командам полезно сходить в базу другого сервиса. Но мы живем в реальном мире. Реально большая проблема — для сотни баз понять кто там главный. Вопрос: у кого спросить о выдаче прав? При том, что кто ее создавал, давно уволился, или перешёл на другую должность, или вообще не помнит, кто с ней работает.

Вам нужен доступ к storage. И тут простейший графовый запрос (Neo4j). Переходите в команду, которая владеет сервисом. Вы от storage идете к сервису, который владеет им. В Авито у продуктовых команд есть технический руководитель и продуктовый руководитель, который не сможет помочь по базам. Дальше для сервиса вы узнаете, кто у этой команды TechLead. Доступ к storage — это не атомарная операция. На слайде на самом деле отображена только половина запроса. Это довольно интересная отдельная задача. Чтобы получить доступ к storage, нужно получить доступ к серверам, на которых он установлен.

Это инсталляция. Для её решения мы добавляем новую сущность. Есть storage, например Redis база (redis:address). Здесь терминологическая проблема. Установку storage на хост мы называем Instance. Есть host — это может быть физическая машина, lxc-контейнер, kubernetes.

Storage для production разумно установить на отдельные физические машины для увеличения performance. У него может быть четыре установки на три хоста, как показано в примере выше. Для dev-среды вам достаточно установить на один хост и назначить Redis разные порты.

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

Второй запрос от storage идет к instance и host. Дальше идёт вторая часть запроса. На слайде — пример для production окружения. Этот запрос рассматривает все инсталляции для соответствующего окружения. Это был пример запроса по выдаче прав для сотрудника не из команды. Исходя из этого выдаются уже права на подключение к конкретным хостам, конкретным портам.

Ему нужно выдать доступ (для начала — read only) ко всем сервисам, ко всем storage этой команды. Рассмотрим пример, когда в команду нужно взять нового сотрудника. Зеленые круги это руководители команд. На слайде реальная команда с неполной выборкой. Желтые — сервисы. Розовые круги — это команды. Серые — это хосты. У ряда жёлтых сервисов есть синие storage. Это пример для небольшого юнита. Фиолетовые — это инсталляции storage на хосты. Для таких юнитов картинка будет большая. Но есть много юнитов, у которых сервисов не 7, а 27. Если использовать есть Persistent Fabric, вы можете сделать запросы в ней и получить ответы списком.

Сущности в Авито — это объявления, пользователи, платежи и так далее. Давайте продолжим наполнять нашу умную ткань и поговорим о бизнес-сущностях. На самом деле все их логировать не обязательно. Из моих публикаций (HP Vertica, проектирование хранилища данных, больших данных, Vertica+Anchor Modeling = запусти рост своей грибницы) про хранилища данных вы знаете, что в Авито этих сущностей сотни. Из аналитического хранилища. Откуда можно получить список сущностей? На первом этапе этого достаточно. Можно выгрузить информацию о том, откуда они эту сущность берут.

Там же указываем, что storage же хранит сущность как кэш или storage хранит сущность как Golden Source, то есть является ее первоисточником. Далее мы развиваем эти знания: для каждой сущности составляем список хранилищ где она есть.

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

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

Для этого вам нужно ограничить доступ к серым кругам. Соответственно, если вы хотите пройти PCI DSS, вам нужно ограничить доступ к определенным сущностям. Это статическая информация. Если нужно имеется ввиду real-time доступ, то закрываем доступ к фиолетовым кругам.

Важно иметь не просто иерархическую связь между сущностями, но и одноуровневые связи. Когда мы говорим про микросервисную архитектуру, самое важное — это то, что она меняется. Связка вида «сервис вызывает сервис». Связка сервисов — пример одноуровневых связей, которую мы неплохо прокачали и используем. Тут есть информация о прямых вызовах — сервис вызывает API другого сервиса.

Это похоже на асинхронную медленную связь, проходящую через шину. Здесь также должна быть информация о связи вида: сервис №1 отправляет в шину (очередь) события, a сервис №2 на это событие подписан. С помощью подобных связей можно проверить работу сервисов, если поменялась версия сервиса, на которые они подписаны. Такая связь тоже важна с точки зрения движения данных.


Если мы рассматриваем задачу поиска точек использования сущности, то очевидный запрос, который у нас возникает, это проверка периметра. Есть сущность и мы знаем, что она хранится в определенных storage. Куда эта сущность может утечь (быть скопирована) из периметра? Storage принадлежат некоторым сервисам. Сервис обратился, получил и сохранил у себя пользователя. Она может утечь через вызовы сервисов. Шины могут вас могут быть соединены друг с другом, используя RabbitMQ, Londiste. Она может утечь через шины. А вот вызовы уже подгрузили. На слайде Londiste мы еще не подгрузили.

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

Вадим упоминал, что для построения реестра endpoints сервисов можно использовать документацию. Endpoints. Если Endpoint важный, то сами разработчики добавят его в мониторинг. Ещё можно получить эту информацию из мониторинга. Если Endpoint не мониторится, то он нам не нужен.

Привязка метрик к storage, к сервисам, к хостам, к instance (шардам баз) и endpoint. Соответственно, из мониторинга можно получить метрики.

От endpoint переходите к сервису, переходите к сервисам, которые этот сервис вызывает, от сервисов идете к storage, от storage идете instance и хостам. Когда у вас возникает сбой, например, endpoint выдает HTTP код 500, то, чтобы отследить корень проблемы, необходимо сделать запрос по этому endpoint.

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

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

На самом деле, оказалось что подграф вызовов у почти любого микросервиса входит и выходит из монолита. Было бы здорово сейчас показать граф сущностей Авито, где изолированные подграфы микросервисов, которые можно поднять независимо, можно тестировать и выкатывать в production. Это — иллюстрация того, что если разрабатывать микросервисы и не думать о таких последствиях, то в итоге микросервисная архитектура все равно не будет работать без монолита и не позволяет проводить изолированное тестирование не позволяет.

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

Это довольно просто сделать при соблюдении нескольких правил. И напоследок — о том, как это всё поддерживать и наполнять.

  • Нельзя заставлять людей заполнять все зависимости всех сервисов. Нужно чтобы каждый источник заполнял только свой небольшой кусок информации. Слой сервисов заполняется из основного источника. Отсутствующие в этом источнике сервисы заполняются из ручных инструментов. Из второго источника заполняются storage. Из третьего — сервера. Из четвертого endpoint. Из отдельных источников заполняются ссылки, что сервис принадлежит команде, что человек стал техлидом и т.п. Информация должна заполнятся быстро и просто.
  • Информация о графе с соблюдением историчности должна загружаться в базу. Так как реальная микросервисная архитектура постоянно «дышит» и меняется. Сервисы возникают, умирают, появляются новые базы, появляется connection, старый connection отменяют, люди ходят между командами. При расследовании инцидентов полезно знать, в каком сервисе живет сущность и в каком сервисе сущность жила раньше. (Как делать граф исторических связей, можно узнать в моём посте про Anchor Modeling). Большинству людей для того чтобы работать с данными историчность не нужна. Им нужен актуальный срез «на сейчас». На основании исторического графа должна строится витрина графа на текущий момент. В Авито используется база данных Neo4j, которую можно использовать для визуализации.
  • Чем больше сотрудники используют граф, тем больше у них появляются стимул самим следить за его актуальностью. Каждый слой этого ткани может наполнить отдельная команда. Например, UI points наполняют frontend разработчики, сервисы заполняют backend разработчики, storage заполняют DBA, серверы заполняют DevOps инженеры, сущности заполняют аналитики.

По некоторым направлениям у нас продолжается работа.
Мы хотим добавить еще информацию о потоке данных через шину (Londiste, PGQ, RabbitMQ).

Граф связи UI points формируется на информации о том как пользователи ходят между ними. Еще мы пытаемся добавить графы пользовательских траекторий. В данное время переходим к объединению этой информации и пробросов в ткань (Persistent Fabric), чтобы можно было пользовательский опыт оттранслировать в UI points, оттуда в Endpoint, оттуда в сервисы. Сейчас это сделано на уровне клиентского логирования. Из этой информации понять чем у нас пользователи чаще всего пользуются и узнать, например, почему небольшой сервис, не очень нагруженный и не очень важный, так аффектит пользовательский опыт.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть