Akri(アクリ)ではじめる AzureでのIoTデバイス管理

2024/05/07に公開

Akriとは

Akriは、エッジデバイス(OPC UAデバイス/カメラ/IoTセンサー/その他周辺機器など)を Kubernetes クラスター内のリソースとして公開できるようにする オープンソース プロジェクトです。Akriは、GPU や FPGA などの組み込みハードウェア リソースの公開をサポートします。Akriは、デバイスにアクセスできるノードを継続的に検出し、それらに基づいてワークロードをスケジュールします。現時点では、OPC UA/ONVIF/udevプロトコルがサポートされています。

AkriはCloud Native Computing Foundation(CNCF)のサンドボックスプロジェクトで、Microsoftが開発を主導しています。またAzure IoT Akriは、Akriの商用バージョンで、執筆時点(2024/05/07)では、プレビュー提供中です。

Akriの機能

Akri にはいくつかのコア機能が用意されています。

  • デバイスの検出
    OPC UA Serverなどネットワークエンドポイント内のリーフデバイスを検出するHandler

  • 動的プロビジョニング
    USB カメラ/OPC UA Serverなどの動的なプロビジョニング機能

今回はAkriの動作を確認するため、非常にシンプルな構成を作成して、デバイスの検出からデータ取得するまでを確認します。

Akriを使用したOPC UA Serverの管理

OPC UAは、OPC Foundationで策定されている国際標準規格で、産業用オートメーション用の通信プロトコルです。アクセス方法が標準化されることにより、異なるメーカや機器との接続時に、ベンダー特有のプログラム改変を行うことなくアクセスできる規格として開発されています。

OPC UA Serverの作成

まずは動作検証のため、OPC PLC serverを用意します。デバイスを用意するのは大変なので、Azure Container Instancesで動くOPC PLC serverを使用します。Azure Container Instancesは、Azureが提供するDockerコンテナホスティングービスです。今回は、これをオンプレに置かれたエッジサーバに見立てて利用します。

OPC PLC serverのGitHubリポジトリの[Deploy to Azure]をクリックします。

ここで、[テンプレートの編集] をクリックし、以下の通り変更します。

■ 変更前

...中略...
"command": [
  "/bin/sh",
  "-c",
  "[concat('./opcplc --pn=50000 --autoaccept --unsecuretransport --sph --wp=80 --alm --ses --gn=5 --sn=', parameters('numberOfSlowNodes'), ' --sr=', parameters('slowNodeRate'), ' --st=', parameters('slowNodeType'), ' --fn=', parameters('numberOfFastNodes'), ' --fr=', parameters('fastNodeRate'), ' --ft=', parameters('fastNodeType'), ' --ph=', variables('aciPlc'), add(copyIndex(), 1), '.', resourceGroup().location, '.azurecontainer.io')]"
],

■ 変更後

...中略...
"command": [
  "/bin/sh",
  "-c",
"[concat('./opcplc --pn=50000 --sph --fn=1 --fr=1 --ft=uint --ftl=65 --ftu=85 --ftr=True --aa --sph --ftl=65 --ftu=85 --ftr=True --ut', ' --ph=', variables('aciPlc'), add(copyIndex(), 1), '.', resourceGroup().location, '.azurecontainer.io')]"
],

このOPC PLC serverは、パラメータを設定することでさまざまなシナリオをシミュレーションできます。今回設定する値は次の通りです。

オプション 説明 今回の設定値
--sph OPC Publisher 設定ファイルをダンプする -
--fn 高速ノードの数 1
--fr 高速ノードを変更する間隔(s) 1
--ft 高速ノードのデータ型 (UInt/Double/Bool/UIntArray) uint
--aa 接続が確立されると、すべての証明書を信頼する -
--ut unsecured transportを有効にする -
--ftu 高速ノードのデータ型の上限 85
--ftl 高速ノードのデータ型の下限 65
--ftr 高速ノード値のランダム化 True
--ph PLC の完全修飾ホスト名 Azure Container InstancesのFQDN

ここで、シミュレーションしたい数をNumber Of Simulationsに設定します。今回は2に設定します。お金持ちの方は、もっと多くのサーバを立ち上げてシミュレーションをしてみてください。

