Настроить GPU scheduling для multi-tenant
ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Настроить GPU scheduling для multi-tenant
1. Цель задачи
Научиться настраивать справедливое распределение GPU-ресурсов между несколькими командами (тенантами) в Kubernetes-кластере, обеспечивая изоляцию и предсказуемую производительность. Требуется реализовать либо аппаратный (NVIDIA MIG), либо программный (Kueue с fair-share) механизм, а затем проверить, что одна команда не может «отобрать» ресурсы у другой.
Ключевой результат Воспроизводимый пайплайн, гарантирующий каждой команде минимальную долю GPU и предотвращающий перегрузки (noisy neighbour) при совместном использовании кластера.
2. Исходные данные
| Что нужно | Откуда взять |
|---|---|
| Kubernetes-кластер (minikube / kind / bare-metal) | Установить самостоятельно или использовать предоставленный dev-кластер |
| Node с NVIDIA GPU (минимум 1 GPU, желательно A100, V100 или A10) | Облачный инстанс (AWS p3.2xlarge, GCP a2-highgpu-1g) или локальный сервер |
| Установленный NVIDIA GPU Operator | Helm-чарт из репозитория NVIDIA |
| Kueue (или Volcano) для программного планирования | Установка через Helm или YAML-манифесты |
| Инструмент для генерации тестовой нагрузки | Python-скрипт с использованием torch или cuda-samples |
| Метрики использования GPU | nvidia-smi, Prometheus + GPU-экспортёр, Grafana |
Если нет реального инструмента — симулируем:
- GPU нет – используйте эмулятор GPU (например,
nvidia-gpu-device-pluginв режиме--mode=fake).- Установите NVIDIA GPU Operator с параметром
devicePlugin.fake= true. - Убедитесь, что NodeResources считает, что GPU доступны.
- Установите NVIDIA GPU Operator с параметром
- MIG не поддерживается (например, на Tesla T4) – сосредоточьтесь только на Kueue.
- Kueue не установлен – следуйте официальной документации:
helm repo add kueue https://kubernetes-sigs.github.io/kueue helm install kueue kueue/kueue - Python / CUDA не доступны – используйте nvidia-smi stress-тесты (например, nvidia-smi -i 0 -l 1 -pm ENABLED и cuda_memtest).
3. Технологический стек
| Компонент | Инструменты | Назначение |
|---|---|---|
| Контейнеризация | Docker, Kubernetes (kind/minikube) | Оркестрация подов |
| GPU-управление | NVIDIA GPU Operator, NVIDIA Container Toolkit | Поддержка GPU в контейнерах |
| Аппаратная изоляция | NVIDIA MIG (Multi-Instance GPU) | Разделение одного GPU на изолированные инстансы |
| Программный scheduling | Kueue (v0.6+) + batch/v1 Job | Очереди, fair-share, квоты по командам |
| Метрики и мониторинг | Prometheus + GPU-метрики, Grafana | Сбор и отображение использования GPU |
| Тестовая нагрузка | Python + PyTorch (или cuda_memtest), sleeper-поды | Эмуляция задач двух команд |
4. Этапы выполнения
Этап 1: Подготовка окружения (оценка 60 мин)
Действия
-
Разверните Kubernetes кластер (если ещё нет).
kind create cluster --name gpu-cluster --config kind-config.yamlВ kind-config.yaml обязательно укажите
extraMountsдля NVIDIA драйверов, если используете реальный GPU. -
Установите NVIDIA GPU Operator.
helm repo add nvidia https://nvidia.github.io/gpu-operator helm install gpu-operator nvidia/gpu-operator –set migManager.enabled=true -
Проверьте, что GPU виден в кластере:
kubectl get nodes -o json | jq '.items[].status.capacity."nvidia.com/gpu"'Ожидаемое значение: количество физических GPU (≥1).
-
Установите Kueue (если выбран программный путь):
helm repo add kueue https://kubernetes-sigs.github.io/kueue helm install kueue kueue/kueue --create-namespace -n kueue-system -
Установите Prometheus и GPU exporter:
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm install prometheus prometheus-community/kube-prometheus-stack -n monitoring kubectl apply -f https://raw.githubusercontent.com/NVIDIA/gpu-operator/master/deploy/gpu-operator/charts/gpu-operator/templates/prometheusrule.yaml
Ожидаемый результат этапа
- GPU ресурсы доступны в нодах (kubectl describe node показывает
nvidia.com/gpu). - Prometheus собирает GPU-метрики (DCGM).
- Kueue установлен и Ready.
Этап 2: Настройка изоляции GPU (MIG) (оценка 45 мин) – альтернативный путь: Kueue
Выберите один путь. Ниже приведён MIG, для Kueue смотрите Этап 2-бис.
Действия (MIG):
-
Проверьте поддержку MIG на GPU:
nvidia-smi mig –gi –gi-profile(должны появиться профили, например
3g.40gbдля A100) -
Создайте MIG-профили через ConfigMap. Пример для одного GPU с двумя инстансами по 20 ГБ:
apiVersion: v1 kind: ConfigMap metadata: name: mig-config namespace: nvidia-gpu-operator data: config: | - devices: all mig-devices: 0: gi-profile: 2g.20gb 1: gi-profile: 2g.20gb -
Примените ConfigMap и дождитесь перезапуска MIG Manager:
kubectl apply -f mig-config.yaml kubectl rollout status -n nvidia-gpu-operator deployment/mig-manager -
Проверьте MIG устройства:
nvidia-smi mig -liДолжно отобразиться два MIG-устройства (например, MIG 3g.40gb/0, MIG 3g.40gb/1).
-
Назначьте каждой команде (tenant) по одному MIG-ресурсу через ResourceQuota или LimitRange.
Ожидаемый результат этапа
- Каждый под может запрашивать только целый MIG-срез.
- Одна команда не может использовать GPU другого MIG-среза.
Этап 2-бис: Настройка Kueue с fair-share (если MIG не используется) (оценка 60 мин)
Действия
-
Создайте ResourceFlavor, описывающий доступные GPU:
apiVersion: kueue.x-k8s.io/v1beta1 kind: ResourceFlavor metadata: name: gpu-flavor spec: nodeLabels: nvidia.com/gpu.product: Tesla-A100 -
Создайте ClusterQueue с fair-share и минимальными квотами для двух команд:
apiVersion: kueue.x-k8s.io/v1beta1 kind: ClusterQueue metadata: name: cluster-queue spec: namespaceSelector: {} resourceGroups: - coveredResources: ["nvidia.com/gpu"] flavors: - name: gpu-flavor resources: - name: nvidia.com/gpu minCount: 2 # минимум 2 GPU одной команде(настройка fair-share задаётся через
spec.cohortилиspec.fairSharing.weight– смотри документацию) -
Создайте LocalQueue в каждом namespace:
apiVersion: kueue.x-k8s.io/v1beta1 kind: LocalQueue metadata: namespace: team-a name: team-a-queue spec: clusterQueue: cluster-queue -
Настройте quotas (ResourceQuota) для команд, чтобы ограничить максимальное количество GPU.
Ожидаемый результат этапа
- Поды команды A и B используют общий пул GPU, но с приоритетами и fair-share.
- При перегрузке одной команды её поды ждут, а другой получают гарантированную долю.
Этап 3: Генерация нагрузки и тестирование изоляции (оценка 30 мин)
Действия
-
Напишите Python-скрипт
gpu_load.py, который занимает 80% памяти GPU на 60 секунд:import torch, time, os tenant = os.environ.get("TENANT", "unknown") x = torch.randn(8000, 8000).cuda() time.sleep(60) -
Запустите по 3 таких пода в каждом namespace (team-a, team-b).
kubectl run --namespace=team-a load-1 --image=pytorch/pytorch:latest -- python gpu_load.py -
Наблюдайте в реальном времени использование GPU через
nvidia-smi dmon -d 1или Grafana. -
Проверьте изоляцию:
- Для MIG: каждый под привязан к своему MIG-устройству; загрузка не влияет на другое.
- Для Kueue: при превышении суммарных ресурсов выше minCount, поды одной команды должны ждать; проведите A/B тест, запуская сначала много подов одной команды, затем другой.
Ожидаемый результат этапа
- Выявлена разница в поведении: для MIG – полная изоляция, для Kueue – fair-share с ожиданием.
Этап 4: Мониторинг и валидация (оценка 30 мин)
Действия
-
Создайте дашборд Grafana для GPU-метрик с панелями:
-
Настройте оповещение при превышении доли (например, если команда использует >90% своего minCount более 5 минут).
-
Задокументируйте конфигурацию в Git-репозитории (Helm-чарт или набор манифестов).
Ожидаемый результат этапа
- Рабочий дашборд, отображающий изоляцию.
- Залитая конфигурация в Git.
5. Критерии приемки (Definition of Done)
- Кластер Kubernetes готов, GPU доступен и виден через
kubectl describe node. - MIG-профили созданы (или Kueue настроен с fair-share).
- В каждом namespace (team-a, team-b) созданы LocalQueue и ResourceQuota (для Kueue).
- Запущена тестовая нагрузка от двух команд; при этом одна команда не может деградировать производительность другой более чем на 20% (для MIG – 0%).
- В Grafana или
nvidia-smiвидно, что каждый под привязан к своему GPU/MIG-ресурсу. - Написана документация (README.md) с командами развёртывания и описанием архитектуры.
- Пройдён сценарий «перегрузка одной команды» — поды другой команды получают минимальную гарантию.
- Все манифесты хранятся в Git и воспроизводимы
kubectl apply -k. - Оповещения (alerts) настроены и проверены.
6. Ожидаемый результат
Основной артефакт
- Git-репозиторий со всеми манифестами:
mig-config.yamlилиkueue-clusterqueue.yaml,localqueue.yaml, namespace YAML, тестовый Job, описание дашборда Grafana.
Содержание
- Полная инструкция по развёртыванию (README).
- Скрипты нагрузки.
- Скриншоты дашборда или вывод
nvidia-smi, подтверждающие изоляцию.
Опционально
- Python-скрипт для автоматического тестирования fair-share (проверка, что время ожидания подов одной команды при дозагрузке GPU соответствует настройкам).
- Видео/скринкаст прогона тестов.
7. Возможные сложности и их решение
| Сложность | Решение |
|---|---|
| MIG не поддерживается GPU (например, Tesla T4, RTX 3090) | Переключиться исключительно на Kueue без MIG. Для изоляции достаточно программного fair-share. |
| GPU Operator не видит GPU из-за драйверов | Проверить версию драйвера (>=450.80.02), установить драйвер через NVIDIA Container Toolkit. Для kind – включить --feature-gates="ExperimentalGPUNode=true". |
| Kueue не назначает поды (Pending) | Проверить, что ResourceFlavor совпадает с node labels; что ClusterQueue имеет minCount <= доступных GPU; что поды содержат метку kueue.x-k8s.io/queue-name. |
| Метрики в Prometheus не отображаются | Убедиться, что DCGM-экспортёр запущен (kubectl get pods -n nvidia-gpu-operator -l app=gpu-operator). Добавить ServiceMonitor для него. |
| Под одной команды вытесняется Kueue при высокой нагрузке другой | Настроить preemptionPolicy в ClusterQueue: PreemptWithinCohort. |
| Разные команды пишут в одно MIG-устройство | Исправить ResourceQuota: на каждый namespace должен быть выделен уникальный mig-id через nodeSelector или limitranges. |
| Нет реального GPU для теста | Использовать фейковый device plugin (см. Этап 1), снизить ожидания – изоляция будет проверена на уровне k8s, без реальных вычислений. |
8. Бюджет времени (оценка)
| Этап | Время (минут) |
|---|---|
| 1. Подготовка окружения | 60 |
| 2. Настройка MIG / Kueue | 60 |
| 3. Генерация нагрузки и тестирование | 30 |
| 4. Мониторинг и валидация | 30 |
| Итого (без учёта изучения) | 180 (3 часа) |
| Примечание: для первого раза заложите дополнительно 1,5–2 часа на решение неожиданных проблем. |
9. Связанные вопросы из базы знаний
| Вопрос | Тема |
|---|---|
| 42 | Настройка NVIDIA GPU Operator в Kubernetes |
| 105 | Управление ресурсами GPU через Device Plugin |
| 203 | Multi-Instance GPU (MIG) – концепции и конфигурация |
| 312 | Kueue: установка и базовые объекты (ClusterQueue, LocalQueue) |
| 456 | Fair-share scheduling в batch-системах |
| 578 | Мониторинг GPU с Prometheus и DCGM |
| 612 | Изоляция workload в multi-tenant кластере |
| 723 | Политики вытеснения в Kueue (preemption) |
| 845 | Helm-чарты для GPU-оператора и Kueue |
| 891 | A/B-тестирование GPU-нагрузок в изолированных namespace |
10. Чек-лист самопроверки
- Я проверил, что GPU виден в кластере до начала настройки.
- Я выбрал один из двух путей (MIG или Kueue) и строго ему следовал; второй указан как альтернатива.
- Я создал как минимум два namespace для двух команд.
- Я выполнил тест с одновременной нагрузкой от обеих команд и убедился, что изоляция работает.
- Я задокументировал все шаги и конфигурацию в Git, чтобы их можно было воспроизвести.
- Я настроил хотя бы одно оповещение на перегрузку GPU (DCGM).
- Я проверил, что поды, превышающие квоту, остаются в состоянии Pending, а не вытесняются без предупреждения (для Kueue).
- Я удостоверился, что в случае MIG каждое MIG-устройство закреплено за конкретным namespace.