次の方法で共有


チュートリアル: OPC UA 資産を Azure IoT Operations クラスターに追加する

このチュートリアルでは、OPC UA 資産を Azure IoT Operations クラスターに手動で追加します。 これらの資産は、Azure IoT Operations クラスターの MQTT ブローカーにメッセージを発行します。 通常、OT ユーザーは次の手順を実行します。

資産とは、物理デバイス、または、デバイス、マシン、システム、またはプロセスを表す論理エンティティです。 たとえば、物理資産には、ポンプ、モーター、タンク、生産ラインなどがあります。 定義する論理資産には、プロパティがあったり、テレメトリをストリーミングしたり、イベントを生成したりする可能性があります。

OPC UA サーバーは、資産と通信するソフトウェア アプリケーションです。 OPC UA タグは、OPC UA サーバーが公開するデータ ポイントです。 OPC UA タグは、資産のステータス、パフォーマンス、品質、状態に関する、リアルタイムまたは履歴データを提供できます。

このチュートリアルでは、Operations Experience Web UI を使用して資産を作成します。 Azure CLI を使って、これらのタスクの一部を完了することもできます。

前提条件

Kubernetes クラスターにデプロイされた Azure IoT Operations のインスタンス。 インスタンスを作成するには、次のいずれかを使用して Azure IoT Operations をデプロイします。

Operations Experience Web UI にサインインするには、Kubernetes - Azure Arc インスタンスを含むリソース グループに対する共同作成者以上のアクセス許可を持つ Microsoft Entra ID アカウントが必要です。 詳細については、「Operations Experience Web UI」を参照してください。

特に明記されていない限り、このチュートリアルのコンソール コマンドは Bash または PowerShell 環境で実行できます。

どのような問題が解決されますか?

OPC UA サーバーが公開するデータは複雑な構造になっている可能性があり、理解が困難な場合があります。 Azure IoT Operations では、OPC UA 資産をタグ、イベント、プロパティとしてモデル化する方法が提供されます。 このモデル化により、データを理解することと、それを MQTT ブローカーやデータフローなどのダウンストリーム プロセスで使用することがより簡単になります。

OPC PLC シミュレーターをデプロイする

このチュートリアルでは、OPC PLC シミュレーターを使用してサンプル データを生成します。 OPC PLC シミュレーターをデプロイするには、次のコマンドを実行します。

kubectl apply -f https://raw.githubusercontent.com/Azure-Samples/explore-iot-operations/main/samples/quickstarts/opc-plc-deployment.yaml

次に示すスニペットは、適用した YAML ファイルです。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: opc-plc-000000
  namespace: azure-iot-operations
  labels:
    app.kubernetes.io/component: opcplc-000000
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/component: opcplc-000000
  template:
    metadata:
      labels:
        app.kubernetes.io/component: opcplc-000000
    spec:
      containers:
      - name: opc-plc
        image: mcr.microsoft.com/iotedge/opc-plc:latest
        args:
          - "--ph=opcplc-000000"
          - "--cdn=opcplc-000000"
          - "--ut"
          - "--sn=25"
          - "--sr=10"
          - "--fn=2000"
          - "--veryfastrate=1000"
          - "--gn=5"
          - "--pn=50000"
          - "--maxsessioncount=100"
          - "--maxsubscriptioncount=100"
          - "--maxqueuedrequestcount=2000"
          - "--ses"
          - "--alm"
          - "--at=FlatDirectory"
          - "--drurs"
          - "--ll-debug"
          - "--nodesfile"
          - "/app/config/nodesfile.json"
        ports:
        - containerPort: 50000
        volumeMounts:
          - name: opc-plc-default-application-cert
            mountPath: /app/pki/own
          - name: opc-plc-trust-list
            mountPath: /app/pki/trusted
          - name: config-volume
            mountPath: /app/config
      volumes:
        - name: opc-plc-default-application-cert
          secret:
            secretName: opc-plc-default-application-cert
        - name: opc-plc-trust-list
          secret:
            secretName: opc-plc-trust-list
        - name: config-volume
          configMap:
            name: opc-plc-config
      serviceAccountName: opcplc-000000-service-account
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: opc-plc-config
  namespace: azure-iot-operations
  labels:
    app.kubernetes.io/component: opcplc-000000