[確認と作成] ボタンをクリックして、Azure Container Instancesを立ち上げます。デプロイが完了すると、 [リソースに移動] をクリックし、OPC UAサーバのエンドポイント(FQDN)を確認します。

データの確認

作成したOPC UAサーバーに接続して、データを確認します。OPC UAサーバーに接続するためのクライアントアプリケーションはいくつかありますが、今回はUAExpertを使用します。

UAExpertを起動して、OPC UAサーバーのエンドポイント(FQDN)を入力します。例えば、aci-contoso-xxx-plc1.eastus.azurecontainer.ioの場合、以下のように入力します。

opc.tcp://aci-contoso-xxx-plc1.eastus.azurecontainer.io:50000

[Connect] ボタンをクリックし、OPC UAサーバーに接続します。

テレメトリデータの確認は、[Telemetry]-[Fast]-[FastUInt1] を選択して、メインウインドウにドラッグアンドドロップします。データが更新されるたびに、値が変わっていることが確認できます。

ここでは、[NamespaceIndex]=3[value] の値を取得します。1秒間隔で65~85のランダムな値が取得されてることを確認します。2台のOPC UA Serverから温度データが上がってきていると、妄想してください。

KubernetesクラスタをAzure Arcに接続

作成したOPC UA ServerをAkriで管理するために、Kubernetesクラスタを作成し、Azure Arc-enabled Kubernetesに接続します。

Azure Arc-enabled Kubernetesは、クラウドのみならずオンプレ/エッジ環境など任意の場所で実行されているKubernetesクラスターをAzureで管理および構成するためのサービスです。Azure Arc-enabled Kubernetesに接続することで、AzureポータルやAzure CLIを使用して、クラスターの監視/更新/ポリシー適用などを行うことができます。

手順は公式サイト:クイックスタート: Azure IoT Operations プレビューを Arc 対応 Kubernetes クラスターにデプロイするの通りなので、詳しくは説明しませんが、今回はオンプレサーバに見立てた、Azure上のLinux仮想マシンにK3sをセットアップして利用します。K3sは、軽量なKubernetesディストリビューションで、IoTエッジデバイスに適しています。

次のコマンドを実行して、仮想マシンにK3sをインストールします。

curl -sfL https://get.k3s.io | sh -

mkdir ~/.kube
sudo KUBECONFIG=~/.kube/config:/etc/rancher/k3s/k3s.yaml kubectl config view --flatten > ~/.kube/merged
mv ~/.kube/merged ~/.kube/config
chmod  0600 ~/.kube/config
export KUBECONFIG=~/.kube/config
kubectl config use-context default

kubectlのシェル自動補完も設定しておきましょう。

source <(kubectl completion bash)
echo "source <(kubectl completion bash)" >> ~/.bashrc 
alias k=kubectl
complete -F __start_kubectl k

ノードの状態を確認しておきます。

k get no

NAME             STATUS   ROLES                  AGE    VERSION
iot-operations   Ready    control-plane,master   5h1m   v1.29.3+k3s1

次に、Linuxのinotify.max_user_watches および inotify.max_user_instancesの制限を増やしておきます。inotifyはLinuxでファイルの変更を検知するツールです。

echo fs.inotify.max_user_instances=8192 | sudo tee -a /etc/sysctl.conf
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
echo fs.file-max = 100000 | sudo tee -a /etc/sysctl.conf

sudo sysctl -p

これでKubernetesクラスタの準備は整いました。次に、Azure Arcに接続します。

curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
az login --use-device

変数LOCATIONにサポートされているリージョン"eastus", "eastus2", "westus", "westus2", "westus3", "westeurope", or "northeurope"のいずれかを設定します。

export LOCATION=eastus
export CLUSTER_NAME=iot-operations
export RESOURCE_GROUP=iot-operations

次に、クラスタをAzure Arcに接続します。

az connectedk8s connect \
  -n $CLUSTER_NAME \
  -l $LOCATION \
  -g $RESOURCE_GROUP

export OBJECT_ID=$(az ad sp show --id bc313c14-388c-4e7d-a58e-70017303ee3b --query id -o tsv)
echo $OBJECT_ID

