Skip to main content
Version: v1.4

EdgeX Foundry

This document mainly describes how to deploy the EdgeX system and YurtIoTDock component on an existing OpenYurt cluster using PlatformAdmin.

In OpenYurt v1.4, we have upgraded the original yurt-edgex-manager and yurt-device-controller components. The former is now built-in within yurt-manager, allowing users to create PlatformAdmin resources by writing YAML files. With just a few lines of configuration, a complete EdgeX system can be built within the node pool. The latter has been renamed as yurt-iot-dock and will be automatically deployed to the edge side when PlatformAdmin is created. This facilitates the one-click management of edge devices for users.

If you don't have an OpenYurt cluster yet, you can use the yurtadm tool to initialize an OpenYurt cluster or convert an existing Kubernetes cluster into an OpenYurt cluster.

Environment

  • OpenYurt v1.4.0+
  • You should first install yurt-manager
  • Nodes outside the same local network as the master node need to deploy a coreDNS pod.
  • Set ServiceTopology to kubernetes.io/hostname for CoreDNS service. For details, please refer to ServiceTopology

Install yurt-iot-dock environment

To deploy yurt-iot-dock using PlatformAdmin, you first need to install the helm chart for yurt-iot-dock into your Kubernetes cluster.

helm install yurt-iot-dock ./charts/yurt-iot-dock

Device platform management

1. Create Node Pool

First, create two nodepools: one is a cloud nodepool named "beijing", and the other is an edge nodepool named "hangzhou".

# Create beijing nodepool
cat << EOF | kubectl apply -f -
apiVersion: apps.openyurt.io/v1beta1
kind: NodePool
metadata:
  name: beijing
spec:
  type: Cloud
EOF

# Create hangzhou nodepool
cat << EOF | kubectl apply -f -
apiVersion: apps.openyurt.io/v1beta1
kind: NodePool
metadata:
  name: hangzhou
spec:
  type: Edge
EOF

Next, add the specified nodes to their respective node pools. Label the node "openyurt-worker" as a cloud node, and label the node "openyurt-worker2" as an edge node.

# Mark "openyurt-worker" as a cloud node
kubectl label node openyurt-worker apps.openyurt.io/nodepool=beijing
# Mark "openyurt-worker2" as an edge node
kubectl label node openyurt-worker2 apps.openyurt.io/nodepool=hangzhou

Finally, check the status of the nodepools to ensure they are in a healthy state.

# Check the status of the nodepools
kubectl get np
NAME       TYPE    READYNODES   NOTREADYNODES   AGE
beijing    Cloud   1            0               4d18h
hangzhou   Edge    1            0               4d18h

2. Create the IoT system PlatformAdmin within the node pool

Configure the use of the EdgeX version and select to create it within the "hangzhou" nodepool.

# Create an EdgeX deployment of the "Minnesota" version within the "hangzhou" node pool
cat <<EOF | kubectl apply -f -
apiVersion: iot.openyurt.io/v1alpha2
kind: PlatformAdmin
metadata:
  name: edgex-sample
spec:
  version: minnesota
  poolName: hangzhou
EOF

# Verify the deployment status to ensure everything is set up correctly
kubectl get po
NAME                                                              READY   STATUS    RESTARTS   AGE
edgex-core-command-hangzhou-4j6pz-8668ff94d7-hqw2r                1/1     Running   0          61s
edgex-core-common-config-bootstrapper-hangzhou-jnw2q-57bd99xr9p   1/1     Running   0          61s
edgex-core-consul-hangzhou-6p9tj-798489c647-6xz4m                 1/1     Running   0          61s
edgex-core-metadata-hangzhou-6l7v5-6f964fc4f-67f9p                1/1     Running   0          61s
edgex-redis-hangzhou-cwgsw-5c7d7fc478-fsgp9                       1/1     Running   0          61s

3. Deploy optional components

In the current version (v1.4.0) of PlatformAdmin, you can use the "components" field to deploy optional components with a single command. Below is an example of deploying "yurt-iot-dock," "edgex-device-virtual," and "edgex-device-rest" using the "components" field:

Refer to the Components Documentation for component names for optional components.

# Add the "components" field on top of the previously deployed PlatformAdmin
cat <<EOF | kubectl apply -f -
apiVersion: iot.openyurt.io/v1alpha2
kind: PlatformAdmin
metadata:
  name: edgex-sample
spec:
  version: minnesota
  poolName: hangzhou
  components:
  - name: yurt-iot-dock
  - name: edgex-device-virtual
  - name: edgex-device-rest
EOF

