English translation is not available yet. Showing Russian content.

Реализовать topology-aware scheduling для K8s device plugin с учётом NVSwitch доменов

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Реализовать topology-aware scheduling для K8s device plugin с учётом NVSwitch доменов

1. Цель задачи

Реализовать и протестировать plugin|device Kubernetes plugin|device plugin, который экспортирует топологию NVSwitch/NVLink в виде меток и аннотаций узла. Настроить скедулер так, чтобы pod’ы, использующие parallelism|tensor parallelism (TP), гарантированно размещались на GPU, соединённых через один NVSwitch, минимизируя меж-GPU задержки и перекрёстные обращения к памяти.

Ключевой результат После выполнения задачи вы сможете развернуть plugin|device plugin, который автоматически назначает GPU для tensor parallel workload’ов внутри одного NVSwitch домена, и ваш кластер обеспечит стабильно низкую латентность (<10 мкс на меж-GPU трансфер) при работе моделей вроде LLaMA-70B.


2. Исходные данные

Что нужноОткуда взять
Кластер Kubernetes с GPU-узлами (NVIDIA A100/H100, поддерживающие NVSwitch)Реальный кластер (например, GPU-нода с 4–8 GPU) или эмуляция через симулятор топологии
Работающий kube-scheduler (vanilla или custom)Стандартная установка Kubernetes
NVIDIA driver, nvidia-container-toolkit, nvidia-device-pluginУстанавливаются через Helm
Доступ к командам: nvidia-smi topo -m, nvidia-smi nvlink -sПодтверждение топологии NVLink/NVSwitch
Тестовый workload с tensor parallelism (например, vLLM или TGI)Docker-образ, HuggingFace модель (скачать или мини-тест)
Helm / kubectl, python3Установлены на машине администратора
(Опционально) Prometheus + Grafana для мониторинга GPU-взаимодействийУстановить заранее или добавить в этапы

Если нет реального инструмента — симулируем:

  1. Разверните одноузловой кластер Kubernetes (kind/minikube) с поддержкой GPU через nvidia-gpu-device-plugin.
  2. Используйте симулятор топологии: напишите configmap с JSON, описывающим домены NVSwitch.
  3. plugin|Device plugin будет читать этот configmap эмулировать структуру NVSwitch.
  4. Для теста TP возьмите миниатюрную модель (например, GPT-2 1.5B через PyTorch + FSDP или MSCCL), настройте так, чтобы требовалось 2 GPU из одного домена и 2 из другого — это проверит корректность привязки.

3. Технологический стек

КомпонентИнструментыНазначение
Управление кластеромKubernetes (1.27+), Helm 3, kubectlОркестрация pod’ов и device plugin
GPU-инфраструктураNVIDIA driver >=535, nvidia-container-toolkit, nvidia-device-plugin (v0.14)Работа с GPU внутри контейнеров
Анализ топологииnvidia-smi topo -m, nvidia-smi nvlink -s, nvtopПолучение матрицы NVLink и доменов NVSwitch
Device pluginGo 1.21+, Kubernetes device plugin APIНаписание плагина, экспортирующего метки
Скедулингkube-scheduler (с приоритетами или через affinity/taints)Размещение pod’ов согласно меткам
Тестовый workloadPython 3.10, PyTorch 2.0+, HuggingFace transformers, vLLM (опционально)Запуск инференса с tensor parallelism
МониторингPrometheus (gpu-exporter) + GrafanaПроверка меж-GPU трафика

4. Этапы выполнения

Этап 1: Изучение топологии NVSwitch и определение требований (оценка времени: 2 часа)

Действия

  1. Соберите топологию вашего GPU-узла
    Зайдите на GPU-ноду и выполните:

    nvidia-smi topo -m
    nvidia-smi nvlink -s
    nvidia-smi nvlink -c
    

    Запишите матрицу подключений: какие GPU находятся в одном NVSwitch домене, какие используют NVLink.

  2. Создайте документацию топологии
    Нарисуйте схему: NVSwitch (обычно 2–3 чипа) соединяют группы GPU (например, GPU 0-3 через NVSwitch 0, GPU 4-7 через NVSwitch 1).
    Определите, сколько пар GPU могут общаться напрямую: для A100 NVSwitch поддерживает до 8 GPU в одном домене.

  3. Определите критерии для tensor parallelism

    • TP требует, чтобы все GPU, используемые моделью, находились в одном NVSwitch домене (максимальная пропускная способность ~600 ГБ/с).
    • Задача device plugin: отметить GPU меткой nvswitch-domain: <id>.
  4. Сформулируйте API для device plugin

    • Device plugin должен сообщать о зарегистрированных GPU и их доменах.
    • На узлах будут созданы:
      • Labels: gpu.nvidia.com/nvswitch-domain=<id> (для каждого GPU может быть разная метка, но при выдаче ресурса pod получит соответствие).
      • Device IDs: nvidia.com/gpu-<index> с привязкой к домену.

Ожидаемый результат этапа
Документированная топология и описание того, как будет выглядеть метки (пример: node gpu-node01 получит gpu-nvswitch-domain-0=0,1,2,3 и gpu-nvswitch-domain-1=4,5,6,7).


