Docker на кластере

Введение

Технология Docker позволяет создавать виртуальную среду исполнения. По сравнению с виртуальными машинами накладные расходы на виртуализацию составляют менее 1%.

С точки зрения пользователя данная технология даёт возможность настраивать среду исполнения и воспроизводить ее на разных компьютерах. Внутри контейнера у пользователя имеются права суперпользователя, что позволяет устанавливать необходимые пакеты без привлечения администратора кластера. Кроме того, Docker может работать с графическими ускорителями, что является очень полезным, например, при работе с библиотеками глубинного обучения.

Стандартная реализация Docker, позволяет пользователю получить права суперпользователя на хостовой машине, что неприемлемо с точки зрения безопасности. Поэтому на кластере используются альтернативная реализация — podman, которая устраняет данный недостаток. Podman имеет ряд особенностей, о которых будет написано ниже.

Для общего понимания технологии Docker можно ознакомиться с шестью статьями на Хабре.

Файлы и каталоги

Пользовательская конфигурация podman хранится в каталоге ~/.config/container. Стандартное размещение данных— ~/.local/share/container, но, поскольку podman не совместим с файловой системой NFS, то на кластере для хранения образов используется локальный каталог /tmp. Это значит, что копии каждого используемого образа будут создаваться по мере необходимости в каталоге /tmp на каждом вычислительном узле. Соответственно, не стоит использовать копирование входных/выходных данных между контейнером и домашним каталогом. Лучше использовать монтирование домашнего каталога внутрь образа при запуске с помощью опции -v $HOME:$HOME

Конфигурация хранилища данных для каждого пользователя кластера описана в ~/.config/containers/storage.conf и имеет вид

[storage]
  driver = "vfs"
  runroot = "/tmp/u9999_run"
  graphroot = "/tmp/u9999_storage"
  [storage.options]
    mount_program = ""

где u9999 надо заменить на свой логин

Запуск готовых образов из публичных реестров

В конфигурации podman на кластере "Уран" в файле /etc/containers/registries.conf подключены публичные реестры registry.access.redhat.com, docker.io, registry.fedoraproject.org, quay.io, registry.centos.org и локальный реестр 172.16.0.1:5000. Если требуется добавить свое хранилище или убрать поиск в вышеперечисленных, то можно скопировать конфигурационный файл в свой каталог ~/.config/container/ и отредактировать копию.

Подготовка и сборка своего образа

На одном из компьютеров кластера по адресу 172.16.0.1:5000 организован локальный Docker-реестр, который может быть использован пользователями кластера для хранения своих образов. ВНИМАНИЕ. Доступ к реестру осуществляется без авторизации и поэтому есть небольшой риск, что кто-то испортит или удалит ваш образ.

При подготовке образа рекомендуется предварительно произвести сборку на локальной машине и убедится в корректности Dockerfile. Сборка контейнера с использованием podman происходит дольше, чем с использованием стандартного docker. Поэтому желательно осуществлять сборку контейнеров на узлах кластера с помощью планировщика SLURM. Для этого можно выполнить команду:

srun -c 8 --mem 10000 --pty bash

В результате мы попадём на один из вычислительных узлов, на котором можно выполнять сборку. Команда сборки запускается из каталога, где находится Dockerfile и должна иметь примерно следующий вид:

docker build -t <tag> <directory>

Пример

docker build -t hello_docker .

Важно понимать, что после сборки контейнер будет находиться в каталоге /tmp на узле, на котором производилась сборка. Чтобы контейнер можно было использовать на других узлах, его необходимо скопировать в общественное хранилище на кластере с помощью следующей команды:

docker push --tls-verify=false localhost/hello_docker 172.16.0.1:5000/hello_docker

Также обе команды можно выполнить за одной задачей, выполнив:

srun -c 8 --mem=10000 bash -c 'docker build -t hello_docker . && docker push --tls-verify=false localhost/hello_docker 172.16.0.1:5000/hello_docker

ВАЖНО. Так как на общественном хранилище не настроена авторизация, то при создании контейнеров необходимо использовать уникальные имена, например, используя в качестве префикса имя пользователя.

Минимальный Dockerfile приведен в приложении 1. Bash скрипт для сборки и загрузки контейнера в общее хранилища приведен в приложении 2.

