В рамках проекта SODALITE H2020 одно из направлений, на котором мы фокусируемся, - это развертывание оптимизированных контейнеров с моделями машинного обучения для конкретных ускорителей для пограничного логического вывода между гетерогенными пограничными шлюзами в парке подключенных транспортных средств. Для этого существует тройная мотивация:

  • Хотя мы можем извлечь выгоду из ресурсов облака и высокопроизводительных вычислений для обучения базовой модели (например, TensorFlow), по-прежнему требуются адаптации для конкретных ускорителей (например, подготовка производных моделей TFLite для выполнения на графическом процессоре или EdgeTPU).
  • Жизненный цикл транспортного средства намного превышает жизненный цикл конкретной облачной службы, а это означает, что мы не можем делать предположений о среде, в которой мы развертываем с течением времени.
  • Различные службы могут иметь более сильную потребность в конкретном типе ускорителя (например, GPU, FPGA), а это означает, что может потребоваться перепланирование существующей службы и ее повторное развертывание на другом доступном ресурсе на наиболее подходящей основе.

Маркировка узлов в Kubernetes

Несмотря на то, что существовала работа, посвященная маркировке функций узлов в гетерогенных кластерах Kubernetes, наиболее заметным из которых является официальный проект обнаружение функций узлов, поддержка пограничных шлюзов, которые обычно представляют собой встроенные SBC (обычно либо в виде автономного черного ящика, либо как часть ранее существовавшей бортовой информационно-развлекательной системы (IVI)) оказался в некоторой степени недостаточным. В случае официального подключаемого модуля устройства NVIDIA GPU, например, для обнаружения графического процессора требуется использование библиотеки управления NVIDIA (NVML), которая, в свою очередь, предполагает наличие перечисляемой шины PCI. Поэтому пользователям Jetson Nano со встроенным графическим процессором в настоящий момент просто не повезло. Другие, такие как Coral Dev Board, обеспечивают перечисление EdgeTPU через шину PCI, но пока не предоставляют специальный плагин для управления и доступа к устройству EdgeTPU. В обоих случаях мы можем обойти эти ограничения, пометив узел с помощью свойств / возможностей устройства, зависящих от платформы, и развернув контейнер с целевой средой выполнения, зависящей от ускорителя.

Войти в дерево устройств

Общей чертой большинства этих пограничных шлюзов является наличие полустандартного blob-объекта дерева устройств (DTB), который предоставляет статическое описание оборудования и соответствующей топологии через специальную древовидную структуру. В то время как исчерпывающее объяснение дерева устройств выходит за рамки этой статьи, те, кто так склонен, могут прочитать спецификацию здесь. Общий обзор дерева устройств и его структуры node выглядит следующим образом:

В случае с Jetson Nano это выглядит следующим образом, где / представляет вершину дерева или корневой узел:

/dts-v1/;
/ {
        nvidia,fastboot-usb-pid = <0xb442>;
        compatible = "nvidia,jetson-nano", "nvidia,tegra210";
        nvidia,proc-boardid = "3448";
        nvidia,pmu-boardid = "3448";
        serial-number = "xxxxxxxxxxxxxxxxx";
        nvidia,dtbbuildtime = "Jul 16 2019", "17:09:35";
        model = "NVIDIA Jetson Nano Developer Kit";
        ...
        gpu {
                compatible = "nvidia,tegra210-gm20b", "nvidia,gm20b";
                access-vpr-phys;
                resets = <0x21 0xb8>;
                status = "okay";
                interrupts = <0x0 0x9d 0x4 0x0 0x9e 0x4>;
                reg = <0x0 0x57000000 0x0 0x1000000 0x0 0x58000000 0x0 0x1000000 0x0 0x538f0000 0x0 0x1000>;
                iommus = <0x2b 0x1f>;
                reset-names = "gpu";
                nvidia,host1x = <0x78>;
                interrupt-names = "stall", "nonstall";
        };
        ...

в то время как в общем случае свойство model корневого узла должно предоставить нам уникальный идентификатор для конкретной модели системной платы в стандартном формате manufacturer,model, в спецификации, к сожалению, выбирается простой выход и только рекомендует стандартный формат. Такое размытие спецификации означает, что мы, к сожалению, не можем использовать свойство model в качестве последовательного источника для маркировки узлов и вместо этого должны использовать свойства compatible - хотя спецификация и здесь не содержит твердых требований, они находятся на наименее вынужден неявно принимать стандартное соглашение, чтобы соответствовать соглашениям об именах ядра Linux.

Создание меток узлов из свойств DeviceTree

Чтобы сгенерировать метки узлов из свойств DeviceTree, мы разработали специальный контроллер Kubernetes специально для этой цели:



Учитывая отсутствие согласованности model encoding, как упоминалось выше, подход, используемый нашим устройством для маркировки узлов DeviceTree, состоит в том, чтобы перебирать свойства compatible в корневом узле, а также любых назначенных дочерних элементах, для которых мы хотим предоставить метки, например, в случае с Jetson Nano узел gpu. Простой пробный запуск узла с определенными интересующими дочерними элементами демонстрирует теги, которые будут сгенерированы:

$ k8s-dt-node-labeller -d -n gpu
Discovered the following devicetree properties:
beta.devicetree.org/nvidia-jetson-nano: 1
beta.devicetree.org/nvidia-tegra210: 1
beta.devicetree.org/nvidia-tegra210-gm20b: 1
beta.devicetree.org/nvidia-gm20b: 1

Развертывание маркировщика узлов в гетерогенном кластере

Сам маркировщик узлов предназначен для развертывания в гетерогенном кластере как DaemonSet. Общий обзор этикетировочной машины в действии показан ниже:

Он предназначен для развертывания непосредственно в кластере с использованием примера спецификации DaemonSet в дереве:

$ kubectl apply -f k8s-dt-labeller-ds.yaml

Целевое размещение пакета

После того, как этикетировщик запущен и работает, теперь можно настроить таргетинг на определенные шлюзы или пары шлюз + ускоритель. Например, для нацеливания на Jetson Nano можно использовать метку beta.devicetree.org/nvidia-jetson-nano для конкретной модели в качестве основы для выбора узла. Для нацеливания на конкретный графический процессор можно использовать beta.devicetree.org/nvidia-gm20b. Чтобы еще больше ограничить выбор, можно использовать несколько меток вместе для определения основы выбора.

Используя эхо-сервер HTTP в качестве простого примера развертывания, описание целевого модуля для Jetson Nano с графическим процессором GM20B можно записать следующим образом:

Что затем может быть показано с помощью простого определения службы, как показано ниже:

Далее мы открываем порт извне для тестирования вне кластера:

$ kubectl port-forward service/http-echo-service 5678:5678

и продемонстрируем подключение к соответствующему узлу:

$ curl -X GET localhost:5678
hello from a Jetson Nano with an NVIDIA GM20B GPU

Следующие шаги

Конкретные базовые среды выполнения контейнеров для различных типов ускорителей готовятся отдельно и будут доступны на более поздних этапах проекта SODALITE.

Чтобы увидеть пример начала работы со средой выполнения контейнера NVIDIA GPU, нацеленной на Jetson Nano, обратитесь к официальному руководству NVIDIA здесь. Планирование Pod в кластере может выполняться с использованием вышеупомянутых критериев выбора узла и шаблона Pod.

Ограничения

  • Чтобы пройти по дереву устройств, необходим доступ к каталогу /sys/firmware узла - в настоящее время это можно сделать, запустив Pod в режиме privileged. Можно использовать allowedProcMountTypes для отключения маскировки пути внутри модуля и запуска без привилегированного режима, но это еще не было проверено.
  • В настоящее время не существует механизма, с помощью которого DaemonSet мог бы корректно завершить работу без запуска перезапуска, из-за того, что Pod RestartPolicy вынужден Always в спецификациях DaemonSet Pod. Это означает, что в настоящий момент селектор начального узла для DaemonSet должен ограничиваться узлами, о которых известно, что они поддерживают DeviceTree, чтобы избежать ложных перезапусков. Это еще не было проблемой при нацеливании в первую очередь на цели arm64 и armhf, но может быть проблематичным для других архитектур.
  • Хотя этикетировщик может подтвердить существование узла в дереве устройств, он не предлагает подробной информации или элементов управления для конкретного устройства - все это необходимо реализовать с помощью специального плагина устройства (или встроить в среду выполнения контейнера. - как и в случае с GPU). Однако этикетировщик можно использовать в качестве основы для планирования подключаемых модулей устройства на узлах с соответствующими возможностями.