
# Работа с фреймворком, обзор основных компонентов

- [Работа с фреймворком, обзор основных компонентов](#работа-с-фреймворком-обзор-основных-компонентов)
  - [Модули](#модули)
  - [Структура базового модуля](#структура-базового-модуля)
  - [Репозиторий](#репозиторий)
  - [Образ Docker](#образ-docker)
  - [DataBox](#databox)
  - [MLComponent](#mlcomponent)
  - [ExperimentPipeline](#experimentpipeline)
  - [APIComponent](#apicomponent)
  - [Общие элементы компонентов](#общие-элементы-компонентов)


## Модули

Фреймворк работает с тремя типами модулей:

1. Базовый модуль - приложение, которое решает прикладную задачу.
2. MLOps-модуль - библиотека или набор подпрограмм, которые модифицируют работу базового модуля.
3. Модуль данных - набор данных, оформленный в соответствии с интерфейсом, который поддерживается фреймворком.

Основной тип модулей, которые развёртываются на фреймворке - базовые.


## Структура базового модуля

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

- Git-репозиторий
  - Программный код, решающий прикладную задачу
    - Интерфейс взаимодействия с фреймворком
  - Компоненты модуля
    - MLComponent
    - ExperimentPipeline
    - APIComponent
    - DataBox
- Системные компоненты, подключающие модуль к фреймворку
  - Secret
  - Repository
  - PlatformApp
- Образы Docker


Связь компонентов модуля:

```mermaid
flowchart LR
    %% External APIs
    MLAPI[API ML-компонента]
    PipeAPI-General[Общий API пайплайнов]
    PipeAPI1[API первого пайплайна]
    PipeAPI2[API второго пайплайна]
    FileAPI[Файловый API]


    %% S3 Infrastructure
    S3Storage[(S3 Хранилище)]
    FileAPI <---> S3Box-UserData[S3 Box: Данные пользователей]
    FileAPI <---> S3Box-ModelWeights[S3 Box: Веса моделей]
    S3Box-UserData --> S3Storage

    %% ML Component Section
    subgraph ML-Component["ML Component"]
        direction LR
        MLService[ML Service]
    end
    
    MLAPI <--> ML-Component
    ML-Component --> S3Box-UserData
    ML-Component --> S3Box-ModelWeights
    S3Box-ModelWeights --> S3Storage

    %% Pipeline Sections
    subgraph Pipeline1["Pipeline 1"]
        direction LR
        Pipeline1Service1[Pipeline 1 запуск 1]
        Pipeline1Service2[Pipeline 1 запуск 2]
    end
    subgraph Pipeline2["Pipeline 2"]
        direction LR
        Pipeline2Service1[Pipeline 2 запуск 1]
    end

    PipeAPI1 --> Pipeline1
    PipeAPI2 --> Pipeline2
    PipeAPI-General --> Pipeline1
    PipeAPI-General --> Pipeline2

    Pipeline1 --> S3Box-UserData
    Pipeline2 --> S3Box-UserData

```



## Репозиторий

Репозиторий развернутого модуля содержит:

- Исходный код проекта
- Зависимости проекта, по которым можно воссоздать виртуальную среду для работы с ним
- Dockerfile
- Папку с манифестами (файлами YAML) компонентов модуля

Пример репозитория:

```
/mymodule
├── app
│   ├── api-files.yaml
│   ├── api-mlcmp-foo.yaml
│   ├── api-pipeline-bar.yaml
│   ├── api-pipeline-foo.yaml
│   ├── api-pipelines.yaml
│   ├── mlcmp-foo.yaml
│   ├── pipeline-bar.yaml
│   ├── pipeline-foo.yaml
│   └── s3-box.yaml
├── myapp
│   ├── __init__.py
│   ├── inference.py
│   ├── config.py
│   ├── settings.py
│   └── util.py
├── tests
│   ├── data
│   │   ├── img
│   │   │   ├── 0.json
│   │   │   ├── 0.png
│   │   │   ├── 1.json
│   │   │   ├── 1.png
│   │   └── test_model.joblib
│   ├── __init__.py
│   └── test_inference.py
├── .dockerignore                # данные в tests для локальных запусков не включаются в образ
├── .gitignore
├── gpu-pipeline.dockerfile
├── README.md
├── requirements.txt
└── service.dockerfile
```


## Образ Docker

Docker-образ должен быть собран с помощью файла Dockerfile из репозитория модуля.

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

В образе не должно быть лишних данных, таких как:

- папка `.git`
- данные для тестов
- веса ИИ-моделей
- артефакты сборки

Данные файлы рекомендуется сразу добавить в .gitignore для упрощения работы.

Необходимые для работы данные нужно загружать через файловое хранилище S3:

- Для [MLComponent](./mlcmp.md) - через пункты манифеста `modelPath` и `modelBox`.
- Для [ExperimentPipeline](./pipeline.md) - через входную переменную, к которой прописан полный путь в `mountFrom`.

## DataBox

Компонент [DataBox](./databox.md) позволяет модулю использовать файловое хранилище S3. В одном модуле может быть несколько компонентов, отвечающих за разные виды данных. Например, данные конечных пользователей модуля (загруженные для расчётов файлы) и данные разработчика модуля (веса моделей ИИ).



```mermaid
flowchart TB
    User[Пользователь]
    User <--> FileAPI
    S3Box-UserData --> S3Storage

    subgraph Module["Модуль"]
        direction TB

        FileAPI["Файловый API
        .../files/userbox/run_inputs/data.txt"]
        subgraph Container["Контейнер Docker"]
             Filesystem["Файловая система
             /home/user/data/myfile.txt"]
        end
        S3Box-UserData
        S3Box-ModelWeights
    end


    Filesystem <--Монтирование в '/home/user/data/'--> S3Box-UserData[S3 Box: Данные пользователей]
    Filesystem <--Монтирование в '/home/weights/data/'--> S3Box-ModelWeights[S3 Box: Веса моделей]
    
    FileAPI <---> S3Box-UserData["S3 Box 'userbox': Данные пользователей"]
    FileAPI <---> S3Box-ModelWeights["S3 Box 'weights': Веса моделей"]
    S3Box-ModelWeights --> S3Storage

    S3Storage[("S3 Хранилище
    userbox/USERNAME/file_groups/run_inputs/data.txt")]

```

## MLComponent

[MLComponent](./mlcmp.md) - это компонент, который позволяет создать сервис синхронной обработки вычислительных запросов.

Код MLComponent обязательно должен содержать функцию, написанную на языке Python, с заранее определённым интерфейсом ("функция inference"). В неё передаются данные из запроса пользователя, а так же готовая модель машинного обучения, определённая в манифесте MLComponent.

Разработчик указывает, среди прочего:

- Docker-образ компоненты, который содержит код функции inference
- Путь к Python-модулю функции inference в Docker-контейнере
- Путь в Docker-контейнере, по которому можно загрузить модель ИИ
- Настройки запуска Docker-контейнера: лимиты ресурсов, переменные окружения и т.д.
- Детали подключения файлового хранилища S3 к файловой системе контейнера
  - Какие компоненты DataBox используются
  - Соответствие путей в файловом хранилище S3 и путей в файловой системе контейнера


## ExperimentPipeline

Компонент [ExperimentPipeline](./pipeline.md) ("пайплайн") позволяет создать сервис для асинхронной обработки запросов пользователей.

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

- Считать переменные окружения, получить пути к входным данным и к папкам для размещения выходных данных.
- Считать данные, используя полученные пути к входным данным
- Произвести вычисления
- Сохранить результаты, используя полученные пути для размещения выходных данных

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

В самом манифесте ExperimentPipeline разработчик, помимо прочего, указывает:

- Docker-образ, который будет запускаться
  - В том числе настройки запуска - переменные окружения, лимиты ресурсов
- Входные и выходные переменные этапов
  - Могут указываться детали их подключения - путь к файловом хранилище S3, путь в файловой системе контейнера
- Детали подключения файлового хранилища S3 к файловой системе контейнера
  - Какие компоненты DataBox используются
  - Соответствие путей в файловом хранилище S3 путям в файловой системе контейнера по умолчанию

## APIComponent

Компонент APIComponent предоставляет доступ к элементам модуля через API. Есть четыре основных вида компонентов APIComponent:

1. Файловый API
2. API пайплайнов в целом
3. API отдельного MLComponent
4. API отдельного ExperimentPipeline

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

APIComponent отдельного ExperimentPipeline определяет всю структуру взаимодействия с соответствующим пайплайном через API, то есть входные и выходные переменные, их типы данных и описания.

## Общие элементы компонентов

Манифесты компонентов имеют следующую структуру:

```yaml
apiVersion: "unified-platform.cs.hse.ru/v1"
kind: <Тип>
metadata:
  name: <Название компонента>
  namespace: <Название приложения>
spec:
  <Содержимое компонента>
```

Все компоненты в репозитории являются частью одного и того же приложения, поэтому значение поля `metadata.namespace` у всех компонентов в одном репозитории должно совпадать.