data:
  nodesfile.json: |
    {
      "Folder": "MyTelemetry",
      "NodeList": [
        {
          "NodeId": "ns=3;s=FastUInt100",
          "Name": "Fryer Temperature",
          "DataType": "Double",
          "ValueRank": -1,
          "AccessLevel": "CurrentReadOrWrite",
          "Description": "Fryer Temperature with spikes",
          "Anomaly": "Spike",
          "MinValue": 150.0,
          "MaxValue": 200.0          
        }
      ]
    }
---
apiVersion: v1
kind: Service
metadata:
  name: opcplc-000000
  namespace: azure-iot-operations
  labels:
    app.kubernetes.io/component: opcplc-000000
spec:
  type: ClusterIP
  selector:
    app.kubernetes.io/component: opcplc-000000
  ports:
    - port: 50000
      protocol: TCP
      targetPort: 50000
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: opc-plc-self-signed-issuer
  namespace: azure-iot-operations
  labels:
    app.kubernetes.io/component: opcplc-000000
spec:
  selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: opc-plc-default-application-cert
  namespace: azure-iot-operations
  labels:
    app.kubernetes.io/component: opcplc-000000
spec:
  secretName: opc-plc-default-application-cert
  duration: 2160h # 90d
  renewBefore: 360h # 15d
  issuerRef:
    name: opc-plc-self-signed-issuer
    kind: Issuer
  commonName: OpcPlc
  dnsNames:
    - opcplc-000000
    - opcplc-000000.azure-iot-operations.svc.cluster.local
    - opcplc-000000.azure-iot-operations
  uris:
    - urn:OpcPlc:opcplc-000000
  usages:
    - digital signature
    - key encipherment
    - data encipherment
    - server auth
    - client auth
  privateKey:
    algorithm: RSA
    size: 2048
  encodeUsagesInRequest: true
  isCA: false
---
apiVersion: v1
kind: Secret
metadata:
  name: opc-plc-trust-list
  namespace: azure-iot-operations
  labels:
    app.kubernetes.io/component: opcplc-000000
data: {}
---
apiVersion: batch/v1
kind: Job
metadata:
  name: opcplc-000000-execute-mutual-trust
  namespace: azure-iot-operations
  labels:
    app.kubernetes.io/component: opcplc-000000
spec:
  backoffLimit: 1
  template:
    spec:
      containers:
      - name: kubectl
        image: mcr.microsoft.com/oss/kubernetes/kubectl:v1.27.1
        imagePullPolicy: Always
        command: ["/bin/sh"]
        args: ["/scripts/execute-commands.sh"]
        volumeMounts:
        - name: scripts
          mountPath: /scripts
          readOnly: true
      restartPolicy: Never
      serviceAccountName: opcplc-000000-service-account
      volumes:
      - name: scripts
        configMap:
          name: opcplc-000000-execute-commands-script
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: opcplc-000000-execute-commands-script
  namespace: azure-iot-operations
  labels:
    app.kubernetes.io/component: opcplc-000000
data:
  execute-commands.sh: |
    #!/bin/sh

    # wait 20 seconds for the resources to be created
    sleep 20

    # Extract the OPC UA connector application instance certificate and add it to the OPC PLC trust list
    cert=$(kubectl -n azure-iot-operations get secret aio-opc-opcuabroker-default-application-cert -o jsonpath='{.data.tls\.crt}' | base64 -d)
    data=$(kubectl create secret generic temp --from-literal=opcuabroker.crt="$cert" --dry-run=client -o jsonpath='{.data}')
    kubectl patch secret opc-plc-trust-list -n azure-iot-operations -p "{\"data\": $data}"

    # Extract the OPC PLC application instance certificate and add it to the OPC UA connector trust list
    cert=$(kubectl -n azure-iot-operations get secret opc-plc-default-application-cert -o jsonpath='{.data.tls\.crt}' | base64 -d)
    data=$(kubectl create secret generic temp --from-literal=opcplc-000000.crt="$cert" --dry-run=client -o jsonpath='{.data}')
    kubectl patch secret aio-opc-ua-broker-trust-list -n azure-iot-operations -p "{\"data\": $data}"
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: opcplc-000000-service-account
  namespace: azure-iot-operations
  labels:
    app.kubernetes.io/component: opcplc-000000
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: opc-plc-000000-secret-access-role
  namespace: azure-iot-operations
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: opc-plc-000000-secret-access-rolebinding
  namespace: azure-iot-operations