Запуск образа

Запуск осуществляется следующей командой:

srun -c 8 --mem=10000 docker run -v ~:/work -t hello_docker ls /work

если образ на узле отсутствует, то он будет подтянут из реестра автоматически. При следующих запусках на том же узле будет повторно использована сохраненная на этом узле копия образа.

В данном случае опция -v ~:/work указывает на то, что домашний каталог пользователя будет смонтирован в каталог /work внутри контейнера. Если в вашей программе или скриптах используется путь к домашнему каталогу, то можно попробовать другой вариант -v $HOME:$HOME, который смонтирует домашний каталог в контейнере под тем же именем, под которым он виден вне контейнера.

Вместо команды ls /work может присутствовать любая другая команда с параметрами.

При запуске можно добавить в образ дополнительные библиотеки:

srun -c 8 --mem=10000 docker run -v ~:/work -v /common/user/gpu_libs:/gpu_libs -e LD_LIBRARY_PATH=/gpu_libs -t hello_docker ls /work

Bash скрипт для запуска контейнера с использованием GPU приведен в приложении 3.

Приложение 1. Минимальный Dockerfile

FROM ubuntu:18.04
VOLUME /work
WORKDIR /work
CMD ['ls']

Приложение 2. Bash скрипт для сборки контейнера

CONTAINER_NAME=hello_docker
docker build -t $CONTAINER_NAME .
docker push --tls-verify=false localhost/$CONTAINER_NAME 172.16.0.1:5000/$CONTAINER_NAME

Приложение 3. Bash скрипт для запуска контейнера

CONTAINER_NAME=hello_docker
docker pull --tls-verify=false 172.16.0.1:5000/$CONTAINER_NAME
CUDA=""
if [ -e /dev/nvidiactl ]; then
  DEVICES=(/dev/nvidia*)
  CUDA=${DEVICES[@]/#/--device }
fi
docker run $CUDA -v ~:/work -t $CONTAINER_NAME ls /work

Приложение 4. Пример сообщений при загрузке образа

$> srun -n 1 --mem=10000  --output=quad.txt docker run -v $HOME:$HOME -ti tkralphs/coinor-optimization-suite cbc $HOME/coin-or/Cbc/examples/quad.mps
$> less quad.txt
Trying to pull 172.16.0.1:5000/tkralphs/coinor-optimization-suite...
  manifest unknown: manifest unknown
Trying to pull registry.access.redhat.com/tkralphs/coinor-optimization-suite...
  name unknown: Repo not found
Trying to pull docker.io/tkralphs/coinor-optimization-suite...
Getting image source signatures
Copying blob sha256:14f491cdf5e5dfff3f9296e85ea5f0ed7ed792a0df58fd753e5a53aa72dda67f
Copying blob sha256:53e3366ec435596bed2563cc882ba47ec25df6be2b1027e3243e83589c667c1e
Copying blob sha256:ee690f2d57a128744cf4c5b52646ad0ba7a5af113d9d7e0e02b62c06d35fd14c
Copying blob sha256:e92ed755c008afc1863a616a5ba743b670c09c1698f7328f05591932452a425f
Copying blob sha256:c596d3b2f1a98fc3d58eb98ff4de80a9bd7c50f8118e89189ec25c1b4c9678cb
Copying blob sha256:b9fd7cb1ff8f489cf082781b0e1fe0c13b840e20147e8fc8204b4592da7c2f70
Copying blob sha256:891eb02d766a3e5408f240128347472983067be41493f1a10cf6216574f614f0
Copying blob sha256:42f559670afb72a15465e2a0785122587c284f0cbfb7b7883bc0f27e6e999a32
Copying blob sha256:87ef252adc7d4cab8eae16d70cc529d70c857d964309e13c3a9d64b6b49670d8
Copying blob sha256:8f85d5b3b6ecc7367bb8186917017bcba3d916c08ffcea240b896b2e562fe0fa
Copying blob sha256:38e446e2e1f2ec785a4ad5e49af8cfcd34027a915c3b5d40732eeb65f66b5019
Copying config sha256:c9697bd9dae0a80bf39e3feca37bc7b4ef9a6efa03269870cb7d15f51426999c
Writing manifest to image destination
Storing signatures
....