az connectedk8s enable-features \
  -n $CLUSTER_NAME \
  -g $RESOURCE_GROUP \
  --custom-locations-oid $OBJECT_ID \
  --features cluster-connect custom-locations

接続が完了すると、azure-arc名前空間にリソースが生成されます。

k get ns
NAME                STATUS   AGE
kube-system         Active   8m36s
kube-public         Active   8m36s
kube-node-lease     Active   8m36s
default             Active   8m36s
azure-arc-release   Active   3m11s
azure-arc           Active   79s

Azure Arc-enabled Kubernetesそのものの説明とDeep Diveは後日あらためて。

Akriの実行

これで、Kubernetesクラスタの環境が準備できたので、OPC UAサーバーをAkriで管理します。KubernetesクラスタにAkriをデプロイするため、Helmを使用します。

まず、Helmをインストールします。

curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh

Akriのリポジトリを追加し、アップデートします。

helm repo add akri-helm-charts https://project-akri.github.io/akri/
helm repo update

HelmでAkriをデプロイします。ここで、opcua.configuration.discoveryDetails.discoveryUrls[0]opcua.configuration.discoveryDetails.discoveryUrls[0]は、Azure Container Instancesで立てたOPC UAサーバーのエンドポイントに変更してください。

helm install akri akri-helm-charts/akri \
  --set opcua.discovery.enabled=true \
  --set opcua.configuration.enabled=true \
  --set opcua.configuration.name=akri-opcua-monitoring \
  --set opcua.configuration.brokerPod.image.repository="ghcr.io/project-akri/akri/opcua-monitoring-broker" \
  --set opcua.configuration.brokerPod.image.tag="latest-dev" \
  --set opcua.configuration.brokerProperties.IDENTIFIER='FastUInt1' \
  --set opcua.configuration.brokerProperties.NAMESPACE_INDEX='3' \
  --set opcua.configuration.discoveryDetails.discoveryUrls[0]="opc.tcp://aci-contoso-xxxxx-plc1.eastus.azurecontainer.io:50000/" \
  --set opcua.configuration.discoveryDetails.discoveryUrls[1]="opc.tcp://aci-contoso-xxxxx-plc2.eastus.azurecontainer.io:50000/" 

Azure上でAkriを素早く利用したいときはAkriの商用サービスであるAzure IoT Akriを利用すると便利です。が、Azure IoT Akriはマネージドなため、今回は機能をいろいろ見たかったのでオープンソースのAkriを利用しています。

動作確認

Akriをデプロイすると、以下のようなPodとServiceが作成されます。

$ k get po,svc
NAME                                              READY   STATUS    RESTARTS   AGE
pod/akri-agent-daemonset-lzl79                    1/1     Running   0          12s
pod/akri-controller-deployment-86cf9c5cf-2zdt6    1/1     Running   0          12s
pod/akri-opcua-discovery-daemonset-gq7mt          1/1     Running   0          12s
pod/akri-webhook-configuration-69d9bd7d89-5tqwv   1/1     Running   0          12s

NAME                                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/kubernetes                   ClusterIP   10.43.0.1      <none>        443/TCP   15m
service/akri-webhook-configuration   ClusterIP   10.43.246.85   <none>        443/TCP   12s

DaemonSetをみてみましょう。

$ k get daemonset

NAME                             DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
akri-agent-daemonset             1         1         1       1            1           kubernetes.io/os=linux   3m5s
akri-opcua-discovery-daemonset   1         1         1       1            1           kubernetes.io/os=linux   3m5s

akri-opcua-discoveryという名前のDaemonSetがOPC UAサーバーを検出します。

ログを確認してみます。エッジのOPC UA Serverに見立てた、Azure Container Instanceをopc.tcp://aci-contoso-xxx-plc1.eastus.azurecontainer.io:50000/opc.tcp://aci-contoso-xxx-plc2.eastus.azurecontainer.io:50000/を検出しているのが確認できます。

$ k logs akri-opcua-discovery-daemonset-gq7mt  

