다음을 통해 공유


빠른 시작: 클러스터 구성

Important

Azure IoT 작업 미리 보기 - Azure Arc에서 지원되는 Azure IoT 작업은 현재 preview로 제공됩니다. 프로덕션 환경에서는 이 미리 보기 소프트웨어를 사용하면 안 됩니다.

일반적으로 릴리스되는 릴리스가 제공되면 새로운 Azure IoT 작업 설치를 배포해야 합니다. 미리 보기 설치는 업그레이드할 수 없습니다.

베타, 미리 보기로 제공되거나 아직 일반 공급으로 릴리스되지 않은 Azure 기능에 적용되는 약관은 Microsoft Azure 미리 보기에 대한 추가 사용 약관을 참조하세요.

이 빠른 시작에서는 Azure IoT Operations 미리 보기 클러스터에서 다음 리소스를 구성합니다.

  • 빵집에서 오븐을 시뮬레이션하는 시뮬레이션된 OPC PLC 서버에 대한 연결을 정의하는 자산 엔드포인트입니다.
  • 오븐을 나타내고 오븐이 노출하는 데이터 요소를 정의하는 자산입니다.
  • 시뮬레이션된 오븐에서 메시지를 조작하는 데이터 흐름입니다.

자산은 디바이스, 컴퓨터, 시스템, 프로세스 중 하나를 나타내는 물리적 디바이스 또는 논리 엔터티입니다. 예를 들어 물리적 자산이라 하면 펌프, 모터, 탱크, 생산 라인이 가리킬 수 있습니다. 정의한 논리 자산은 속성을 갖거나, 원격 분석 데이터를 스트리밍하거나, 이벤트를 생성할 수 있습니다.

OPC UA 서버는 자산과 통신하는 소프트웨어 애플리케이션입니다. OPC UA 태그는 OPC UA 서버가 노출하는 데이터 포인트입니다. OPC UA 태그는 자산의 상태, 성능, 품질, 조건에 대한 실시간 데이터 또는 기록 데이터를 제공할 수 있습니다.

이 빠른 시작에서는 Bicep 파일을 사용하여 Azure IoT Operations 인스턴스를 구성합니다.

필수 조건

Kubernetes 클러스터에 Azure IoT Operations Preview 인스턴스를 배포합니다. 빠른 시작: K3s를 사용하여 GitHub Codespaces에서 Azure IoT Operations Preview를 실행하면 빠른 시작에 사용할 수 있는 Azure IoT Operations 인스턴스를 배포하는 간단한 지침이 제공됩니다.

달리 명시되지 않는 한 Bash 또는 PowerShell 환경에서 이 빠른 시작에서 콘솔 명령을 실행할 수 있습니다.

어떤 문제를 해결할 수 있나요?

OPC UA 서버가 노출하는 데이터는 구조가 복잡하고 이해하기 어려울 수 있습니다. Azure IoT Operations는 OPC UA 자산을 태그, 이벤트, 속성으로 모델링하는 방법을 제공합니다. 이 모델링을 사용하면 데이터를 더 쉽게 이해하고 MQTT 브로커 및 데이터 흐름과 같은 다운스트림 프로세스에서 사용할 수 있습니다. 데이터 흐름을 사용하면 Azure Event Hubs와 같은 클라우드 서비스로 데이터를 조작하고 라우팅할 수 있습니다. 이 빠른 시작에서는 데이터 흐름이 페이로드에 있는 일부 필드의 이름을 변경하고 메시지에 자산 ID를 추가합니다.

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

주의

이 구성은 자체 서명된 애플리케이션 인스턴스 인증서를 사용합니다. 프로덕션 환경에서는 이 구성을 사용하지 마세요. 자세한 내용은 OPC UA용 커넥터에 대한 OPC UA 인증서 인프라 구성을 참조하세요.


환경 변수 설정

Codespaces 환경을 사용하는 경우 필요한 환경 변수가 이미 설정되어 있으며 이 단계를 건너뛸 수 있습니다. 그렇지 않으면 셸에서 다음 환경 변수를 설정합니다.

# Your subscription ID
SUBSCRIPTION_ID=<subscription-id>

# The name of the resource group where your Kubernetes cluster is deployed
RESOURCE_GROUP=<resource-group-name>