# You can see that the optional components have been deployed
kubectl get po
NAME                                                              READY   STATUS    RESTARTS   AGE
edgex-core-command-hangzhou-cwgs2-77bb5d9cdd-zp89r                1/1     Running   0          20m
edgex-core-common-config-bootstrapper-hangzhou-bqhnb-57bd9c4q5q   1/1     Running   0          20m
edgex-core-consul-hangzhou-5rl7c-66dbc9c7d7-dqvm8                 1/1     Running   0          20m
edgex-core-metadata-hangzhou-srpff-dd6c6f9cb-2cj9k                1/1     Running   0          20m
edgex-device-rest-hangzhou-v7p99-7b8bb4f5d4-kz8sq                 1/1     Running   0          7m49s
edgex-device-virtual-hangzhou-ssz59-796f948c69-5k4tc              1/1     Running   0          7m49s
edgex-redis-hangzhou-bk5g5-5fbdf6fffb-cmf6d                       1/1     Running   0          20m
yurt-iot-dock-hangzhou-56f98-8549f848f5-v2pjn                     1/1     Running   0          7m49s

4. Modify component configuration

PlatformAdmin provides advanced users with an entry point for custom configurations. All configurations of PlatformAdmin are controlled by a configmap named "platformadmin-framework". By modifying the values of this configmap, users can modify the configuration of each component. Here is an example of the "platformadmin-framework" configmap:

apiVersion: v1
data:
  framework: |
    components:
    - deployment:
        selector:
          matchLabels:
            app: edgex-core-command
        strategy: {}
        template:
          metadata:
            creationTimestamp: null
            labels:
              app: edgex-core-command
          spec:
            containers:
            - env:
              - name: SERVICE_HOST
                value: edgex-core-command
              - name: EXTERNALMQTT_URL
                value: tcp://edgex-mqtt-broker:1883
              envFrom:
              - configMapRef:
                  name: common-variables
              image: openyurt/core-command:3.0.0
              imagePullPolicy: IfNotPresent
              name: edgex-core-command
              ports:
              - containerPort: 59882
                name: tcp-59882
                protocol: TCP
              resources: {}
            hostname: edgex-core-command
      name: edgex-core-command
      service:
        ports:
        - name: tcp-59882
          port: 59882
          protocol: TCP
          targetPort: 59882
        selector:
          app: edgex-core-command
...

5. Add components manually

Considering that some users may need to add their own custom components or modify existing components, the component mechanism of PlatformAdmin also supports adding new components. To add a new component, please follow these steps:

Configure PlatformAdmin

Add the name of the component you want to add to the "components" field in PlatformAdmin. For example, if we want to add a component named "nginx-demo."

# Add nginx-demo in components
cat <<EOF | kubectl apply -f -
apiVersion: iot.openyurt.io/v1alpha2
kind: PlatformAdmin
metadata:
  name: edgex-sample
spec:
  version: minnesota
  poolName: hangzhou
  components:
  - name: yurt-iot-dock
  - name: edgex-device-virtual
  - name: edgex-device-rest
  - name: nginx-demo
EOF

Edit PlatformAdminFramework

Since the AutoCollector does not collect a standard configuration file for the "nginx-demo" component, there is no corresponding configuration in the platformadmin-framework. In this case, users can manually add this component.

# Modify the contents of the configmap using kubectl edit
kubectl edit cm platformadmin-framework

# Added deployment and service for nginx-demo
apiVersion: v1
data:
  framework: |
    components:
    - deployment:
        selector:
          matchLabels:
            app: nginx-demo
        strategy: {}
        template:
          metadata:
            creationTimestamp: null
            labels:
              app: nginx-demo
          spec:
            containers:
            - image: nginx
              imagePullPolicy: IfNotPresent
              name: nginx-demo
              ports:
              - containerPort: 80
                name: nginx
                protocol: TCP
              resources: {}
            hostname: nginx-demo
      name: nginx-demo
      service:
        ports:
        - name: nginx
          port: 80
          protocol: TCP
          targetPort: 80
        selector:
          app: nginx-demo
...

End device management

Next, we introduce the end-device management function of yurt-iot-dock, using virtual devices as a case study.

1. Add device-virtual components manually

To make things easy, we just deploy a virtual device driver device-virtual-go.

It simulates different kinds of devices to generate device data, and users can send commands to get responses from or conduct control instructions to the devices.

First, we need to ensure that edgex-device-virtual is existing in the configmap of the PlatformAdmin framework:

# Get the contents of the configmap using kubectl edit
kubectl get cm platformadmin-framework -o yaml