[2024-05-02T23:54:48Z INFO  opcua_discovery_handler] main - opcua discovery handler started
[2024-05-02T23:54:48Z TRACE akri_discovery_utils::discovery::discovery_handler] run_discovery_handler - registering with Agent with uds endpoint
[2024-05-02T23:54:48Z INFO  akri_discovery_utils::registration_client] register_discovery_handler - entered
[2024-05-02T23:54:48Z TRACE akri_discovery_utils::registration_client] register_discovery_handler - sleeping for 10 seconds and trying again
[2024-05-02T23:54:48Z INFO  akri_discovery_utils::discovery::server] internal_run_discovery_server - entered
[2024-05-02T23:54:58Z INFO  akri_opcua::discovery_handler] discover - called for OPC UA protocol
[2024-05-02T23:54:58Z INFO  akri_opcua::discovery_impl] do_standard_discovery - for DiscoveryUrls ["opc.tcp://aci-contoso-xxx-plc1.eastus.azurecontainer.io:50000/", "opc.tcp://aci-contoso-xxx-plc2.eastus.azurecontainer.io:50000/"]
[2024-05-02T23:55:16Z TRACE akri_opcua::discovery_impl] get_discovery_urls - Server at opc.tcp://aci-contoso-xxx-plc1.eastus.azurecontainer.io:50000/ responded with 1 Applications
[2024-05-02T23:55:16Z TRACE akri_opcua::discovery_impl] get_discovery_url_from_application - found server : OpcPlc
[2024-05-02T23:55:16Z TRACE akri_opcua::discovery_impl] get_discovery_url_from_application - server has [UAString { value: Some("opc.tcp://aci-contoso-xxx-plc1.eastus.azurecontainer.io:50000/") }] DiscoveryUrls
[2024-05-02T23:55:16Z TRACE akri_opcua::discovery_impl] get_discovery_urls - Server at opc.tcp://aci-contoso-xxx-plc2.eastus.azurecontainer.io:50000/ responded with 1 Applications
[2024-05-02T23:55:16Z TRACE akri_opcua::discovery_impl] get_discovery_url_from_application - found server : OpcPlc
[2024-05-02T23:55:16Z TRACE akri_opcua::discovery_impl] get_discovery_url_from_application - server has [UAString { value: Some("opc.tcp://aci-contoso-xxx-plc2.eastus.azurecontainer.io:50000/") }] DiscoveryUrls
[2024-05-02T23:55:16Z TRACE akri_opcua::discovery_handler] discover - found OPC UA server at DiscoveryURL opc.tcp://aci-contoso-xxx-plc1.eastus.azurecontainer.io:50000/
[2024-05-02T23:55:16Z TRACE akri_opcua::discovery_handler] discover - found OPC UA server at DiscoveryURL opc.tcp://aci-contoso-xxx-plc2.eastus.azurecontainer.io:50000/
......

OPC UA Serverが検出されたら、まもなくOPC UA Serverのデータを取得するためのBroker Podがデプロイされます。

k get po
NAME                                              READY   STATUS    RESTARTS   AGE
akri-agent-daemonset-lzl79                        1/1     Running   0          5m29s
akri-controller-deployment-86cf9c5cf-2zdt6        1/1     Running   0          5m29s
akri-opcua-discovery-daemonset-gq7mt              1/1     Running   0          5m29s
akri-webhook-configuration-69d9bd7d89-5tqwv       1/1     Running   0          5m29s
iot-operations-akri-opcua-monitoring-47ef33-pod   1/1     Running   0          4m51s
iot-operations-akri-opcua-monitoring-07aa54-pod   1/1     Running   0          4m52s

Broker Podiot-operations-akri-opcua-monitoring-47ef33のログを確認します。これをみると、まずApplication Configurationを作成し、OPC UA Serverのエンドポイントを検出し、セッションを作成し、OPC UA Serverのノードをブラウズしています。その後、1秒間隔でデータを取得しているのがわかります。

k logs iot-operations-akri-opcua-monitoring-47ef33-pod

.NET Core OPC UA Console Client Start
1 - Create an Application Configuration.
2 - Discover endpoints of opc.tcp://aci-contoso-xxx-plc1.eastus.azurecontainer.io:50000/.
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://[::]:8083
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /app
    Selected endpoint uses: None
