Kubernetes последовательно захватывает все новые ниши для декларативного описания ожидаемого состояния и теперь ресурсами Kubernetes можно управлять облачными провайдерами (например, через Crossplane), создавать и масштабировать функции (KNative) и многим другим. И кажется интересной идея конфигурирования через Kubernetes физических устройств, имеющих механизм удаленного управления и отправки информации о текущем состоянии. В CNCF был зарегистрирован проект (сейчас находится в sandbox) Akri, который предлагает модель унифицированного управления устройствами умного дома и в этой статье мы рассмотрим основные аспекты конфигурирования Akri на примере udev и OPC UA.

Akri определяет два типа ресурсов: описание устройства и описание экземпляра Akri для управления устройством. Для обнаружения устройств Akri поддерживает протоколы udev (универсальный механизм обнаружения для Linux), ONVIF (для обнаружения IP-камер), OPC UA (промышленный стандарт для взаимодействия с датчиками и управляющими устройствами), в работе поддержка Bluetooth, LoRaWAN, CoAP и других, при этом можно создать собственное расширение для обнаружения устройств по специальному протоколу, который описан в документации на основе шаблона на Rust. Мы будем использовать udev и будем обнаруживать существующую веб-камеру на компьютере, но разумеется здесь можно было бы использовать любую другую конфигурацию (например, управлять GPU или другими устройства, в том числе подключенными через USB).

Для установки будем использовать Helm, это поможет сразу настроить необходимую конфигурацию обнаружения. Для получения информации о состоянии pod будет использоваться crictl, которая доступна по умолчанию в Kubernetes 1.24+. Важно при установке указать расположение socket-файла для cri в переменной agent.host.containerRuntimeSocket (например, при установке с Containerd расположение будет /run/containerd/contained.sock, для Docker + Mirantis cri-dockerd: /run/cri-dockerd.sock)

helm repo add akri-helm-charts https://project-akri.github.io/akri/
helm install akri akri-helm-charts/akri \
     --set agent.host.containerRuntimeSocket=/run/cri-dockerd.sock \
     --set udev.discovery.enabled=true \
     --set udev.configuration.enabled=true \
     --set udev.configuration.name=akri-udev-video \
     --set udev.configuration.discoveryDetails.udevRules[0]='KERNEL=="video[0-9]*"' \
     --set udev.configuration.brokerPod.image.repository="ghcr.io/project-akri/akri/udev-video-broker"

При установке мы указываем способ обнаружения устройств, дополнительные настройки для поиска устройств (здесь мы ищем все доступные видеоустройства в /dev). Также можно разрешить все доступные способы обнаружения через переменную agent.full=true. Для мониторинга в Prometheus можно разрешить экспорт метрик через prometheus.enabled=true, среди которых будет кроме обычных замеров rust-приложений значения количества обнаруженных устройств (akri_instance_count), запущенных брокеров (akri_broker_pod_count), а также метрики от брокеров (например, akri_frame_count от брокера udev-video).

При использовании сетевых методов обнаружения можно указывать список ip или MAC-адресов для отбора устройств и анонсированные возможности (например, scopes в ONVIF). Более подробно примеры конфигурации можно посмотреть в документации. Конфигурация discovery создает ресурс с типом Configuration (apiVersion: akri.sh/v0) со следующей спецификацией:

spec:
  brokerProperties: {}
  brokerSpec:
    brokerPodSpec:
      containers:
      - image: ghcr.io/project-akri/akri/udev-video-broker:latest
        name: akri-udev-video-broker
        securityContext:
          privileged: true
  capacity: 1
  configurationServiceSpec:
    ports:
    - name: grpc
      port: 80
      protocol: TCP
      targetPort: 8083
    type: ClusterIP
  discoveryHandler:
    discoveryDetails: |
      udevRules:
      - KERNEL=="video[0-9]*"
    name: udev
  instanceServiceSpec:
    ports:
    - name: grpc
      port: 80
      protocol: TCP
      targetPort: 8083
    type: ClusterIP

В спецификации указывается образ контейнера для брокера, который будет использоваться для взаимодействия с устройствами, и настройки discovery, которые применяются в discovery-daemonset для обнаружения новых устройств. Кроме пода обнаружения на узлы устанавливается агент, который реализует протокол Device Plugins для уведомления о доступных устройствах, устанавливается через DaemonSet на все узлы кластера. Для обнаруженных устройств создается экземпляр ресурса Instance (apiVersion: akri.sh/v0), который содержит информацию о доступе к устройству для брокера (brokerProperties). Также в brokerProperties может содержаться информация о настройках устройствах (например, частота кадров для камеры). Агент отслеживает изменения в списке устройств и соответственно создает, изменяет или удаляет ресурсы Instance.