subjects:
- kind: ServiceAccount
  name: opcplc-000000-service-account
  namespace: azure-iot-operations
roleRef:
  kind: Role
  name: opc-plc-000000-secret-access-role
  apiGroup: rbac.authorization.k8s.io

注意

この構成はセキュリティで保護されていません。 運用環境では、この構成を使わないでください。

Operations Experience にサインインする

資産エンドポイント、資産を作成し、OPC UA タグとイベントをサブスクライブするには、Operations Experience を使用します。

ブラウザーで Operations Experience を参照し、Microsoft Entra ID 資格情報を使用してサインインします。

サイトを選択する

サイトは、Azure IoT Operations インスタンスのコレクションです。 通常、サイトは物理的な場所でインスタンスをグループ化し、OT ユーザーが資産を見つけて管理するのを容易にします。 IT 管理者は、サイトを作成し、それに Azure IoT Operations インスタンスを割り当てます。 新しい展開を使用しているため、サイトはまだありません。 前に作成したクラスターは、[未割り当てインスタンスの表示] を選択することで確認できます。 操作エクスペリエンスで、インスタンスは Azure IoT Operations をデプロイしたクラスターを表します。

Operations Experience で未割り当てのインスタンス ノードを示すスクリーンショット。

インスタンスを選択する

前のチュートリアルで Azure IoT Operations をデプロイしたクラスターを選びます。

Azure IoT Operations インスタンスの一覧のスクリーンショット。

ヒント

インスタンスが表示されない場合は、適切な Microsoft Entra ID テナントにいない可能性があります。 操作エクスペリエンスの右上のメニューからテナントを変更できます。

資産エンドポイントを追加する

前の記事で Azure IoT Operations をデプロイしたときに、組み込みの OPC PLC シミュレーターを含めました。 この手順では、OPC PLC シミュレーターに接続できるようにする資産エンドポイントを追加します。

資産エンドポイントを追加するには、次のようにします。

  1. [資産エンドポイント] を選択し、続いて [資産エンドポイントの作成] を選択します。

    Operations Experience の [資産エンドポイント] ページを示すスクリーンショット。

  2. 次のエンドポイント情報を入力します。

    フィールド
    資産エンドポイント名 opc-ua-connector-0
    OPC UA サーバー URL opc.tcp://opcplc-000000:50000
    ユーザー認証モード Anonymous
  3. 定義を保存するには、[作成] を選択します。

    この構成では、opc-ua-connector-0 という新しい資産エンドポイントをクラスターにデプロイします。 kubectl を使って資産エンドポイントを表示できます。

    kubectl get assetendpointprofile -n azure-iot-operations
    

資産を管理する

Operations Experience でインスタンスを選択すると、[資産] ページに使用可能な資産の一覧が表示されます。 資産がまだない場合、この一覧は空です。

Azure IoT Operations の空の資産リストのスクリーンショット。

資産の作成

資産を作成するには、[資産の作成] を選択します。 その後、次の資産情報を入力します。

フィールド
資産エンドポイント opc-ua-connector-0
資産名 thermostat
説明 A simulated thermostat asset
既定の MQTT トピック azure-iot-operations/data/thermostat

既存のカスタム プロパティを削除し、次のカスタム プロパティを追加します。 後のチュートリアルの Power BI テンプレートでそのクエリを実行するので、正確なプロパティ名を使うよう注意してください。

プロパティ名 プロパティの詳細
batch 102
顧客 コントソ
備品 Boiler
isSpare true
location シアトル

Azure IoT Operations の資産の詳細ページのスクリーンショット。

[次へ] を選択して、[タグの追加] ページに移動します。

OPC UA タグを作成する

[タグの追加] ページで 2 つの OPC UA タグを追加します。 それぞれのタグを追加するには、[タグまたは CSV の追加] を選択し、続いて [タグの追加] を選択します。 次の表に示すタグの詳細を入力します。