apiVersion: v1
data:
  framework: |
    components:
    - deployment:
        selector:
          matchLabels:
            app: edgex-device-virtual
        strategy: {}
        template:
          metadata:
            creationTimestamp: null
            labels:
              app: edgex-device-virtual
          spec:
            containers:
            - env:
              - name: SERVICE_HOST
                value: edgex-device-virtual
              envFrom:
              - configMapRef:
                  name: common-variables
              image: openyurt/device-virtual:3.0.0
              imagePullPolicy: IfNotPresent
              name: edgex-device-virtual
              ports:
              - containerPort: 59900
                name: tcp-59900
                protocol: TCP
              resources: {}
            hostname: edgex-device-virtual
      name: edgex-device-virtual
      service:
        ports:
        - name: tcp-59900
          port: 59900
          protocol: TCP
          targetPort: 59900
        selector:
          app: edgex-device-virtual
...

The device-virtual-go component automatically creates and registers the deviceservice, the 5 different types of devices and their deviceprofiles at startup, and the yurt-iot-dock component synchronizes them all to OpenYurt. So you can check it with kubectl:

$ kubectl get deviceservice
NAME                      NODEPOOL   SYNCED   AGE
hangzhou-device-virtual   hangzhou   true     2d1h

$ kubectl get device
NAME                                     NODEPOOL   SYNCED   AGE
hangzhou-random-binary-device            hangzhou   true     2d1h
hangzhou-random-boolean-device           hangzhou   true     2d1h
hangzhou-random-float-device             hangzhou   true     2d1h
hangzhou-random-integer-device           hangzhou   true     2d1h
hangzhou-random-unsignedinteger-device   hangzhou   true     2d1h

$ kubectl get deviceprofile
NAME                                     NODEPOOL   SYNCED   AGE
hangzhou-random-binary-device            hangzhou   true     2d1h
hangzhou-random-boolean-device           hangzhou   true     2d1h
hangzhou-random-float-device             hangzhou   true     2d1h
hangzhou-random-integer-device           hangzhou   true     2d1h
hangzhou-random-unsignedinteger-device   hangzhou   true     2d1h

2. Create Device, DeviceProfile

In addition to synchronizing devices, device profiles, and device services in edgex by means of preconfigured configurations, the Openyurt side also provides a more general way to create devices and deviceprofiles.

  1. Create a DeviceProfile
apiVersion: iot.openyurt.io/v1alpha1
kind: DeviceProfile
metadata:
  name: openyurt-created-random-boolean-deviceprofile
spec:
  description: Example of Device-Virtual Created By OpenYurt
  deviceCommands:
  - isHidden: false
    name: WriteBoolValue
    readWrite: W
    resourceOperations:
    - defaultValue: ""
      deviceResource: Bool
    - defaultValue: "false"
      deviceResource: EnableRandomization_Bool
  - isHidden: false
    name: WriteBoolArrayValue
    readWrite: W
    resourceOperations:
    - defaultValue: ""
      deviceResource: BoolArray
    - defaultValue: "false"
      deviceResource: EnableRandomization_BoolArray
  deviceResources:
  - description: used to decide whether to re-generate a random value
    isHidden: true
    name: EnableRandomization_Bool
    properties:
      defaultValue: "true"
      readWrite: W
      valueType: Bool
  - description: Generate random boolean value
    isHidden: false
    name: Bool
    properties:
      defaultValue: "true"
      readWrite: RW
      valueType: Bool
  - description: used to decide whether to re-generate a random value
    isHidden: true
    name: EnableRandomization_BoolArray
    properties:
      defaultValue: "true"
      readWrite: W
      valueType: Bool
  - description: Generate random boolean array value
    isHidden: false
    name: BoolArray
    properties:
      defaultValue: '[true]'
      readWrite: RW
      valueType: BoolArray
  labels:
  - openyurt-created-device-virtual-example
  manufacturer: OpenYurt Community
  model: OpenYurt-Device-Virtual-01
  nodePool: hangzhou

This DeviceProfile is just a copy of random-boolean DeviceProfile created by device-virtual-go for demo purpose.

  1. Create a Device

Create a virtual device using the pre-synchronized DeviceService and the DeviceProfile created above:

apiVersion: iot.openyurt.io/v1alpha1
kind: Device
metadata:
  name: openyurt-created-random-boolean-device
spec:
  adminState: UNLOCKED
  description: Example of Device Virtual
  labels:
  - openyurt-created-device-virtual-example
  managed: true
  nodePool: hangzhou
  notify: true
  operatingState: UP
  profileName: openyurt-created-random-boolean-deviceprofile
  protocols:
    other:
      Address: openyurt-created-device-virtual-bool-01
      Port: "300"
  serviceName: device-virtual

