Как мы делали скоринг на микросервисной архитектуре руками не-программистов

2023 год — год противоречивых сигналов для будущего IT-отрасли и, в частности, занятости айтишников.

С одной стороны, с поголовной цифровизацией всех сфер и процессов экономики растёт спрос на разработчиков — что, безусловно, плюс для айтишников.

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

В реальности все, кто хоть немного разбираются в вопросе, понимают, что до этого далеко: во многих чувствительных областях нейросети, возможно, никогда не заменят человека — потому что с человека можно спросить за ошибки, а с нейросети взятки гладки. Реальным ответом на запрос рынка видятся сегодня low-code и no-code — то есть, технологии создания ПО с помощью визуального редактора с минимальным написанием кода или без написания кода вообще.

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

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

В сумме, получается непростой год для всех, с одной стороны, и отличный момент для запуска проекта, который совмещает идею микросервисов, позволяющую модификацию составных частей без влияния на остальные, с лёгкостью и простотой low-code — с другой.

Идея, которая приснилась

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

Во время работы на «околостраховой» банк мне приснилось обучение скоринговой модели. В то время с данными, их целостностью и структурированностью дела обстояли не очень хорошо. Не существовало специализированных отделов и специалистов, которые могли бы помочь с оптимизацией и выстраиванием процессов накопления и разработки витрин с данными для обучения. Кроме того, хотя задача и казалась интересной, моих знаний катастрофически не хватало, а единственным спасательным кругом стал термин «линейная регрессия», который и послужил отправной точкой в путешествие по миру Data Science.

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

  • Статистическим процессором R, который является мощным, но всё же ограниченным инструментом.
  • SAS — проприетарным ПО с закрытым и не универсальным кодом.

Так родилась идея построения гибкой системы, которая совместила бы в себе возможность самостоятельного написания кода с помощью различных языков и их расширений, а также удобство SAS для выстраивания процесса из блоков так, чтобы каждый реализовывал свою атомарную операцию (да, в то время я не знал о low-code). Концепцию — а, в дальнейшем, и разработку системы поддержал коллега-разработчик из другого крупного банка Игорь Золотов, ставший партнёром Кирилла и сооснователем SPLime.

2. Первый подход к снаряду — Definition of Done.

Ключевая проблема заключалась в совмещении разных языков программирования. В процессе обсуждения мы поняли, что идеальная платформа — это нечто среднее между low-code-конструктором и открытыми репозиториями на github. Данную концепцию мы назвали semi-code, как среднее арифметическое между pro-code и no-code :)

Планируя архитектуру нашего проекта, мы довольно быстро отказались от решения на распространённом фреймворке AirFlow. AirFlow — это хороший инструмент, но для нашей задачи он имел ряд принципиальных недостатков, таких как:

  • Отсутствие контроля входных/выходных параметров.
  • Невозможность нативного использования связки различных языков программирования в большом проекте.
  • Отсутствие функции версионирования пайплайнов (последовательностей).
  • Невозможность частичного переноса для дальнейшего переиспользования в других проектах.
  • Создание самого графа происходит посредством написания кода, а не составления визуальной последовательности логических действий
  • Ограниченность возможности реализации встроенными средствами (ручная сборка контейнеров под каждый таск).

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

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

<i>Реализация проекта из набора блоков.</i>
Реализация проекта из набора блоков.

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

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

Ноды и адаптеры

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

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

В результате обсуждения технических и функциональных особенностей мы остановились на следующем варианте:

  • Минимальная информация о ноде: название и описание ее функциональности.
  • Простейший редактор кода с базовой подсветкой синтаксиса и соблюдением отступов.
  • Список для выбора языка ноды.
  • Таблицы для входных и выходных данных.

Когда мы реализовали прототип интерфейса и занялись серверной частью, перед нами возник вопрос: как совместить друг с другом не связанные ноды, заставив их функционировать как единое целое? Причём нашей целью было не просто заставить работать конкретные блоки друг с другом, а сделать их взаимодействие универсальным, независимым от языка, задачи и разработчика.

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