# The name of your Kubernetes cluster
CLUSTER_NAME=<kubernetes-cluster-name>

클러스터 구성

다음 명령을 실행하여 Azure IoT Operations 인스턴스를 구성하는 Bicep 파일을 다운로드하고 실행합니다. Bicep 파일:

  • OPC PLC 시뮬레이터에 연결하는 자산 엔드포인트를 추가합니다.
  • 오븐을 나타내는 자산을 추가하고 오븐이 노출하는 데이터 요소를 정의합니다.
  • 시뮬레이션된 오븐에서 메시지를 조작하는 데이터 흐름을 추가합니다.
  • 데이터를 받을 Azure Event Hubs 인스턴스를 만듭니다.
wget https://raw.githubusercontent.com/Azure-Samples/explore-iot-operations/main/samples/quickstarts/quickstart.bicep -O quickstart.bicep

AIO_EXTENSION_NAME=$(az k8s-extension list -g $RESOURCE_GROUP --cluster-name $CLUSTER_NAME --cluster-type connectedClusters --query "[?extensionType == 'microsoft.iotoperations'].id" -o tsv | awk -F'/' '{print $NF}')
AIO_INSTANCE_NAME=$(az iot ops list -g $RESOURCE_GROUP --query "[0].name" -o tsv)
CUSTOM_LOCATION_NAME=$(az iot ops list -g $RESOURCE_GROUP --query "[0].extendedLocation.name" -o tsv | awk -F'/' '{print $NF}')

az deployment group create --subscription $SUBSCRIPTION_ID --resource-group $RESOURCE_GROUP --template-file quickstart.bicep --parameters clusterName=$CLUSTER_NAME customLocationName=$CUSTOM_LOCATION_NAME aioExtensionName=$AIO_EXTENSION_NAME aioInstanceName=$AIO_INSTANCE_NAME

구성 검토

Bicep 파일은 다음 리소스를 구성했습니다.

  • OPC PLC 시뮬레이터에 연결하는 자산 엔드포인트입니다.
  • 오븐을 나타내고 오븐이 노출하는 데이터 요소를 정의하는 자산입니다.
  • 시뮬레이션된 오븐에서 메시지를 처리하는 두 개의 데이터 흐름입니다.
  • 데이터 흐름에 대한 대상 허브를 포함하는 Azure Event Hubs 네임스페이스입니다.

자산 엔드포인트, 자산 및 데이터 흐름을 보려면 브라우저에서 작업 환경 UI로 이동하고 Microsoft Entra ID 자격 증명으로 로그인합니다. 새 배포를 사용 중이므로 아직 사이트가 없습니다. 이전 빠른 시작에서 할당되지 않은 인스턴스를 선택하여 만든 클러스터를 찾을 수 있습니다. 운영 환경에서 인스턴스는 Azure IoT 작업을 배포한 클러스터를 나타냅니다.

할당되지 않은 인스턴스를 보여 주는 작업 환경의 스크린샷

자산 엔드포인트는 OPC PLC 시뮬레이터에 대한 연결을 정의합니다.

자산 엔드포인트 목록을 보여 주는 작업 환경의 스크린샷

오븐 자산은 오븐이 노출하는 데이터 요소를 정의합니다.

자산 목록을 보여 주는 작업 환경의 스크린샷

데이터 흐름은 시뮬레이션된 오븐의 메시지를 처리하고 클라우드의 Event Hubs로 라우팅하는 방법을 정의합니다.

데이터 흐름 목록을 보여 주는 작업 환경의 스크린샷

다음 스크린샷은 온도 변환 데이터 흐름이 구성된 방법을 보여 줍니다.

온도 변환 계산을 보여 주는 작업 환경의 스크린샷

데이터가 MQTT broker로 흐르는지 확인