3 - Create a session with OPC UA server.
4 - Browse the OPC UA server namespace.
 DisplayName, BrowseName, NodeClass
 Server, Server, Object
   + ServerArray, ServerArray, Variable next
   + NamespaceArray, NamespaceArray, Variable next
   + UrisVersion, UrisVersion, Variable next
   + ServerStatus, ServerStatus, Variable next
   + ServiceLevel, ServiceLevel, Variable next
   + EstimatedReturnTime, EstimatedReturnTime, Variable next
   + LocalTime, LocalTime, Variable next
   + ServerCapabilities, ServerCapabilities, Object next
   + ServerDiagnostics, ServerDiagnostics, Object next
   + VendorServerInfo, VendorServerInfo, Object next
   + ServerRedundancy, ServerRedundancy, Object next
   + Namespaces, Namespaces, Object next
   + GetMonitoredItems, GetMonitoredItems, Method next
   + ResendData, ResendData, Method next
   + RequestServerStateChange, RequestServerStateChange, Method next
   + ServerConfiguration, ServerConfiguration, Object next
   + Quantities, Quantities, Object next
   + DefaultHAConfiguration, DefaultHAConfiguration, Object next
   + DefaultHEConfiguration, DefaultHEConfiguration, Object next
   + PublishSubscribe, PublishSubscribe, Object next
   + Dictionaries, Dictionaries, Object next
   + Resources, Resources, Object next
 Aliases, Aliases, Object
   + FindAlias, FindAlias, Method next
   + LastChange, LastChange, Variable next
   + TagVariables, TagVariables, Object next
   + Topics, Topics, Object next
 Locations, Locations, Object
 DeviceSet, 2:DeviceSet, Object
   + DeviceFeatures, 2:DeviceFeatures, Object next
 NetworkSet, 2:NetworkSet, Object
 DeviceTopology, 2:DeviceTopology, Object
   + OnlineAccess, 2:OnlineAccess, Variable next
 Boilers, 4:Boilers, Object
   + Boiler #2, 4:Boiler #2, Object next
   + Boiler #1, 4:Boiler #1, Object next
 OpcPlc, 3:OpcPlc, Object
   + Telemetry, 3:Telemetry, Object next
   + Methods, 3:Methods, Object next
   + SimulatorConfiguration, 3:SimulatorConfiguration, Object next
 ReferenceTest, 6:ReferenceTest, Object
   + Scalar, 6:Scalar, Object next
   + DataAccess, 6:DataAccess, Object next
   + References, 6:References, Object next
   + AccessRights, 6:AccessRights, Object next
   + NodeIds, 6:NodeIds, Object next
   + Methods, 6:Methods, Object next
   + Views, 6:Views, Object next
   + Locales, 6:Locales, Object next
   + Attributes, 6:Attributes, Object next
5 - Create a subscription with publishing interval of 1 second.
6 - Add node FastUInt1 to the subscription.
7 - Add the subscription to the session.
8 - Running...Press Ctrl-C to exit...
FastUInt1: 79, 05/02/2024 23:56:03, Good
FastUInt1: 84, 05/02/2024 23:56:04, Good
FastUInt1: 75, 05/02/2024 23:56:05, Good
FastUInt1: 73, 05/02/2024 23:56:06, Good
FastUInt1: 74, 05/02/2024 23:56:07, Good
FastUInt1: 68, 05/02/2024 23:56:08, Good
FastUInt1: 75, 05/02/2024 23:56:09, Good
FastUInt1: 81, 05/02/2024 23:56:10, Good
FastUInt1: 77, 05/02/2024 23:56:11, Good
FastUInt1: 65, 05/02/2024 23:56:12, Good
FastUInt1: 69, 05/02/2024 23:56:13, Good
FastUInt1: 84, 05/02/2024 23:56:14, Good

....

すばらしい✨✨✨✨
OPC UA Serverからデータを取得できました。
めでたしめでたし。

Akriの基本動作と内部アーキテクチャ

一般的にエッジ環境では、デバイスそのもののトラブルや、ネットワークの瞬断などで、デバイスの状態が頻繁に変化します。自動車の車載器などをイメージしてもらうとわかりやすいかもしれません。そもそも車が運転されていないのか、故障中なのか、走っているけどたまたまトンネルにいるので、通信が途切れているのか、など、状況によってデバイスの状態が変わります。
これが、車数台であればなんとか頑張れそうな気もします。しかし、数百台、数千台となると、デバイスの状態を管理するのが大変です。また、24時間365日、デバイスの状態を監視し続けるのも大変です。これらのデバイスを管理するため、クラウドなどのサーバで集約する仕組みも必要でブローカを置く必要がありますが、なにぶん規模が大きくなればなるほど、デバイスを極限までコスパ良く管理したくなるものです。

