Доброго времени суток, дорогой читатель!
Последние несколько лет в решении бизнес-задач прогрессирует тренд использования Искусственного Интеллекта. Перед специалистами, отвечающими за инфраструктуру встают вопросы о том, какие решения они могут предложить ML-специалистам для закрытия их потребностей в отказоустойчивой и гибкой инфраструктуре с учетом специфических потребностей сферы ML. В том числе, растет число инструментов и фич, которые они предоставляют, и многие задаются вопросом: как собрать свой MLOps-стек, чтобы он был удобный, (желательно) бесплатный и закрывал большинство распространенных потребностей?
В сегодняшней статье рассмотрим способы реализации model serving, то есть инструментов, которые нужны для того, чтобы подготовить модель к деплою и запустить в Kubernetes.
Я сравню несколько решений для запуска моделей - таких как:
Дефолтные сущности k8s + самописный/собранный docker image для работы с моделью
SeldonCore
Kserve
BentoML
Постараемся дать краткий обзор на каждый инструмент и сравнить их возможности и ограничения.
Почему Kubernetes?
(ну потому что я devops) Помимо того, что K8s уже стал стандартом для построения отказоустойчивой production-инфраструктуры, у него есть возможность реализации нескольких преимуществ, которые особенно полезны при работе с моделями машинного обучения:
Возможность автоматического горизонтального масштабирования приложений.
В облаках есть возможность динамически заказывать ресурсы только на тот период, когда в них есть потребность, что может быть актуально, например, если вам периодически требуется GPU.
Возможность внедрения инструментов гибкого управления трафиком с помощью сущностей Ingress и дополнительных операторов.
Удобные механизмы для деплоя приложений и поддержания подхода IaaC.
Реализуемые “из коробки” Rolling Updates, которые позволяют без даунтайма обновлять приложения.
Есть сценарии, где k8s избыточен и не нужен. На моей практике это либо когда проект ещё совсем маленький и ему не нужно столько ресурсов, либо, наоборот, под инференс модели требуется большой мощный кластер 24/7, и нет потребности в масштабировании.
Inference Server
Для более полного понимания картины опишу ещё что такое Inference Server. Это код, который отвечает за работу с моделью - по аналогии с веб-сервером, который может отвечать за выполнение файлов кода на сервере. Однако, в IS также можно реализовывать дополнительную логику для оптимизации работы с CPU/GPU, отдаче метрик итд. Сейчас есть несколько особенно популярных решений для распространенных архитектур моделей. Инструменты, которые мы будем рассматривать дальше (для model serving) интегрируются с некоторыми Inference Servers.
Один из таких IS - это Nvidia Triton, про него пару слов ниже.
NVIDIA Triton
Может работать с TensorFlow, Nvidia TensorRT, PyTorch, MXNet, Python, ONNX, RAPIDS FIL (для XGBoost, scikit-learn, etc.), OpenVINO, custom C++. Можно настроить динамический batching (группировку запросов), может работать с разными типами запросов: запросы в реальном времени, batching, streaming. Можно соединять модели в пайплайны и ансамбли для передачи данных напрямую от модели к модели без необходимости лишних транзакций данных. Поддерживает как CPU, так и GPU.
Подробнее про его возможности можно почитать здесь.
Развертывание в Kuberenetes реализуем, в том числе, и простым deployment.yaml, где вместо вашего кастомного docker image вы выбираете один из images Triton и кладете в указанное место (s3, nfs или pvc) модель в соответствии с инструкциями по подготовке этого вида модели к деплою. Есть также интересный вариант автомасштабирования количества инстансов в k8s при помощи кастомных метрик, которые собираются с самого Inference Server через PodMonitor от Prometheus и HPA. В целом это классический вариант автомасштабирвания в k8s при помощи кастомных метрик, но производитель сам описал этот кейс и все нужные манифесты для его применения здесь, что довольно удобно. Как это работает - на схеме ниже:
Model Serving in Kubernetes
Теперь посмотрим на способы, которыми модель можно задеплоить в k8s.
0. Дефолтные сущности k8s
Первый и самый очевидный способ - это поступить с моделью в k8s так же, как и с любым другим приложением: сначала упаковать все необходимое в docker, а потом запустить в k8s через такие сущности как deployment, service и ingress. Docker-контейнеры без труда версионируются в вашем любимом docker registry, а задачи размещения их в k8s можно решить с помощью встроенных фич куба и Ingress Controller. Разберем, как будет происходить процесс деплоя по шагам:
ML-инженер в результате экспериментов с параметрами и данными для обучения создает модель.
ML-инженер или отдельный специалист пишет код для запуска модели, который может обрабатывать запросы, реализовывать какую-либо дополнительную логику, предоставлять метрики. Код упаковывается в docker-контейнер.
Далее можно код и модель упаковать в один контейнер и запускать в классическом deployment.yaml. Либо, как вариант, упаковать в docker только код, а саму модель монтировать извне.
Добавляем svc и ingress, который балансирует на TCP, - и наш Inference Service готов. Зачастую для задач обучения и работы модели нужно, чтобы k8s предоставлял GPU, это реализуемо за счет Nvidia GPU Operator . Это kubernetes-оператор для того, чтобы GPU можно было подключать как ресурсы внутри k8s.
Однако такой подход не всегда удобен по следующим причинам:
Для запуска модели в коде нужно поддерживать множество фич, таких как предоставление развернутых метрик/графиков во время работы модели, поддержка работы с GPU, поддержка gRCP, HTTP, REST, поддержка потоковой обработки, итд. И тут на помощь может прийти рассмотренный выше Nvidia Triton либо другие предсобранные решения.
Если вы ML-инженер, а не девопс со стажем, то создание многочисленных сущностей k8s, таких, как deployment (а может быть job или statefulset?), ingress, svc, ingress-controller может занимать очень много времени (тем более для организации ансамблей моделей) и в итоге все равно могут быть не вполне понятны все взаимосвязи, и не достигнут желаемый результат.
Для решения этих и других проблем существую более “коробочные” решения и для подготовки модели к деплою и для самого инференса. Ниже попытаемся рассмотреть и сравнить их.
1. Kserve
Kserve предоставляет Kubernetes-оператор и предсобранные docker images для работы модели, он дает возможность написать один yaml-файл для запуска модели, скрыв под капотом все используемые для этого сущности Kubernetes (Deployment, Service, Ingress, HPA, etc). И при этом собрать в yaml всю необходимую информацию про модель и реализовать желаемый функционал. Предсобранные docker images есть для запуска различных архитектур моделей (Tensorflow, XGBoost, ScikitLearn, PyTorch, ONNX, итд) Kserve интегрируется с KubeFlow так, что в его интерфейсе после создания модели вы сможете видеть информацию о модели и настроить туда вывод TensorBoard графиков с инференса. Кроме этого, у Kserve есть SDK для деплоя из ноутбука в KubeFlow, то есть сущность InferenceServer можно деплоить не только как yaml-файл, но и через код в ноутбуке через api KubeFlow.
Ещё у кастомной сущности InferenceService через графу transformer задавать pre и post процессинг данных:
apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
name: torchserve-transformer
spec:
transformer:
containers:
- image: kfserving/torchserve-image-transformer:latest
name: kserve-container
env:
- name: STORAGE_URI
value: gs://kfserving-examples/models/torchserve/image_classifier
predictor:
pytorch:
storageUri: gs://kfserving-examples/models/torchserve/image_classifier
2. SeldonCore
Seldon Core, как и Kserve, реализуется через Kubernetes-оператор и CRD (SeldonDeployment) для развертывания моделей в k8s, также интегрируется с KubeFlow. При этом, сам по себе является частью большого платного проекта Seldon Deploy. У Seldon Core тоже есть свои предсобранные docker images для разных типов моделей, включая NVIDIA Triton и MLFLow. Пре- и постобработку данных тоже можно делать через спецификацию TRANSFORMER и реализовать как класс Python. Ещё Seldon Core предлагает абстракцию графов вывода. Через неё можно делать не только преобразование данных, но и настраиваемый ROUTER для определения того, в какую модель внутри одного SeldonDeployment отправлять данные, и, ещё есть COMBINER для создания ансамбля моделей внутри одного SeldonDeployment. Примеры можно посмотреть здесь. Но эти фичи недоступны, если вы используете MLServer или Triton Server (issue https://github.com/SeldonIO/MLServer/issues/287).
Для запуска модели в SeldonCore есть два варианта: Reusable Server и Non-reusable Server. Разница в том, что в Reusable модель как файл монтируется в работающий под, а в Non-reusable этот файл зашивается в docker image, поэтому его нужно пересобирать каждый раз при обновлении модели.
В общем виде, если мы используем Reusable, то деплой модели выглядит следующим образом:
Сбилдили docker image через s2i:
s2i build https://github.com/seldonio/seldon-core.git
--context-dir=wrappers/s2i/python/test/model-template-app seldonio/seldon-core-s2i-python3:1.15.0-dev
seldon-core-template-model
Обновили версию Docker image в CRD.
Применили CRD.
Profit! Измененная модель задеплоена.
3. BentoML + Yatai
BentoML - это фреймворк для сервировки моделей. Он предлагает функционал для того, чтобы можно было максимально удобно сделать из модели запускаемый сервис. Через BentoML можно собрать как Bento, так и docker-контейнер, готовый к деплою. Bento - это формат хранения модели в распределенном виде. Это автономный архив, содержащий весь исходный код, файлы моделей и определения зависимостей, необходимые для запуска сервиса. Всю пре- и постобработку данных можно также зашить в Bento, в принципе, любой код.
Что классно - у BentoML также доступны интеграции для деплоя в облака и различные другие платформы (AWS Lambda, AWS EC2, Heroku, Google Cloud Run etc) через bentoctl. А для деплоя в k8s у них есть инструмент Yatai (название от японской тележки для продажи бенто). Под капотом содержит Kubernetes-оператор и PostgreSQL/ Minio / docker-registry для хранения компонентов Bento, конечно, для надежности настраивается так, чтобы компоненты типа Postgres или docker registry можно было подключить извне k8s. В целом у BentoML довольно активное англоговорящее комьюнити и весьма подробная документация. К слову, Nvidia Triton вы тоже можете использовать в Bento, если зададите его в качестве компонента Runner.
Сравнение
Для наглядности сведем основные требования для model serving tools:
Возможности | Kserve | Seldon Core | BentoML + Yatai | Deployment+svc+ingress |
Предсобранные решения для Scikit-Learn, PyTorch, TensorFlow, XGBoost, Nvidia Triton, MLFlow | Да, предсобранные docker-images | Да, но для PyTorch доступен только Nvidia Triton и только с кастомным протоколом V2 | Есть оптимизированные решения для сборки моделей основных фреймворков | Можно взять docker контейнеры от других инструментов либо писать код самому. |
Работа с GPU | + | + | + | Зависит от Inference Server |
Возможность запускать кастомные решения для IS | + | + | + | + |
Автоматическая группировка запросов (batching) | + | - | + | - |
Автомасштабирование и scale to zero | По трафику | По CPU/mem | CPU | -, но можно сделать через сущность k8s HPA, или HPA+Prometheus |
Canary Deployments | + | + | + | -, но решаемо через дополнительные настройки сущностей k8s |
A/B tests | - | + | - | -, но решаемо через дополнительные настройки сущностей k8s |
GRPC | +, но требует ручной реализации | + | - | -, но решаемо через дополнительные настройки сущностей k8s и Contour |
Отдача метрик | + | + | + | Зависит от Inference Server |
Pre/post процессинг данных | + | + | + | - |
Вывод
Как видно из описания выше и сравнительной таблицы, все инструменты призваны упростить задачу предоставления доступа к модели. Кроме того, они постоянно совершенствуются и стремятся расширить свой функционал. Выбирая для работы один из них, мы бы рекомендовали, в первую очередь, четко определить потребности вашей модели, и рассматривать каждый инструмент с точки зрения того, насколько он видится вашей команде удобным/интуитивно понятным.
Надеюсь, что после прочтения этой статьи у вас сложилось представление о том, что же представляет из себя каждый инструмент, и закроет ли он ваши потребности. Если у вас есть важные дополнения и вопросы к обсуждению по ним, то прошу в комментарии :).
А за новостями по теме DevOps (и не только) - прошу в наш telegram-канал DevOps FM.