ノード ID タグ名 可観測性モード
ns=3;s=FastUInt10 温度 なし
ns=3;s=FastUInt100 タグ 10 なし

監視モードは値 NoneGaugeCounterHistogramLog のいずれかになります。

[既定の設定の管理] を選択して、各タグの既定のサンプリング間隔とキュー サイズを変更できます。

Azure IoT Operations の [タグの追加] ページのスクリーンショット。

[次へ] を選択して [イベントの追加] ページに移動し、[次へ] を選択して [確認] ページに移動します。

確認

資産とタグの詳細を確認し、必要な調整を行ったら、[作成] を選択します。

Azure IoT Operations の [資産の作成] レビュー ページのスクリーンショット。

この構成では、thermostat という名前の新しい資産をクラスターにデプロイします。 以下のように kubectl を使用することで、資産を表示できます。

kubectl get assets -n azure-iot-operations

データが流れていることを確認する

mosquitto_sub ツールを使用して、データが MQTT ブローカーに流れていることを確認します。 この例では、Kubernetes クラスターで mosquitto_sub ツールを実行します。

  1. 次のコマンドを実行して、クラスターの MQTT ブローカーを操作するのに役立つ mosquitto_pubmosquitto_sub ツールを含むポッドをデプロイします。

    kubectl apply -f https://raw.githubusercontent.com/Azure-Samples/explore-iot-operations/main/samples/quickstarts/mqtt-client.yaml
    

    次に示すスニペットは、適用した YAML ファイルです。

    # Important: do not use in production environments
    # Create a service account
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: mqtt-client
      namespace: azure-iot-operations
    ---
    # Creates a pod with mosquitto-clients and mqttui utilities in your cluster
    apiVersion: v1
    kind: Pod
    metadata:
      name: mqtt-client
      # The namespace must match the IoT MQ BrokerListener's namespace
      # Otherwise use the long hostname: aio-broker.azure-iot-operations.svc.cluster.local
      namespace: azure-iot-operations
    spec:
      # Use the "mqtt-client" service account which comes with default deployment
      # Otherwise create it with `kubectl create serviceaccount mqtt-client -n azure-iot-operations`
      serviceAccountName: mqtt-client
      containers:
        # Install mosquitto and mqttui utilities on Alpine linux
      - image: alpine
        name: mqtt-client
        command: ["sh", "-c"]
        args: ["apk add mosquitto-clients mqttui && sleep infinity"]
        resources:
          limits:
            cpu: 500m
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 100Mi
        volumeMounts:
        - name: broker-sat
          mountPath: /var/run/secrets/tokens
        - name: trust-bundle
          mountPath: /var/run/certs
      volumes:
      - name: broker-sat
        projected:
          sources:
          - serviceAccountToken:
              path: broker-sat
              audience: aio-internal # Must match audience in BrokerAuthentication
              expirationSeconds: 86400
      - name: trust-bundle
        configMap:
          name: azure-iot-operations-aio-ca-trust-bundle # Default root CA cert
    

    注意

    この構成はセキュリティで保護されていません。 運用環境では、この構成を使わないでください。

  2. mqtt-client ポッドが実行されているときに、次のコマンドを実行して、作成したポッド内にシェル環境を作成します。

    kubectl exec --stdin --tty mqtt-client -n azure-iot-operations -- sh
    
  3. mqtt-client ポッドの Bash シェルで、次のコマンドを実行し、data/thermostat トピックにサブスクライブされた mosquitto_sub ツールを使用して MQTT ブローカーに接続します。

    mosquitto_sub --host aio-broker --port 18883 --topic "azure-iot-operations/data/#" -v --debug --cafile /var/run/certs/ca.crt -D CONNECT authentication-method 'K8S-SAT' -D CONNECT authentication-data $(cat /var/run/secrets/tokens/broker-sat)
    

    このコマンドは、Ctrl+C キーを押して停止するまで実行を続け、data/thermostat トピックにメッセージが到着するとそれを表示します。 シェル環境を終了するには、「exit」と入力します。

追加したサーモスタット資産がデータを発行していることを確認するには、azure-iot-operations/data トピックのテレメトリを表示します。

