Миграция ML-нагрузок из Slurm в Kubernetes: запуск распределённого обучения через SkyPilot
Перенос рабочих нагрузок машинного обучения с традиционных суперкомпьютерных кластеров на платформу Kubernetes стал ключевым направлением развития инфраструктуры искусственного интеллекта. Исторически HPC-среда опиралась на планировщик пакетных задач Slurm (Simple Linux Utility for Resource Management). Он предоставлял инженерам простой интерфейс: shell-скрипт с директивами #SBATCH, запуск распределённого обучения через srun и прозрачный доступ к общей сетевой файловой системе (NFS) без контейнерной изоляции.
Однако при масштабировании инфраструктуры платформенные команды стремятся консолидировать мощности в единой среде Kubernetes. Оркестратор обеспечивает централизованный контроль оборудования, разграничение прав (RBAC), декларативное управление конфигурациями, изоляцию в контейнерах и развитый мониторинг. Но для ML-исследователей переход на «чистый» Kubernetes создает высокий порог входа: вместо лаконичного скрипта приходится писать многостраничные YAML-манифесты Pod и Job, вручную настраивать тома PersistentVolumeClaims (PVC) и сетевое взаимодействие узлов.
Открытый фреймворк SkyPilot устраняет этот разрыв. Выступая декларативным диспетчером задач, он транслирует сценарии запуска в примитивы Kubernetes и сохраняет привычный исследовательский цикл без необходимости переписывать обучающие пайплайны.
Отличие парадигм: почему прямой перенос вызывает сложности
Различие между Slurm и Kubernetes кроется в их фундаментальных моделях:
- Модель планирования: Slurm ориентирован на научные расчеты и реализует семантику gang scheduling («всё или ничего»). Если задаче требуются два узла по восемь GPU, планировщик не запустит процесс до тех пор, пока все шестнадцать ускорителей не будут одновременно выделены. Стандартный планировщик Kubernetes размещает контейнеры по мере освобождения ресурсов: он может выделить один Pod, а второй оставить в ожидании (Pending). В распределенном обучении PyTorch это приводит к блокировке части GPU и сбою инициализации по таймауту. Для решения проблемы в кластер внедряют специализированные очереди вроде Volcano или Kueue.
- Модель окружения: В Slurm окружение активируется системными модулями (
module load cuda) и виртуальными средами Python прямо на хосте. В Kubernetes среда строго изолирована внутри Docker-образа. - Файловая система: Slurm предоставляет общую точку монтирования NFS/Lustre с поддержкой POSIX на всех узлах. В Kubernetes сетевой том необходимо явно объявлять через PersistentVolume.
Анатомия манифеста: от Slurm-скрипта к task.yaml
SkyPilot предлагает декларативный формат описания задачи task.yaml. Вместо сложных структур Kubernetes Job разработчик описывает необходимые ресурсы, подготовку окружения (setup) и команду запуска (run).
Сравним классический batch-скрипт Slurm и манифест SkyPilot для распределенного обучения Llama:
Скрипт Slurm (train.sh):
#!/bin/bash
#SBATCH --job-name=llama-pretrain
#SBATCH --nodes=2
#SBATCH --gpus-per-node=8
module load cuda/12.4
source /shared/envs/torch/bin/activate
srun torchrun \
--nnodes=$SLURM_NNODES \
--nproc_per_node=$SLURM_GPUS_PER_NODE \
--rdzv_id=$SLURM_JOB_ID \
--rdzv_backend=c10d \
--rdzv_endpoint=$(scontrol show hostnames $SLURM_JOB_NODELIST | head -n 1):29500 \
train.py --batch-size 32
Манифест SkyPilot (task.yaml):
name: llama-pretrain
num_nodes: 2
resources:
accelerators: H100:8
infra: kubernetes
setup: |
pip install --upgrade pip
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu124
pip install -r requirements.txt
run: |
HEAD_IP=$(echo "$SKYPILOT_NODE_IPS" | head -n 1)
torchrun \
--nnodes=$SKYPILOT_NUM_NODES \
--nproc_per_node=$SKYPILOT_NUM_GPUS_PER_NODE \
--node_rank=$SKYPILOT_NODE_RANK \
--master_addr=$HEAD_IP \
--master_port=29500 \
train.py --batch-size 32
Блок setup выполняется при инициализации среды на каждом узле. Для ускорения старта команды используют базовые Docker-образы с предустановленным PyTorch и CUDA через директиву image_id.
Трансляция переменных окружения и CLI-команд
Официальная документация Migrating from Slurm to SkyPilot определяет прямое соответствие между системными переменными:
| Параметр | Slurm | SkyPilot |
|---|---|---|
| Количество узлов | $SLURM_NNODES | $SKYPILOT_NUM_NODES |
| Ранг узла (Rank) | $SLURM_NODEID / $SLURM_PROCID | $SKYPILOT_NODE_RANK |
| GPU на узел | $SLURM_GPUS_PER_NODE | $SKYPILOT_NUM_GPUS_PER_NODE |
| Список IP узлов | $SLURM_JOB_NODELIST | $SKYPILOT_NODE_IPS |
| ID задачи | $SLURM_JOB_ID | $SKYPILOT_TASK_ID |
В отличие от Slurm, где список узлов передается строкой вида node[01-02], переменная $SKYPILOT_NODE_IPS содержит простой построчный список IP-адресов. Нумерация рангов $SKYPILOT_NODE_RANK всегда начинается с нуля, где нулевой ранг выполняет роль координатора рандеву.
Сопоставление команд жизненного цикла задачи:
- Интерактивная отладка:
salloc --gpus=8→sky launch -c dev-node --gpus H100:8. - Запуск команды на узле:
srun python train.py→sky exec dev-node python train.py. - Пакетная отправка:
sbatch script.sh→sky jobs launch task.yaml. - Просмотр очереди:
squeue→sky statusилиsky jobs queue. - Отмена задачи:
scancel <id>→sky jobs cancel <id>илиsky down dev-node.
Организация дисковой подсистемы: SkyPilot Volumes
В суперкомпьютере датасеты доступны по единому пути /shared/datasets. В Kubernetes локальная файловая система пода эфемерна. Для создания общего сетевого хранилища с поддержкой параллельной записи (ReadWriteMany) SkyPilot использует декларативные тома поверх PVC:
name: shared-checkpoints
type: k8s-pvc
infra: kubernetes
size: 500Gi
config:
access_mode: ReadWriteMany
Применение тома выполняется вызовом sky volumes apply -f volume.yaml, после чего в task.yaml подключается директива:
volumes:
/mnt/checkpoints: shared-checkpoints
Для существующего NFS-сервера параметры монтирования указываются через pod_config. Локальный код синхронизируется директивой workdir: ..
Пошаговый регламент миграции и проверка
Для безопасного переноса пайплайнов рекомендуется следовать регламенту:
- Инвентаризация скрипта: Зафиксировать версии CUDA, зависимости Python, сетевые порты и параметры
torchrun. - Формирование окружения: Подготовить образ или команды в блоке
setup. - Настройка томов: Создать манифесты SkyPilot Volumes для чекпоинтов и датасетов.
- Одиночный smoke-тест: Запустить тестовую сессию на одном узле:
Внутри проверить вывод
sky launch -c test-node --gpus 1 task-single.yamlnvidia-smi, доступность каталога/mnt/checkpointsи импорт библиотек в Python. - Проверка распределенного запуска: Запустить двухнодовый тест на 2–3 шага обучения, проверив логи инициализации через
sky logs <job_id>. - Пакетный запуск: Отправить задачу через
sky jobs launch task.yamlи контролировать ход обучения черезsky jobs queueи SkyPilot Dashboard.
Эксплуатационные компромиссы и безопасность
- Очереди и квоты: Для исключения монополизации GPU кластер Kubernetes необходимо оснастить механизмом Kueue, гарантирующим соблюдение Fair Share и очередей между командами.
- Безопасность секретов: API-ключи и пароли к хранилищам данных запрещено указывать в
task.yamlоткрытым текстом — их следует передавать через переменные окружения изk8s secrets. - Производительность I/O: Сетевые PVC могут уступать локальным дискам HPC. Оптимальная практика — кэшировать датасеты на локальных NVMe-накопителях узлов, сохраняя на сетевой том только контрольные точки.
Использование SkyPilot позволяет объединить гибкость Kubernetes с продуктивностью HPC-инструментов, сохраняя проверенные алгоритмы обучения.