В качестве ключевых названий для адаптеров мы определили методы encode и decode, загружающие и сохраняющие данные. Адаптер -- это класс/интерфейс, encode/decode -- его методы. По умолчанию предполагалось, что эти функции имеют входные параметры (данные и название файла — для сохранения; название файла — для загрузки). Однако указанные параметры — лишь формальность, необходимая для корректного функционирования серверной части, тогда как работу с ними бэкенд полностью берёт на себя.

Итоговый вариант страницы редактирования ноды получился следующим:

Меню создания новой ноды/адаптера.
Меню создания новой ноды/адаптера.

3. MVP и идея массовой платформы.

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

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

Вывод результатов работы проекта в режиме разработки.
Вывод результатов работы проекта в режиме разработки.

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

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

Для осуществления описанных процедур была взята уже имеющаяся система API-запросов к бэкенду, а также обновлённая концепция графов-проектов, которая приобрела вид цельных микросервисов.

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

Меню создания микросервиса.
Меню создания микросервиса.

Был также разработан нативный интерфейс на самой платформе, чтобы иметь доступ к созданному микросервису не только посредством API, но и из отдельного меню самой системы:

Ввод данных в микросервис.
Ввод данных в микросервис.
Вывод результатов работы микросервиса.
Вывод результатов работы микросервиса.

Реализовав описанные выше элементы, а также добавив поддержку нескольких языков программирования (Python, Go, JavaScript и Octave), мы получили полноценный MVP, который не терпелось испытать в боевом режиме.

4. Пожелания трудящихся — Feature Importance.

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

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

С первым пунктом всё оказалось довольно просто: настроили Nginx и запустили платформу в многопользовательском режиме. Кроме того, объединение пользователей на одном сервере ещё не означало облегчённый доступ к проектам и нодам друг друга.

Решение и в этот раз лежало на поверхности — сделать простой поиск по всей базе платформы с возможностью форкнуть (копировать) любые публичные сущности.

<i>Поиск по базе платформы.</i>
Поиск по базе платформы.

Со вторым пунктом, который мы для краткости обозначили как ProjectBlueprint, пришлось поработать намного дольше, но нам удалось добиться поставленной цели.

<i>Добавление проекта в качестве отдельного блока.</i>
Добавление проекта в качестве отдельного блока.

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

У нас также появился полноценный менеджер снапшотов проекта, который позволяет совершать следующие действия:

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

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

<i>Менеджер снапшотов проекта.</i>
Менеджер снапшотов проекта.

Каждый блок в свою очередь обзавёлся возможностью переключения состояния, которое фиксируется в автоматическом режиме при создании pruduction-микросервиса или снапшота.

<i>Свойства ноды проекта.</i>
Свойства ноды проекта.

5. R&D и планы развития.

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

  • Полноценный менеджер снапшотов для Блюпринтов (ноды и адаптеры).
  • Менеджер зависимостей — возможность добавления заголовочных файлов, а также указание библиотек для использования в конкретной ноде.
  • Развёртывание каждого из проектов в отдельном контейнере с возможностью балансировки нагрузки средствами Kubernetes.

Как и было задумано, у нас получился своего рода github для low-code-приложений, в котором любой разработчик может создать уникальный набор программных блоков. Этими блоками затем могут воспользоваться другие участники, модифицируя коды любого блюпринта или целых проектов.

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

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

Сейчас наша команда работает над подготовкой платформы к закрытому бета-тестированию. Команда открыта к идеям и комментариям Хабра для улучшения уже имеющихся функций, а также к тестированию SPL в качестве корпоративной платформы-оркестратора.

Любой желающий может присоединиться к нашему каналу в телеграм, чтобы всегда быть в курсе самых актуальных событий, связанных с платформой. А также можно написать мне напрямую: @YastrebovKS

11
Начать дискуссию