- BrainTools - https://www.braintools.ru -
Привет! Я Дмитрий, инженер и руководитель направления MLOps в Совкомбанке. Специализируюсь на разработке и эксплуатации ML-платформ на базе Kubernetes и GPU.
С 2010 года в ИТ: строю инфраструктуру для машинного обучения [1], внедряю Kubeflow и GPU-оператор, настраиваю MIG на H100 в корпоративных средах с повышенными требованиями к безопасности и надежности. В последние годы фокусируюсь на оптимизации ML-пайплайнов, повышении утилизации GPU (включая MIG-профили) и интеграции MLOps-практик в процессы продуктовых команд.
В 2022 году в некоторых командах разработки «Совкомбанка» уже существовали проекты с применением искусственного интеллекта [2]. Это были отдельные компоненты, которые хорошо справлялись с конкретными задачами приложения. Но не хватало единой платформы управления.
По мере роста количества и сложности бизнес-задач возникла необходимость в создании ML-платформы как сервиса с едиными стандартами авторизации, размещения моделей на платформе для устранения проблем контроля, унификации и безопасности ML-моделей.
Единая ML-платформа с гибким RBAC позволила бы:
Защитить модели и данные — с контролем доступа на уровне команд и проектов.
Отслеживать, кто и когда использует модель — для аудита и безопасности.
Дать бизнесу самостоятельность — без ожидания инженеров, можно быстро тестировать гипотезы.
Мы изучили доступные инструменты, попытались объединить их в одном Kubernetes-кластере, столкнулись с рядом ограничений — и в итоге пришли к архитектуре на базе Kubeflow и GPU-оператора.
В статье рассказываем, какие сложности были в ходе проекта, как выстроили работу с Kubeflow, настраивали H100 с MIG-разделением и что важно учесть, если вы планируете строить ML-платформу на bare-metal-GPU в корпоративной среде.
На рынке в то время были доступны две ML-платформы: проприетарная Caila от Just AI и open-source-решение Kubeflow.
Оба варианта предъявляли схожие требования к инфраструктуре:
кластер Kubernetes последних версий;
bare-metal-серверы с поддержкой GPU;
административные привилегии в кластере.
Для управления кластерами Kubernetes в банке используется платформа «Штурвал», обладающая модульной и расширяемой архитектурой. Большая часть необходимых модулей работала в платформе «из коробки» — и то, что нам потребовалось доработать, никак не повлияло на поддержку решения. Для вычислительных ресурсов выбрали доступные на тот момент bare-metal-серверы с GPU H100.
Сначала мы развернули коммунальный Kubernetes-кластер для обеих ML-платформ. Разделение микросервисов планировалось осуществлять по стандартной схеме с использованием labels и taints для изоляции компонентов разных платформ. Однако этот подход привел к ряду критических проблем:
Конфликты компонентов. Несмотря на использование labels и taints, компоненты Caila и Kubeflow конфликтовали при работе в одном кластере. ML-платформы конкурировали за ресурсы одних и тех же серверов. Распределить их компоненты между разными серверами не удалось, так как у производителей нет такой возможности. Любое обновление компонента платформы приводило бы к изменению конфигурации и откату назад. Из-за этого было сложно применить единую схему маркировки ко всем Kubernetes YAML-манифестам, которые генерировали платформы.
Угрозы информационной безопасности. Обе платформы имели административные привилегии в кластере и, следовательно, получали доступ ко всем компонентам друг друга. Это создавало потенциальные риски для безопасности и изоляции данных.
В результате анализа этих проблем мы решили разделить проекты на два независимых кластера.
Создание и настройку кластера Kubernetes в данной статье опустим, в «Штурвале» это делается просто и быстро. Сфокусируемся на самом интересном — установке Kubeflow и настройке видеокарт.
Необходимые компоненты:
Три виртуальных мастера на RedOS с ядром 6.12.21.
Три железных рабочих ноды на RedOS с ядром 6.12.21 и видеокартами H100.
Собранный кластер на «Штурвале».
Ниже описываем, как пошагово установить и настроить драйверы в любом используемом дистрибутиве Kubernetes.
Закомментируем в /etc/dnf/dnf.conf
exclude=kernel* *kernel
Ставим пакет для ядра.
yum install kernel-lt-devel-6.12.21.red80.x86_64
Проверяем, что в параметрах grub установлены эти значения:
module.sig_enforce=0
rd.driver.blacklist=nouveau
Добавляем драйвер nouveau в blacklist-модулей.
Создаем файл /etc/modprobe.d/blacklist-nouveau.conf:
После перезагрузки вывод следующей команды должен быть пустым: lsmod | grep nouveau
Если это не так, выполняем следующую команду и перезагружаемся:
dracut –force
Отключаем /etc/dnf/plugins/subscription-manager.conf
Далее выставляем флаг disable_system_repos=0
Включаем прокси.
export https_proxy=http://proxy.company.io:3128/
export http_proxy=http://proxy.company.io:3128/
Подключаем два репозитория:
Создаем в /etc/yum.repos.d/
cuda-rhel8.repo:
nvidia-container-toolkit.repo:
Обязательно включаем репозитории: enabled=0 меняем на enabled=1
Устанавливаем драйверы.
dnf install dkms
dnf -y module install nvidia-driver:latest-dkms
Проверяем наличие драйвера
modprobe -vv nvidia
lsmod | grep nvidia
или
nvidia-smi
Включаем службу.
systemctl enable --now nvidia-persistenced.service
Ставим дополнительные пакеты.
yum install nvidia-docker2.noarch
yum install nvidia-fabric-manager-580.95.05.x86_64
Перезагружаемся и проверяем, что все работает корректно.
reboot
nvidia-smi
Во время установки пользователи могут столкнуться с рядом ошибок, которые мы решали совместно с инженерами «Штурвала». Ниже разберем самые распространенные и возможные пути их решения.
После установки драйвера, модуль ядра не подгружается
При попытке выполнить modprobe nvidia, система выдает вот такую ошибку [4]: Required key not available
Решение:
DKMS не видит драйверы
При наборе команды:
dkms status
вместо installed:nvidia/580.95.05: added
Собираем и устанавливаем драйвер с помощью dkms: sudo dkms install -m nvidia -v 580.95.05
Если система выдает подобную ошибку:
Failed command: 'make' -j128 KERNEL_UNAME=6.12.21.red80.x86_64 modules
Error! Bad return status for module build on kernel: 6.12.21.red80.x86_64 (x86_64)
Смотрим логи:
cat /var/lib/dkms/nvidia/580.95.05/build/make.log
Видим ошибку прав доступа у системного линкера:
/bin/sh: line 1: /bin/ld: Permission denied
Далее смотрим права линкера:
ls -l /usr/bin/ld
Удаляем файл и создаем символическую ссылку:
sudo rm -f /usr/bin/ld
sudo ln -s /usr/bin/ld.bfd /usr/bin/ld
Повторно собираем и устанавливаем драйвер:
sudo dkms install -m nvidia -v 580.95.05
dkms status
Если под nvidia-operator-validator не стартует с версией драйверов 580.95.05 из официального репозитория nvidia, то читаем логи пода.
Если видим подобную ошибку:
Error: failed to create containerd task: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error running prestart hook #0: exit status 1, stdout: , stderr: Auto-detected mode as 'legacy' nvidia-container-cli: mount error: failed to add device rules: unable to generate new device filter program from existing programs: unable to create new device filters program: load program: invalid argument: last insn is not an exit or jmp processed 0 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0: unknown
То решаем ее путем изменения параметров ядра: с net.core.bpf_jit_harden=2 на net.core.bpf_jit_harden=1
Настраиваем nvidia container toolkit:
Настраиваем конфиг containerd в /etc/containerd/config.toml
В секции [plugins."io.containerd.grpc.v1.cri".containerd.runtimes] меняем значения для runtime в подсекциях:
В подсекции [plugins."io.containerd.runtime.v1.linux"] меняем значение на runtime = "nvidia"
Перед деплоем gpu-operator необходимо добавить namespace (gpu-operator) в исключение kyverno
Далее качаем helm chart и добавляем в проект кластера. Описание helm values доступно здесь [5].
Отключаем лишнее в values (драйвер ставим самостоятельно):
Указываем mig strategy в mixed:
mig:
strategy: mixed
Добавляем (fix bug) в validator:
Заносим параметры в tolerations:
Запускаем pod для теста:
Смотрим логи:
kubectl apply -f cuda-vectoradd.yaml
kubectl logs pod/cuda-vectoradd
Вот так выглядит корректный ответ:
После удаляем pod:
kubectl delete -f cuda-vectoradd.yaml
MIG (Multi-Instance GPU) в Kubernetes — это функция разделения графического процессора (GPU) на аппаратном уровне. MIG позволяет разделить один GPU на несколько изолированных экземпляров, каждый из которых имеет выделенные вычислительные ядра, память [7] и кэш.
Эта функция полезна, когда у рабочих нагрузок разные требования к ресурсам и нужно обеспечить строгую изоляцию между ними.
Сейчас пошагово расскажем, как с этим работать.
Разбиваем GPU на отдельные виртуальные части mig, чтобы получить:
Изоляцию ресурсов
Каждая MIG-инстанция работает независимо — как отдельная GPU. Нет «шумных соседей» — если один под грузит GPU — другие не страдают. Изоляция на уровне железа, а не софта (как в контейнерах).
Максимальную утилизацию GPU
Вместо одной тяжелой задачи — можно запустить несколько легких. Например: A100 40GB → 7 MIG 1g.10gb → 7 задач inference.
Гарантированную производительность
Каждая MIG получает фиксированный объем памяти и вычислительных ядер. Идеально для inference-задач, где нужна предсказуемая задержка.
Безопасность
Изоляция на уровне железа — даже если один под «сломается», другие продолжат работать. Это отлично подходит для мультитенантных сред, где разные команды/проекты используют одну GPU.
Поддержку ML/DL/Inference
Идеально для inference, batch processing, ML pipelines. Не подходит для heavy training — там лучше использовать целую GPU.
Работа с MIG
Так как уже развернут gpu-operator со стратегией “mig strategy: mixed” и точно известно, что карты поддерживают разделение на MIG, важно выполнить несколько шагов, чтобы разбить имеющиеся у нас карты.
Первым делом смотрим доступные GPU на нодах:
kubectl get node -o json | jq '.items[].metadata.labels'
Меняем профиль MIG на ноде (all-1g.10gb или all-1g.20gb — профиль, необходимо выбрать подходящий):
kubectl label nodes $NODE nvidia.com/mig.config=all-1g.10gb --overwrite
Можем изменить профиль на сбалансированный, то есть разбить всю карту на части разных размеров:
kubectl label nodes <node-name> nvidia.com/mig.config=all-balanced --overwrite
При желании отключить MIG на ноде вводим эту команду:
kubectl label nodes $NODE nvidia.com/mig.config=all-disabled --overwrite
Для того чтобы посмотреть список профилей MIG’ов, достаточно на ноде с GPU выполнить:
nvidia-smi mig -lgip
Запускаем поды с MIG — для этого указываем в манифесте следующее:
Если нужен конкретный MIG-инстанс, даем больше информации:
Далее проверяем корректность работы с помощью команды.
kubectl get pods -o jsonpath='{range
.items[]}{.metadata.name}{"t"}{.spec.containers[].resources.limits}{"n"}{end}'
Если вы все сделали правильно, то на выходе получаете такой ответ:
my-pod-1 map[nvidia.com/mig-1g.10gb:1]
Принципы и примеры разбиения на MIG.
Существует несколько подходов:
По нагрузке — меняем объем GB: MIG 1g.10gb или MIG 2g.20gb.
По количеству задач — меняем цифру в начале: 4 MIG 1g.10gb. или 2 MIG 2g.20gb.
По вычислительной мощности — изменяем количество ядер: 1g → 1/7 ядер, 2g → 2/7 ядер
По типу задач:
Inference — 1g.10gb
Training — 7g.40gb
Batch processing — 2g.20gb
По изоляции — при работе с критичными задачами важно выделить отдельную MIG.
По стоимости: MIG дешевле, чем целая GPU.
Примеры разбиения:
H100 80 GB:
14 MIG 1g.10gb (для inference)
4 MIG 2g.20gb (для training)
2 MIG 4g.40gb (для heavy training)
1 MIG 7g.80gb (для super-heavy training)
4. Недоступность MIG
Поды не могут переопределить MIG, если одна MIG становится недоступна, так как это физическая изоляция.
Что же происходит, если MIG недоступна:
Под не запускается — то есть Kubernetes не может выделить MIG.
Pod переходит в состояние Pending, пока не освободится MIG.
Восстановление реализуется через livenessProbe, readinessProbe, HorizontalPodAutoscaler.
Есть несколько доступных механизмов, позволяющих обойти это:
Failover. Можно переключиться на другую GPU, например, на GPU 1.
Circuit Breaker. Не нужно работать с MIG, если она недоступна. Если inference-сервис не отвечает, стоит переключиться на резервный.
Retry с экспоненциальной задержкой через 1s, 2s, 4s, 8s.
5. Планирование разбиения на MIG.
Для начала необходимо определить, какие задачи будут запускаться, и ответить себе на несколько вопросов:
Сколько нужно памяти и вычислительных ресурсов?
Какие MIG-инстанции доступны и сколько MIG можно создать?
Сколько задач могут запускать одновременно?
Нужна ли изоляция между задачами? А гарантированная производительность?
Какие метрики, логи, трейсы, дашборды важны для MIG?
Что важно учесть в CI/CD?
Для работы kubeflow необходимы cert-manager и istio, которые есть в репе с Kubeflow.
А для создания деплоя на джамп-хост необходимо установить kustomize и yq.
Первым делом клонируем проект:
Устанавливаем kubeflow в кластер по инструкции на странице проекта по адресу https://github.com/kubeflow/manifests [8] с помощью kustomize.
Пропускаем установку cert-manager, так как он уже присутствует в кластере «Штурвала».
Поэтому устанавливаем только cert-manager-kubeflow-Issuer.
Istio устанавливаем, как указано для oauth-proxy, а oauth-proxy устанавливаем для dex.
Создаем файл ingress с типом сервиса istio-ingressgateway и названием ingress.yaml:
Пример манифеста ingress.yaml представлен ниже (вместо переменных $clustername и $env подставьте свои значения):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
name: ingress-kubeflow
namespace: istio-system
spec:
ingressClassName: nginx
rules:
- host: kubeflow.apps.k8s-$clustername-$env.company.io
http:
paths:
- backend:
service:
name: istio-ingressgateway
port:
number: 80
path: /
pathType: Prefix
tls:
- hosts:
- kubeflow.apps.k8s-$clustername-$env.company.io
Применяем манифест командой kubectl apply -f ingress.yaml
Kubeflow состоит из множества компонентов и разворачивается довольно долго. В процессе деплоя возможно появление различных ошибок, во всяком случае у нас они постоянно появлялись, которые необходимо будет дебажить по мере их появления.
После деплоя Kubeflow выполняем вход по адресу (вместо переменных подставьте свои значения):
kubeflow.apps.k8s-$clustername-$env.company.io/ru
и авторизуемся с профилем по умолчанию —user@example.com [9] и паролем 12341234.
Первоначальная установка Kubeflow завершена.
Изначально в банке использовались две системы, которые не могли корректно работать вместе. Мы задеплоили их в два разных кластера, которые синхронно управляются платформой «Штурвал» — и благодаря этому они больше не конфликтуют.
В результате мы получили управляемую, безопасную и надежную ML-платформу. Теперь бизнес-заказчики могут быстро разворачивать модели для тестирования своих идей, а инженеры — заказывать и получать необходимые ресурсы.
Гибкое управление видеокартами, включая их разбиение, позволило равномерно использовать GPU-ресурсы. Сейчас они используются оптимально и могут быть при необходимости переконфигурированы. Решение успешно работает на отечественном софте в закрытом окружении.
В планах — автоматизировать процесс подготовки новых worker-нод для ML-платформы. Также мы хотим реализовать управление MIG’ами через подход «Инфраструктура как код». А команда «Штурвала» добавит больше «автоматики» в ближайших релизах 2.13 и 2.14.
Что вы думаете об этом опыте [10]? Какие сложности вы видите при построении ML-платформ на Kubernetes? Какие вопросы остались без ответа в статье? Поделитесь своим мнением и задавайте вопросы в комментариях!
Автор: SovcomTech
Источник [11]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/25576
URLs in this post:
[1] обучения: http://www.braintools.ru/article/5125
[2] интеллекта: http://www.braintools.ru/article/7605
[3] Image: https://sourcecraft.dev/
[4] ошибку: http://www.braintools.ru/article/4192
[5] здесь: https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/latest/getting-started.html#chart-customization-options
[6] https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/latest/getting-started.html#running-sample-gpu-applications: https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/latest/getting-started.html#running-sample-gpu-applications
[7] память: http://www.braintools.ru/article/4140
[8] https://github.com/kubeflow/manifests: https://github.com/kubeflow/manifests
[9] user@example.com: mailto:user@example.com
[10] опыте: http://www.braintools.ru/article/6952
[11] Источник: https://habr.com/ru/companies/sovcombank_technologies/articles/994534/?utm_source=habrahabr&utm_medium=rss&utm_campaign=994534
Нажмите здесь для печати.