Kubernetesは、コンテナのデプロイメント、スケーリング、ネットワークの設定、ロードバランシング、ロギング、モニタリングなどを自動化できるコンテナオーケストレーションツールです。主に分散システムでの利用を想定されていて、障害時のオートヒール機能やスケーリングなどの機能を備えています。このしくみに乗っかる形でエッジデバイスを管理していこうというのが、Akriのコンセプトです。

Akriは刻々と変わるデバイスの状態が変化するたびに、デバイスを探し出し、その変化をKubernetesに通知し、デバイスからデータを取得したり管理したりしてくれるオープンソースソフトウェアです。


で、それをもう少し厳密に説明すると、Akriは以下の流れでデバイスを管理しています。

  1. Akri Configuration でデバイスを定義する。Akri discoveryHandlerがデバイスを検索し、見つかったら Akri Agent へ通知する

  2. デバイスが見つかったらAkri AgentがAkri Instance(CRD)を作成する

  3. Akri Controller が Akri Instance(CRD)に対応した Broker Pod をデプロイする

  4. 検出されたデバイスとBroker Pod デバイスが接続される

これらを実現するため、Akriは次のアーキテクチャで実装されています。

このように複数の登場人物が、 複雑な系を維持するために各々に与えられた仕事を淡々とやっていくわけなのですが、主要なコンポーネントの役割と動きを説明します。

Akri Configuration CRD

デバイスを管理するには、何はともあれまずはデバイスの情報を定義する必要があります。Akri Configurationは、検出したいデバイスの情報と、リソースを検出するノードにデプロイする必要があるPodを定義するためのCRD(Custom Resource Definitions)です。

k get  Configuration

NAME                    CAPACITY   AGE
akri-opcua-monitoring   1          26m

discoveryHandlerでデバイスを検出するハンドラーを指定し、brokerSpecでデバイスのデータを取得するためのBroker Podを指定します。OPC UAの場合、discoveryDetailsでデバイスのエンドポイントを指定します。

今回の場合だと、 「お忙しいところすいませんが、opc.tcp://aci-contoso-xxx-plc1.eastus.azurecontainer.io:50000 opc.tcp://aci-contoso-xxx-plc2.eastus.azurecontainer.io:50000を探しに行って、見つかったらghcr.io/project-akri/akri/opcua-monitoring-brokerをBroker Podとしてデプロイしてくださいね。あ、デバイスからのデータはFastUInt13でとれますので!」を設定しています。

apiVersion: akri.sh/v0
kind: Configuration
metadata:
  name: akri-opcua-monitoring
spec:
  brokerProperties:
    IDENTIFIER: FastUInt1
    NAMESPACE_INDEX: "3"
  brokerSpec:
    brokerPodSpec:
      containers:
      - image: ghcr.io/project-akri/akri/opcua-monitoring-broker:latest-dev
        name: akri-opcua-monitoring-broker
  capacity: 1
  discoveryHandler:
    name: opcua
    discoveryDetails: "opcuaDiscoveryMethod: \n  standard:\n    discoveryUrls:\n    -
      opc.tcp://aci-contoso-xxx-plc1.eastus.azurecontainer.io:50000/\n    - opc.tcp://aci-contoso-xxx-plc2.eastus.azurecontainer.io:50000/\napplicationNames:\n
      \ action: Exclude\n  items: []\n"

この情報は、Kubernetesのetcdに保存され、Akri Agentが定期的に監視しています。

Akri Controller

Akri Controllerは、大きく2つの役割があります。

  • デバイスへのクラスターアクセスの有効化
  • デバイスの消失の処理

Akri Controllerはクラスター内のマスターノードで実行されます。

KubernetesのControllerはクラスターの状態を常時監視し、必要に応じて変更を加えたり要求したりする制御ループです。Kubernetesの特徴ともいえるこの機能のおかげて、クラスター内のリソースが常に正しい状態に保たれます。