Client $server-generated/05a22b94-c5a2-4666-9c62-837431ca6f7e received PUBLISH (d0, q0, r0, m0, 'azure-iot-operations/data/thermostat', ... (152 bytes))
{"temperature":{"SourceTimestamp":"2024-07-29T15:02:17.1858435Z","Value":4558},"Tag 10":{"SourceTimestamp":"2024-07-29T15:02:17.1858869Z","Value":4558}}
Client $server-generated/05a22b94-c5a2-4666-9c62-837431ca6f7e received PUBLISH (d0, q0, r0, m0, 'azure-iot-operations/data/thermostat', ... (152 bytes))
{"temperature":{"SourceTimestamp":"2024-07-29T15:02:18.1838125Z","Value":4559},"Tag 10":{"SourceTimestamp":"2024-07-29T15:02:18.1838523Z","Value":4559}}
Client $server-generated/05a22b94-c5a2-4666-9c62-837431ca6f7e received PUBLISH (d0, q0, r0, m0, 'azure-iot-operations/data/thermostat', ... (152 bytes))
{"temperature":{"SourceTimestamp":"2024-07-29T15:02:19.1834363Z","Value":4560},"Tag 10":{"SourceTimestamp":"2024-07-29T15:02:19.1834879Z","Value":4560}}
Client $server-generated/05a22b94-c5a2-4666-9c62-837431ca6f7e received PUBLISH (d0, q0, r0, m0, 'azure-iot-operations/data/thermostat', ... (152 bytes))
{"temperature":{"SourceTimestamp":"2024-07-29T15:02:20.1861251Z","Value":4561},"Tag 10":{"SourceTimestamp":"2024-07-29T15:02:20.1861709Z","Value":4561}}
Client $server-generated/05a22b94-c5a2-4666-9c62-837431ca6f7e received PUBLISH (d0, q0, r0, m0, 'azure-iot-operations/data/thermostat', ... (152 bytes))
{"temperature":{"SourceTimestamp":"2024-07-29T15:02:21.1856798Z","Value":4562},"Tag 10":{"SourceTimestamp":"2024-07-29T15:02:21.1857211Z","Value":4562}}

データが流れていない場合は、aio-opc-opc.tcp-1 ポッドを再起動します。

  1. 次のコマンドを実行して、aio-opc-opc.tcp-1 ポッドの名前を確認します。

    kubectl get pods -n azure-iot-operations
    

    ポッドの名前は aio-opc-opc.tcp-1-849dd78866-vhmz6 のようになります。

  2. 次の例のようなコマンドを実行して、aio-opc-opc.tcp-1 ポッドを再起動します。 前の手順の aio-opc-opc.tcp-1 ポッド名を使います。

    kubectl delete pod aio-opc-opc.tcp-1-849dd78866-vhmz6 -n azure-iot-operations
    

前のチュートリアルで追加したサンプル タグによって、次の例のようなメッセージが資産から生成されます。

{
    "temperature": {
        "SourceTimestamp": "2024-08-02T13:52:15.1969959Z",
        "Value": 2696
    },
    "Tag 10": {
        "SourceTimestamp": "2024-08-02T13:52:15.1970198Z",
        "Value": 2696
    }
}

問題をどのように解決したか。

このチュートリアルでは、資産エンドポイントを追加して、資産とタグを定義しました。 資産とタグは、OPC UA server からのデータをモデル化して、MQTT ブローカーやその他のダウンストリーム プロセスでデータを容易に使用できるようにします。 次のチュートリアルでは、定義したサーモスタット資産を使用します。

リソースをクリーンアップする

次のチュートリアルに進む場合は、すべてのリソースを保持してください。

Azure IoT Operations デプロイは削除するが、クラスターは保持する場合は、az iot ops delete コマンドを使用します。

az iot ops delete --cluster $CLUSTER_NAME --resource-group $RESOURCE_GROUP

このクイックスタート用に作成したすべてのリソースを削除する場合は、Azure IoT Operations をデプロイした Kubernetes クラスターを削除した後、そのクラスターを含んでいた Azure リソース グループを削除します。

これらのクイックスタートで Codespaces を使った場合は、GitHub から Codespace を削除します。

次のステップ

チュートリアル: データフローを使用して資産テレメトリをクラウドに送信する