Этап 2: Разработка и деплой кастомного device plugin (оценка времени: 6 часов)

Действия

  1. Скачайте шаблон nvidia-device-plugin

    git clone https://github.com/NVIDIA/k8s-device-plugin
    cd k8s-device-plugin
    
  2. Напишите код, который читает топологию NVSwitch
    Используйте go bindings для nvidia-smi (такие как github.com/NVIDIA/go-nvml/pkg/nvml).
    Реализуйте функцию GetNVSwitchDomains():

    // Псевдокод: для каждой GPU получить её номер и сопоставить с доменом NVSwitch
    func GetNVSwitchDomains() map[string]string {
        // Использовать NVML nvmlDeviceGetNvLinkRemoteDeviceType или 
        // получить через nvidia-smi topo парсинг вывода
        // ...
    }
    
  3. Расширьте экспорт ресурсов плагина
    В методе ListAndWatch() модифицируйте объявление device’ов: для каждого GPU укажите дополнительные метки (labels) в аннотациях узла.
    Для этого используйте k8s.io/client-go для обновления metadata node.

    Пример добавляемой аннотации на node:

    metadata:
      annotations:
        nvidia.com/gpu-topo-nvswitch-domain-0: "0,1,2,3"
        nvidia.com/gpu-topo-nvswitch-domain-1: "4,5,6,7"
    
  4. Протестируйте плагин локально
    Установите плагин как DaemonSet в кластере. Проверьте, что после запуска плагина на узле появились правильные аннотации:

    kubectl describe node <node-name> | grep -A5 nvidia
    
  5. Добавьте механизм приоритетной выдачи GPU
    По умолчанию device plugin распределяет GPU в порядке лицензирования. Измените логику, чтобы pod с j тэгом tensor-parallel=true получал GPU из одного домена (разумеется, если запрашивает 2 или 4 GPU).

    В Allocate() проверьте, сколько GPU запрашивает pod, и если все они должны быть в одном домене — выбрать домен, где есть свободное количество ≥ запрошенного.

Ожидаемый результат этапа
Работающий device plugin, который:

  • корректно отображает топологию (метки/аннотации),
  • ограничивает выдачи GPU выходом за пределы домена, если pod явно того требует (через label selector).

Этап 3: Интеграция с kube-scheduler через node affinity (оценка времени: 3 часа)

Действия

  1. Сконфигурируйте pod’ы с TP, чтобы они выбирали узел с нужной меткой
    В spec pod’а добавьте:

    spec:
      nodeSelector:
        nvidia.com/gpu-topo-nvswitch-domain-0: "0,1,2,3"
      containers:
      - name: inference
        resources:
          limits:
            nvidia.com/gpu: 4  # 4 GPU из одного домена
    

    Но стандартный nodeSelector привяжет ко всему узлу, а не к отдельным GPU. Чтобы выбирать GPU внутри узла, используйте предварительное выделение (pre-scheduling) через device plugin.

  2. Настройте тождество (identity) домена через extended resource
    Сделайте device plugin, который публикует домен как extended resource (например, nvidia.com/nvswitch-domain-0 count = 4). Тогда pod может запросить конкретный ресурс и все GPU будут из этого домена:

    resources:
      limits:
        nvidia.com/gpu: 4
        nvidia.com/nvswitch-domain-0: 4
    

    Это нестандартно; лучше использовать метки узлов и под специальный scheduler.

  3. Разверните custom scheduler
    Напишите простой scheduler (или используйте kube-scheduler с extender). Scheduler будет отказываться от назначения pod’а на узел, если не может выделить нужное количество GPU из одного домена.

    Реализация: scheduler extender в виде HTTP-сервиса, который проверяет:

    • запрос nvidia.com/gpu: X
    • топологию узла,
    • доступность GPU в одном домене.

    Простейшая реализация на Python (Flask) – 50 строк, не входит в этот этап, но можно использовать существующий gpushare-scheduler-extender.

  4. Настройте pod priority и pod group (для TP)
    Через аннотацию scheduler.alpha.kubernetes.io/critical-pod или используйте coscheduling.

Ожидаемый результат этапа
Pod с TP запросами (например, 4 GPU) размещаются на узлах, где есть свободная группа GPU в одном NVSwitch домене. Pod не может запуститься, если такой группы нет.


Этап 4: Тестирование и верификация (оценка времени: 4 часа)

