Задача, отправленная в SLURM командой sbatch, падает с ошибкой вида «command not found» или «container not found» — это самый частый сценарий, когда пользователь впервые совмещает Singularity (ныне Apptainer) с планировщиком задач на вычислительном кластере. Причина почти всегда одна: скрипт рассчитан на интерактивную сессию, а планировщик запускает его в другом окружении, где переменные, пути и даже смонтированные каталоги отличаются.
Ниже разберём, как устроено взаимодействие контейнеров Singularity/Apptainer с планировщиками задач, как правильно оформить batch-скрипт, какие директивы проверить в первую очередь и как диагностировать типовые сбои. Материал ориентирован на кластеры под управлением SLURM — самого распространённого планировщика в HPC-среде, — но общие принципы применимы и к PBS/Torque, и к LSF.
Почему Singularity и планировщик задач дополняют друг друга
На вычислительных кластерах планировщик (workload manager) решает две задачи: распределяет ресурсы между пользователями и ставит задания в очередь, когда свободных узлов нет. Контейнер же решает другую проблему — воспроизводимость окружения: все библиотеки, зависимости и версии ПО упакованы в один файл образа .sif.
Именно поэтому связка «планировщик + контейнер» стала стандартом в научных вычислениях. Singularity/Apptainer изначально проектировался для HPC: в отличие от Docker, он не требует root-демона, запускается от обычного пользователя и нативно интегрируется с планировщиками — переменные окружения задания, MPI-ранк и лимиты ресурсов прозрачно передаются внутрь контейнера.
Важно понимать разделение ролей: планировщик ничего не знает о содержимом контейнера. Он выделяет узлы, ядра и память, а уже внутри выделенного задания вы вызываете singularity exec или apptainer run. Отсюда следует главное правило: все ресурсные параметры задаются директивами планировщика, а не опциями контейнера.
Подготовка: что проверить перед отправкой задания
Прежде чем писать batch-скрипт, убедитесь, что базовые компоненты доступны на вычислительных узлах, а не только на login-узле. Это разные машины, и ПО на них может отличаться.
- 🔍 Проверьте наличие команды:
which singularityилиwhich apptainer— на многих кластерах модуль загружается черезmodule load singularityилиmodule load apptainer. - 📁 Убедитесь, что файл образа
.sifлежит на общей файловой системе, видимой с вычислительных узлов (обычно это$HOMEили scratch-раздел, а не локальный диск login-узла). - 🧪 Выполните пробный запуск в интерактивной сессии на вычислительном узле, если кластер это позволяет: так вы отделите ошибки контейнера от ошибок планировщика.
- 📜 Уточните в документации вашего кластера допустимые директивы, имена очередей (партиций) и лимиты — они различаются между установками.
⚠️ Внимание: сборку образа (singularity buildс sudo или через fakeroot) на login-узлах кластеров часто запрещают политикой безопасности. Собирайте образ локально или через удалённый билдер, а на кластер переносите готовый.sif-файл.
Структура batch-скрипта для SLURM
Типовой скрипт задания состоит из двух частей: директив #SBATCH в начале и команд запуска ниже. Директивы читает планировщик, команды выполняются уже на выделенном узле.
#!/bin/bash
#SBATCH --job-name=sing_test
#SBATCH --nodes=1
#SBATCH --ntasks=4
#SBATCH --time=01:00:00
#SBATCH --output=job_%j.out
module load apptainer
singularity exec --bind /scratch/$USER:/data \
/home/$USER/images/myimage.sif \
python /data/script.py
Разберём ключевые моменты. Директива --ntasks определяет число процессов, которое выделит планировщик; внутри контейнера ваша программа должна использовать именно эти ресурсы, а не пытаться занять все ядра узла. Опция --bind монтирует каталог с данными внутрь контейнера — без неё программа в контейнере просто не увидит входные файлы.
Отправка задания выполняется стандартно:
sbatch myjob.sh
Статус смотрят через squeue -u $USER, а после завершения анализируют выходной файл job_<id>.out — туда попадают и сообщения об ошибках контейнера.
☑️ Проверка batch-скрипта перед отправкой
Интерактивный и пакетный режимы: в чём разница
Многие сначала отлаживают задачу в интерактивной сессии — например, через srun --pty bash или аналогичную команду, принятую на конкретном кластере. Внутри такой сессии контейнер запускается точно так же, как и в batch-скрипте, но есть тонкость: интерактивная сессия наследует переменные вашего логина, а пакетное задание стартует с более «чистым» окружением.
Отсюда типичный симптом: «в интерактиве работает, через sbatch — нет». Возможные причины — незагруженный модуль, отсутствие переменной в .bashrc (который в пакетном режиме может не выполняться) или относительные пути вместо абсолютных. Лечение простое: всё необходимое окружение задавайте явно внутри скрипта задания, а пути пишите абсолютными.
MPI и многозадачные задания внутри контейнера
Для параллельных вычислений применяется модель «гибридного MPI»: библиотека MPI установлена и в контейнере, и на хост-системе, а планировщик запускает процессы через srun или mpirun снаружи контейнера. Схематично это выглядит так:
srun singularity exec mpi_app.sif /opt/app/mpi_program
Здесь критична совместимость версий MPI внутри и снаружи контейнера: при существенном расхождении задание может зависнуть или упасть с ошибками транспорта. Точные требования зависят от используемой реализации (Open MPI, MPICH, Intel MPI) и настроек кластера — сверяйтесь с документацией вашей системы и администраторами.
Для заданий на GPU добавляется флаг --nv (для NVIDIA), который пробрасывает драйвер и устройства внутрь контейнера. Выделение GPU при этом запрашивается директивой планировщика, например #SBATCH --gres=gpu:1 — синтаксис может отличаться в зависимости от конфигурации кластера.
Типичные ошибки и их диагностика
Большинство сбоев при связке Singularity с планировщиком относятся к нескольким повторяющимся классам. Таблица ниже помогает быстро сузить поиск.
| Симптом | Вероятная причина | Что проверить |
|---|---|---|
| singularity: command not found | Модуль не загружен в скрипте задания | Строка module load до вызова контейнера |
| container not found / no such file | Образ недоступен с вычислительного узла | Расположение .sif на общей ФС, абсолютный путь |
| Программа не видит входные файлы | Каталог не примонтирован | Опция --bind и пути внутри контейнера |
| Задание убито по лимиту | Превышено время или память | Директивы --time, --mem, логи планировщика |
| MPI-задание зависает | Несовместимость MPI внутри и снаружи | Версии MPI, рекомендации администраторов |
⚠️ Внимание: не пытайтесь обойти лимиты планировщика, запуская внутри контейнера больше процессов, чем запрошено директивами. На многих кластерах такие задания принудительно завершаются, а аккаунт может получить ограничения.
Если ошибка неочевидна, запустите контейнер с повышенной детализацией: singularity --debug exec .... Вывод покажет, какие каталоги монтируются, какие переменные передаются и на каком этапе происходит сбой.
Переменные окружения внутри контейнера
По умолчанию Singularity передаёт в контейнер большинство переменных окружения хоста, добавляя префикс SINGULARITYENV_ для явного проброса. Например, SINGULARITYENV_FOO=bar сделает переменную FOO доступной внутри контейнера. В Apptainer аналогичный префикс — APPTAINERENV_. Планировщик SLURM со своей стороны экспортирует переменные вида SLURM_JOB_ID, SLURM_NTASKS — их можно читать внутри контейнера, чтобы программа узнала параметры задания.
Переносимость между кластерами и версиями
Один из главных плюсов контейнерного подхода — образ .sif можно переносить между кластерами без пересборки. Но batch-скрипт при этом почти наверняка придётся адаптировать: имена партиций, лимиты, система модулей и пути к файловым системам у каждой установки свои.
Также учитывайте переименование проекта: новые установки используют команду apptainer, старые — singularity. Скрипт, жёстко привязанный к одному имени, на другом кластере не запустится. Надёжный приём — проверять доступность команды в начале скрипта или использовать переменную для имени исполняемого файла.
FAQ: частые вопросы
Можно ли отправить контейнер в очередь одной командой без скрипта?
Да, sbatch принимает команду через --wrap, например: sbatch --wrap="singularity exec image.sif python script.py". Для одноразовых запусков это удобно, но для воспроизводимости лучше хранить скрипт задания в файле.
Нужен ли root для запуска контейнера на кластере?
Нет. Singularity/Apptainer спроектирован для запуска от обычного пользователя — это одно из ключевых отличий от Docker и причина его распространения в HPC. Root может понадобиться только для сборки образа, что обычно делается вне кластера.
Почему задание работает на login-узле, но падает через sbatch?
Наиболее вероятная причина — различия в окружении: незагруженный модуль, переменные из .bashrc, относительные пути. Пропишите в скрипте задания явную загрузку модулей и абсолютные пути, затем сравните вывод команды env в обоих режимах.
Как передать переменную окружения внутрь контейнера?
Используйте префикс SINGULARITYENV_ (или APPTAINERENV_ для Apptainer) перед именем переменной, либо флаг --env в новых версиях. Обычные переменные хоста часто пробрасываются автоматически, но явный способ надёжнее и переносимее.
Чем Apptainer отличается от Singularity при работе с планировщиком?
С точки зрения пользователя планировщика — практически ничем: это развитие одного проекта, команды и флаги в основном совпадают, меняется имя исполняемого файла и префиксы переменных. Скрипты, как правило, переносятся с минимальными правками.