いわゆる 「Kubernetesがよしなにやってくれるやつ」 です。

Akri Discovery Handlers

どこからデバイスを見つけてきて、見つかったらどうしてほしいかを定義したら、実際にデバイスを検索するハンドラーが必要になります。Akri Discovery Handlersは、検出されたすべてのデバイスをAkri Agentにアドバタイズします。Akri Discovery Handlersは、discovery.protoで定義されているDiscoveryHandler を実装します。Akri Discovery Handlersの詳細は、Hemlで設定できます。

Heml key 説明 デフォルト値
opcua.configuration.discoveryDetails.discoveryUrls OPC UA ServerのURL ["opc.tcp://localhost:4840/"]
opcua.configuration.discoveryDetails.applicationNames.action OPC UA アプリケーションに対して実行するフィルターアクション Exclude

ほかにも資格情報の設定やブローカの設定などを細かく指定できます。詳細はこちらを参照してください。

Akri Agent

検出されたデバイス用のKubernetes Device-Pluginsで、次を実行します。

  • Configurationの変更を監視して、どのリソースを検索するかを決定
  • リソースの可用性を監視し、どのリソースをアドバタイズするか決定
  • 変更に応じて、Kubernetes にリソースの正常性と可用性を通知

Akri Agentはデバイスの状態(Waiting/ Active / Offline(Instant)) を保持しています。また、Akri AgentはDaemonSetとして動作します。DaemoSetは、Kubernetesのノード上で必ず1つだけ動くPodのことで、もしAkri Agentがクラッシュした場合、Kubernetesが自動的に再起動します。

デバイスからのデータ取得と異常値検出

取得したデータを使って異常値検出をするサンプルアプリがあるので、試してみます。

インストールは、以下のコマンドで行います。

kubectl apply -f https://raw.githubusercontent.com/project-akri/akri/main/deployment/samples/akri-anomaly-detection-app.yaml

PodとServiceが作成されていることを確認します。ServiceはNodePortで公開されているので、クラスタにアクセスできる環境からブラウザでアクセスします。

$ k get po,svc
NAME                                                  READY   STATUS    RESTARTS   AGE
pod/akri-anomaly-detection-app-7f989d6d4b-7759d       1/1     Running   0          25s

NAME                                       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
service/akri-anomaly-detection-app         NodePort    10.43.180.49    <none>        80:32509/TCP   25s
$ k get no -o wide
NAME             STATUS   ROLES                  AGE     VERSION        INTERNAL-IP   EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION     CONTAINER-RUNTIME
iot-operations   Ready    control-plane,master   3h11m   v1.29.4+k3s1   10.0.0.4      <none>        Ubuntu 22.04.4 LTS   6.5.0-1019-azure   containerd://1.7.15-k3s1

たとえば、NodeのInternal-IPが10.0.0.4akri-anomaly-detection-appに割り当てられたNodePortが32509の場合、htttp://10.0.0.4:32509/でアクセスできます。

このサンプルの実装は、gRPC Serverから温度値を取得しこの値がデータセットの外れ値であるかどうかを判断しています。データセットは、70~80 の数値がランダムにセットされたCSVで、この範囲外は外れ値(赤色)と見なされます。 Web アプリケーションはすべての温度値とその値を送信した OPC UA Serverのアドレスを表示します。

なお、今回はたまたまこのサンプルをKubernetesクラスタ上にデプロイしていますが、実際には、gRPCが喋れるクライアントから異常値を検出するアプリケーションを作成することができます。

まとめ

Akriを使うとKubernetes上でデバイスの管理ができることが分かりました。内部アーキテクチャもざっくり確認しましたが、Kubernetes上で管理されているので、独自のHanderやBrokerを実装したい!となったときも参考になりそうと思いました。

IoTではハードウエアの実装/対応しているプロトコル/デバイスの仕様などな複雑な技術要素を考慮しての設計が必要となり、総合格闘技的な力業が必要になるシーンもあるのではと思っています。

ただし、決して簡単な仕組みではないため本番環境で利用するには、十分な検証が必要だなとおもいました(小並感)

Microsoft (有志)

Discussion