Действия

  1. Запустите тестовый workload с tensor parallelism
    Используйте vLLM (небольшую модель, например, facebook/opt-1.3b). Конфигурация:

    apiVersion: v1
    kind: Pod
    metadata:
      name: tp-test-1
      labels:
        tensor-parallel: "true"
    spec:
      containers:
      - image: vllm/vllm-openai:latest
        args: ["--model", "facebook/opt-1.3b", "--tensor-parallel-size", "2"]
        resources:
          limits:
            nvidia.com/gpu: 2
    
  2. Проверьте, что оба GPU из одного NVSwitch домена
    После запуска pod’а выполните nvidia-smi nvlink -s на узле и найдите GPU, закреплённые за pod’ом (по PID). Убедитесь, что NVLink между ними активен.

  3. Измерьте latency меж-GPU обмена
    Напишите простой скрипт или используйте nvbandwidth внутри контейнера. Сравните latency для GPU из одного домена vs разных доменов.

  4. Нагрузочное тестирование
    Запустите несколько pod’ов (2-3) с разными запросами на количество GPU. Убедитесь, что scheduler не назначает pod, если нет свободной группы в домене.

  5. Соберите метрики
    Если есть Prometheus/nvidia-exporter — понаблюдайте за метрикой DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL. Для GPU из одного домена пропускная способность должна быть стабильно высокой.

Ожидаемый результат этапа
Подтверждено, что TP-модели работают с ожидаемой производительностью, а scheduler корректно изолирует группы GPU.


Этап 5: Документация и CI (оценка времени: 2 часа)

Действия

  1. Напишите README
    Опишите архитектуру, как разворачивать плагин, как тестировать.

  2. Создайте Makefile и Helm chart
    Упакуйте device plugin как Helm chart, чтобы можно было развернуть одной командой.

  3. Напишите тесты

    • unit тест для функции парсинга топологии,
    • интеграционный тест (kubetest) на развертывание и проверку меток.
  4. Задокументируйте ограничения
    Например: текущая версия работает только для узлов с одинаковым количеством GPU в каждом домене.

Ожидаемый результат этапа
Репозиторий с code и документацией, готовый к повторному использованию.


5. Критерии приемки (Definition of Done)

  • Device plugin разворачивается через Helm и корректно экспортирует метки NVSwitch домена на узлах.
  • Тестовый pod с 2 GPU и меткой tensor-parallel: "true" получает GPU, принадлежащие одному домену (подтверждается через nvidia-smi nvlink -s).
  • При недостатке GPU в одном домене pod не может быть запланирован (остаётся в Pending).
  • Для TP workload измерение latency меж-GPU (через nvbandwidth или внутри модели) показывает значения < 5 мкс при передаче 1MB.
  • Код плагина покрыт unit-тестами (минимум 3 теста на парсинг топологии).
  • Документация включает схему разворачивания и примеры конфигурации pod’ов.
  • Все этапы воспроизводимы без прямого доступа к NVSwitch (через симулятор).

6. Ожидаемый результат

Основной артефакт

  • Репозиторий GitHub (или директория) с исходным кодом кастомного device plugin на Go.
  • Helm chart для его деплоя.
  • README с инструкциями.
  • Набор скриптов для тестирования (bash/Python).

Дополнительно (опционально):

  • Scheduler extender с фильтрацией доменов.
  • Демонстрация работы на видео (1-2 минуты) или GIF.
  • JSON-схема топологии для симуляции.

7. Возможные сложности и их решение

СложностьРешение
Отсутствие доступа к NVIDIA NVML в контейнере device pluginПрограммно читать /proc/driver/nvidia/gpus/*/nvlink или вызывать nvidia-smi через файловую систему (bind mount).
Невозможность протестировать на реальном кластереИспользовать симуляцию с помощью фейкового NVML (mock) – написать интерфейс NVML и подменять его.
Pod с TP требует чётное количество GPU, а домены не заполненыРеализовать в scheduler осведомлённость о чётности и оставшихся GPU.
Device plugin не может изменить метки/аннотации узла из-за RBACДобавить ClusterRole с правами на nodes/status и nodes/annotations.
vLLM не поддерживает маленькие модели (ошибка out of memory)Использовать модель OPT-125M или GPT-2 small; уменьшить контекст до 128 токенов.

8. Бюджет времени (оценка)

ЭтапВремя (часы)
Этап 1: Изучение топологии2
Этап 2: Разработка device plugin6
Этап 3: Интеграция scheduler3
Этап 4: Тестирование4
Этап 5: Документация и CI2
Итого17 часов

Примечание Для первого выполнения рекомендуется выделить 20-25 часов с учётом отладки.


9. Связанные вопросы из базы знаний

ВопросТема
12Принципы работы NVLink и NVSwitch
15Архитектура Kubernetes Device Plugin
18Написание Custom Scheduler Extender
22Tensor Parallelism в распределённом инференсе
31Профилирование меж-GPU коммуникаций (nvbandwidth)
45Helm chart структура и деплой
67RBAC для изменений ресурсов узла
88Prometheus exporter для GPU метрик
112Как тестировать device plugin без реальных GPU (mock)
145Оптимизация расстановки pod'ов с помощью topology-aware scheduling

10. Чек-лист самопроверки

  • Я умею собирать топологию NVSwitch через nvidia-smi topo -m и интерпретировать матрицу.
  • Y устройство плагина корректно экспортирует аннотации узла с доменами (проверено kubectl describe node).
  • Мой тестовый pod с 2 GPU не может быть запланирован, если свободны только 2 GPU из разных доменов.
  • Я измерил latency меж-GPU и убедился, что внутри домена она <5 мкс.
  • Код покрыт тестами, Helm chart устанавливается без ошибок, README содержит примеры.