Then, we can see the resource objects in OpenYurt through kubectl as below:

$ kubectl get deviceprofile openyurt-created-random-boolean-deviceprofile
NAME                                            NODEPOOL   SYNCED   AGE
openyurt-created-random-boolean-deviceprofile   hangzhou   true     15h

$ kubectl get device openyurt-created-random-boolean-device
NAME                                     NODEPOOL   SYNCED   AGE
openyurt-created-random-boolean-device   hangzhou   true     14h

4. Retrieve device generated data

We have already set up the environment and simulated a virtual bool device.

In OpenYurt, we can easily get the latest data generated by devices just by checking the status sub-resource of Device resource object like this:

$ kubectl get device openyurt-created-random-boolean-device -o yaml
apiVersion: iot.openyurt.io/v1alpha1
kind: Device
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"iot.openyurt.io/v1alpha1","kind":"Device","metadata":{"annotations":{},"name":"openyurt-boolean-device","namespace":"default"},"spec":{"adminState":"UNLOCKED","description":"Example of Device Virtual","labels":["openyurt-device-boolean-virtual"],"managed":true,"nodePool":"hangzhou","notify":true,"operatingState":"UP","profileName":"Random-Boolean-Device","protocols":{"other":{"Address":"openyurt-device-boolean-virtual-01","Port":"301"}},"serviceName":"openyurt-boolean-device"}}
  creationTimestamp: "2023-09-14T06:25:10Z"
  finalizers:
  - iot.openyurt.io/device
  generation: 2
  name: openyurt-boolean-device
  namespace: default
  resourceVersion: "1717015"
  uid: 6677eb4a-b644-4d5d-970a-1446f141a353
spec:
  adminState: UNLOCKED
  description: Example of Device Virtual
  deviceProperties:
    Bool:
      desiredValue: "true"
      name: Bool
  labels:
  - openyurt-created-device-virtual-example
  managed: false
  nodePool: hangzhou
  notify: true
  operatingState: UP
  profileName: openyurt-created-random-boolean-deviceprofile
  protocols:
    other:
      Address: openyurt-created-device-virtual-bool-01
      Port: "300"
  serviceName: device-virtual
status:
  adminState: UNLOCKED
  deviceProperties:
    Bool:
      actualValue: "true"
      getURL: http://edgex-core-command:59882/api/v3/device/name/openyurt-boolean-device/Bool
      name: Bool
    BoolArray:
      actualValue: '[true, true, true, false, false]'
      getURL: http://edgex-core-command:59882/api/v3/device/name/openyurt-boolean-device/BoolArray
      name: BoolArray
  edgeId: 5e63effd-deeb-4505-890e-17ec32f02511
  operatingState: UP
  synced: true

The deviceProperties shows all the properties of this device.

For example, the Bool property has the latest value false and the value is retrieved from the EdgeX rest api http://edgex-core-command:59882/api/v2/device/name/openyurt-created-random-boolean-device/Bool.

5. Update the properties of device

If you want to control the device by updating its writable attributes, you should first set the Device.Spec.Managed field to true to indicate that yurt-iot-dock takes over the device, otherwise all update operations will be ignored.

  1. Set the managed field of device to true
kubectl patch device openyurt-created-random-boolean-device -p '{"spec":{"managed":true}}'  --type=merge
  1. Change the adminState of device

The administrative state (aka admin state) provides control of the device service by man or other systems. It can be set to LOCKED or UNLOCKED. When a device service is set to locked, it is not supposed to respond to any command requests nor send data from the devices.

kubectl patch device openyurt-created-random-boolean-device -p '{"spec":{"adminState":"UNLOCKED"}}'  --type=merge

Set the DeviceProperties to control/update device

kubectl patch device openyurt-created-random-boolean-device --type=merge -p '{"spec":{"managed":true,"deviceProperties":{"Bool": {"name":"Bool", "desiredValue":"false"}}}}'

In the command, we set the Bool DeviceProperty value to false, yurt-iot-dock will trigger a EdgeX command and change the property of the device. We can check this by watch the status of device for multiple times, you will find the value is always false unless you change this property to true again.

watch "kubectl get device openyurt-created-random-boolean-device -o json | jq '.status.deviceProperties.Bool.actualValue'"

# output
Every 2.0s: kubectl get device openyurt-boolean-device -o json | jq '.status.deviceProperties.Bool.actualValue'                                VM-16-6-ubuntu: Sat Sep 16 16:39:58 2023

"false"