mosquitto_sub 도구를 사용하여 데이터가 MQTT 브로커로 흐르는지 확인합니다. 이 예에서는 Kubernetes 클러스터 내에서 mosquitto_sub 도구를 실행합니다.

  1. 클러스터의 MQTT 브로커와 상호 작용하는 데 유용한 mosquitto_pubmosquitto_sub 도구가 포함된 Pod를 배포하려면 다음 명령을 실행합니다.

    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
    # 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 Pod가 실행 중일 때 다음 명령을 실행하여 만든 Pod에 셸 환경을 만듭니다.

    kubectl exec --stdin --tty mqtt-client -n azure-iot-operations -- sh
    
  3. mqtt-client Pod의 Bash 셸에서 다음 명령을 실행하여 토픽을 구독한 mosquitto_sub 도구를 사용하여 MQTT broker에 data/thermostat 연결합니다.

    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/oven', ... (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/oven', ... (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/oven', ... (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/oven', ... (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/oven', ... (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 Pod를 다시 시작합니다.

  1. 다음 명령을 사용하여 aio-opc-opc.tcp-1 Pod의 이름을 찾습니다.

    kubectl get pods -n azure-iot-operations
    

    Pod의 이름은 aio-opc-opc.tcp-1-849dd78866-vhmz6과 같습니다.

  2. 다음 예제와 같은 명령을 사용하여 aio-opc-opc.tcp-1 Pod를 다시 시작합니다. 이전 단계의 aio-opc-opc.tcp-1 Pod 이름을 사용합니다.

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

이 빠른 시작의 앞부분에서 추가한 샘플 자산은 다음 예제와 같은 메시지를 생성합니다.

{
   "Temperature":{
      "SourceTimestamp":"2024-11-04T21:30:31.9454188Z",
      "Value":357
   },
   "FillWeight":{
      "SourceTimestamp":"2024-11-04T21:30:31.9455619Z",
      "Value":357
   },
   "EnergyUse":{
      "SourceTimestamp":"2024-11-04T21:30:31.9455641Z",
      "Value":357
   }
}

데이터가 Event Hubs로 흐르는지 확인

데이터가 클라우드로 흐르는지 확인하려면 Azure Portal에서 Event Hubs 인스턴스를 볼 수 있습니다. 데이터 흐름이 시작되고 메시지가 이벤트 허브로 전달되기까지 몇 분 정도 기다려야 할 수 있습니다.

이전에 적용한 Bicep 구성은 데이터 흐름에서 대상으로 사용되는 Event Hubs 네임스페이스 및 허브를 만들었습니다. 네임스페이스 및 허브를 보려면 Azure Portal에서 IoT Operations 인스턴스가 포함된 리소스 그룹으로 이동한 다음 Event Hubs 네임스페이스를 선택합니다.

메시지가 인스턴스로 흐르는 경우 인스턴스 개요 페이지에서 들어오는 메시지 수를 볼 수 있습니다.

들어오는 메시지가 있는 Event Hubs 인스턴스 개요 페이지를 보여 주는 스크린샷.

메시지가 흐르는 경우 데이터 탐색기를 사용하여 메시지를 볼 수 있습니다.

Event Hubs 인스턴스 **데이터 탐색기** 페이지의 스크린샷.

메시지를 보려면 Event Hubs 네임스페이스에 대한 Azure Event Hubs 데이터 수신기 역할에 자신을 할당해야 할 수 있습니다.

문제를 어떻게 해결했나요?

이 빠른 시작에서는 bicep 파일을 사용하여 자산 엔드포인트, 자산 및 데이터 흐름을 사용하여 Azure IoT Operations 인스턴스를 구성했습니다. 구성은 시뮬레이션된 오븐에서 데이터를 처리하고 라우팅합니다. 구성의 데이터 흐름은 메시지를 Azure Event Hubs 인스턴스로 라우팅합니다.

리소스 정리

다음 빠른 시작을 진행하는 경우에는 모든 리소스를 유지해야 합니다.

Azure IoT Operations 배포를 제거하지만 클러스터를 유지하려면 az iot ops delete 명령을 사용합니다.

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

이 빠른 시작을 위해 만든 모든 리소스를 삭제하려면 Azure IoT 작업을 배포한 Kubernetes 클러스터를 삭제한 다음 클러스터가 포함된 Azure 리소스 그룹을 제거합니다.

이러한 빠른 시작에서 Codespaces를 사용한 경우 GitHub에서 Codespace를 삭제합니다.

다음 단계

오븐 데이터에서 인사이트를 얻기 위해 Microsoft Fabric 대시보드를 빌드하는 방법을 알아보려면 자습서: 처리된 데이터에서 인사이트 가져오기를 참조하세요.