apiVersion: akri.sh/v0
kind: Instance
metadata:
  name: akri-udev-video-db97e3
  namespace: default
spec:
  brokerProperties:
    UDEV_DEVNODE: /dev/video0
  configurationName: akri-udev-video
  deviceUsage:
    akri-udev-video-db97e3-0: mynotebook
  nodes:
  - mynotebook
  shared: false

Брокер может изменять состояние устройства при изменениях в brokerProperties, а также предоставлять методы для получения информации от устройства через REST, gRPC или другие протоколы. Для udev-video-broker можно использовать клиент, который отображает полученные кадры в веб-интерфейсе:

kubectl apply -f https://raw.githubusercontent.com/project-akri/akri/main/deployment/samples/akri-video-streaming-app.yaml
PORT=`kubectl get service/akri-video-streaming-app --output=jsonpath='{.spec.ports[?(@.name=="http")].nodePort}'`
open http://localhost:$PORT

Исходные тексты приложения можно посмотреть здесь. Приложение запрашивает кадры с опубликованного grpc-сервиса (связан с соответствующим Instance и может быть найден по имени Instance и суффиксу -svc, grpc опубликован на порт 80), описание протокола выглядит следующим образом:

syntax = "proto3";

option csharp_namespace = "Camera";

package camera;

service Camera {
  rpc GetFrame (NotifyRequest) returns (NotifyResponse);
}

message NotifyRequest {
}

message NotifyResponse {
  bytes frame = 1;
  string camera = 2;
}

Рассмотрим теперь более сложный пример с использованием OPC UA. Для симуляции устройства будем использовать https://github.com/flopach/opc-ua-sensor-simulator. Установим симулятор и попробуем подключиться к нему через свободный клиент:

git clone https://github.com/flopach/opc-ua-sensor-simulator
apt install libxslt-dev libxml2-dev libffi-dev
cd opc-ua-sensor-simulator
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt install python3.8 python3.8-distutils
sudo rm /usr/bin/python
sudo ln -s /usr/bin/python3.8 /usr/bin/python
pip3 install -r requirements.txt
unzip sensor.csv.zip
sed -i 's/127.0.0.1/0.0.0.0/ig' opc-ua-server.py
python opc-ua-server.py

Установим свободный клиент:

snap install --edge opcua-client

Для получения информации о доступных устройствах и замеров датчиков подключимся к localhost:4840 и получим возможность просмотра списка зарегистрированных поставщиков данных и значения температуры и давления.

Теперь настроим подключение Akri к серверу. Для этого создадим конфигурацию обнаружения:

apiVersion: akri.sh/v0
kind: Configuration
metadata:
  name: akri-opcua
  namespace: default
spec:
  brokerProperties: {}
  brokerSpec:
    brokerPodSpec:
      containers:
      - image: nginx:latest
        name: akri-opcua-broker
  capacity: 1
  configurationServiceSpec:
    ports:
    - name: grpc
      port: 80
      protocol: TCP
      targetPort: 8083
    type: ClusterIP
  discoveryHandler:
    discoveryDetails: "opcuaDiscoveryMethod: \n  standard:\n    discoveryUrls:\n    -
      opc.tcp://IP:4840/opcua/\napplicationNames:\n  action: Exclude\n
      \ items: []\n"
    name: opcua
  instanceServiceSpec:
    ports:
    - name: grpc
      port: 80
      protocol: TCP
      targetPort: 80
    type: ClusterIP

Вместо IP здесь необходимо подставить соответствующий внешний адрес узла. Для каждого обнаруженного устройства будет создан брокер (в нашем случае сервер nginx). Для разработки собственного брокера (например, получения температуры с датчиков) можно использовать свободные реализации gRPC и OPC UA (например, topic и opcua в crates для Rust, grpc и asyncua в python). Во второй части статьи мы рассмотрим пример обработки данных с датчика температуры через протокол спецификации OPC UA и их извлечения в веб-интерфейсе с построением графика истории изменения температуры (также будем использовать возможности обнаружения Akri), а также поговорим о возможностях интеграции системы обнаружения со свободными системами умного дома Majordomo и Home Assistant.

В преддверии запуска курса "Инфраструктурная платформа на основе Kubernetes" приглашаю всех на бесплатный демоурок, в рамках которого мои коллеги расскажут как шаблонизировать манифесты Kubernetes разными способами и не только.

Комментарии (0)