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.

  1. 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”.

  1. # Create beijing nodepool
  2. cat << EOF | kubectl apply -f -
  3. apiVersion: apps.openyurt.io/v1beta1
  4. kind: NodePool
  5. metadata:
  6. name: beijing
  7. spec:
  8. type: Cloud
  9. EOF
  10. # Create hangzhou nodepool
  11. cat << EOF | kubectl apply -f -
  12. apiVersion: apps.openyurt.io/v1beta1
  13. kind: NodePool
  14. metadata:
  15. name: hangzhou
  16. spec:
  17. type: Edge
  18. 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.

  1. # Mark "openyurt-worker" as a cloud node
  2. kubectl label node openyurt-worker apps.openyurt.io/nodepool=beijing
  3. # Mark "openyurt-worker2" as an edge node
  4. 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.

  1. # Check the status of the nodepools
  2. kubectl get np
  3. NAME TYPE READYNODES NOTREADYNODES AGE
  4. beijing Cloud 1 0 4d18h
  5. 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.

  1. # Create an EdgeX deployment of the "Minnesota" version within the "hangzhou" node pool
  2. cat <<EOF | kubectl apply -f -
  3. apiVersion: iot.openyurt.io/v1alpha2
  4. kind: PlatformAdmin
  5. metadata:
  6. name: edgex-sample
  7. spec:
  8. version: minnesota
  9. poolName: hangzhou
  10. EOF
  11. # Verify the deployment status to ensure everything is set up correctly
  12. kubectl get po
  13. NAME READY STATUS RESTARTS AGE
  14. edgex-core-command-hangzhou-4j6pz-8668ff94d7-hqw2r 1/1 Running 0 61s
  15. edgex-core-common-config-bootstrapper-hangzhou-jnw2q-57bd99xr9p 1/1 Running 0 61s
  16. edgex-core-consul-hangzhou-6p9tj-798489c647-6xz4m 1/1 Running 0 61s
  17. edgex-core-metadata-hangzhou-6l7v5-6f964fc4f-67f9p 1/1 Running 0 61s
  18. 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.

  1. # Add the "components" field on top of the previously deployed PlatformAdmin
  2. cat <<EOF | kubectl apply -f -
  3. apiVersion: iot.openyurt.io/v1alpha2
  4. kind: PlatformAdmin
  5. metadata:
  6. name: edgex-sample
  7. spec:
  8. version: minnesota
  9. poolName: hangzhou
  10. components:
  11. - name: yurt-iot-dock
  12. - name: edgex-device-virtual
  13. - name: edgex-device-rest
  14. EOF
  15. # You can see that the optional components have been deployed
  16. kubectl get po
  17. NAME READY STATUS RESTARTS AGE
  18. edgex-core-command-hangzhou-cwgs2-77bb5d9cdd-zp89r 1/1 Running 0 20m
  19. edgex-core-common-config-bootstrapper-hangzhou-bqhnb-57bd9c4q5q 1/1 Running 0 20m
  20. edgex-core-consul-hangzhou-5rl7c-66dbc9c7d7-dqvm8 1/1 Running 0 20m
  21. edgex-core-metadata-hangzhou-srpff-dd6c6f9cb-2cj9k 1/1 Running 0 20m
  22. edgex-device-rest-hangzhou-v7p99-7b8bb4f5d4-kz8sq 1/1 Running 0 7m49s
  23. edgex-device-virtual-hangzhou-ssz59-796f948c69-5k4tc 1/1 Running 0 7m49s
  24. edgex-redis-hangzhou-bk5g5-5fbdf6fffb-cmf6d 1/1 Running 0 20m
  25. 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:

  1. apiVersion: v1
  2. data:
  3. framework: |
  4. components:
  5. - deployment:
  6. selector:
  7. matchLabels:
  8. app: edgex-core-command
  9. strategy: {}
  10. template:
  11. metadata:
  12. creationTimestamp: null
  13. labels:
  14. app: edgex-core-command
  15. spec:
  16. containers:
  17. - env:
  18. - name: SERVICE_HOST
  19. value: edgex-core-command
  20. - name: EXTERNALMQTT_URL
  21. value: tcp://edgex-mqtt-broker:1883
  22. envFrom:
  23. - configMapRef:
  24. name: common-variables
  25. image: openyurt/core-command:3.0.0
  26. imagePullPolicy: IfNotPresent
  27. name: edgex-core-command
  28. ports:
  29. - containerPort: 59882
  30. name: tcp-59882
  31. protocol: TCP
  32. resources: {}
  33. hostname: edgex-core-command
  34. name: edgex-core-command
  35. service:
  36. ports:
  37. - name: tcp-59882
  38. port: 59882
  39. protocol: TCP
  40. targetPort: 59882
  41. selector:
  42. app: edgex-core-command
  43. ...

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.”

  1. # Add nginx-demo in components
  2. cat <<EOF | kubectl apply -f -
  3. apiVersion: iot.openyurt.io/v1alpha2
  4. kind: PlatformAdmin
  5. metadata:
  6. name: edgex-sample
  7. spec:
  8. version: minnesota
  9. poolName: hangzhou
  10. components:
  11. - name: yurt-iot-dock
  12. - name: edgex-device-virtual
  13. - name: edgex-device-rest
  14. - name: nginx-demo
  15. 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.

  1. # Modify the contents of the configmap using kubectl edit
  2. kubectl edit cm platformadmin-framework
  3. # Added deployment and service for nginx-demo
  4. apiVersion: v1
  5. data:
  6. framework: |
  7. components:
  8. - deployment:
  9. selector:
  10. matchLabels:
  11. app: nginx-demo
  12. strategy: {}
  13. template:
  14. metadata:
  15. creationTimestamp: null
  16. labels:
  17. app: nginx-demo
  18. spec:
  19. containers:
  20. - image: nginx
  21. imagePullPolicy: IfNotPresent
  22. name: nginx-demo
  23. ports:
  24. - containerPort: 80
  25. name: nginx
  26. protocol: TCP
  27. resources: {}
  28. hostname: nginx-demo
  29. name: nginx-demo
  30. service:
  31. ports:
  32. - name: nginx
  33. port: 80
  34. protocol: TCP
  35. targetPort: 80
  36. selector:
  37. app: nginx-demo
  38. ...

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:

  1. # Get the contents of the configmap using kubectl edit
  2. kubectl get cm platformadmin-framework -o yaml
  3. apiVersion: v1
  4. data:
  5. framework: |
  6. components:
  7. - deployment:
  8. selector:
  9. matchLabels:
  10. app: edgex-device-virtual
  11. strategy: {}
  12. template:
  13. metadata:
  14. creationTimestamp: null
  15. labels:
  16. app: edgex-device-virtual
  17. spec:
  18. containers:
  19. - env:
  20. - name: SERVICE_HOST
  21. value: edgex-device-virtual
  22. envFrom:
  23. - configMapRef:
  24. name: common-variables
  25. image: openyurt/device-virtual:3.0.0
  26. imagePullPolicy: IfNotPresent
  27. name: edgex-device-virtual
  28. ports:
  29. - containerPort: 59900
  30. name: tcp-59900
  31. protocol: TCP
  32. resources: {}
  33. hostname: edgex-device-virtual
  34. name: edgex-device-virtual
  35. service:
  36. ports:
  37. - name: tcp-59900
  38. port: 59900
  39. protocol: TCP
  40. targetPort: 59900
  41. selector:
  42. app: edgex-device-virtual
  43. ...

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:

  1. $ kubectl get deviceservice
  2. NAME NODEPOOL SYNCED AGE
  3. hangzhou-device-virtual hangzhou true 2d1h
  4. $ kubectl get device
  5. NAME NODEPOOL SYNCED AGE
  6. hangzhou-random-binary-device hangzhou true 2d1h
  7. hangzhou-random-boolean-device hangzhou true 2d1h
  8. hangzhou-random-float-device hangzhou true 2d1h
  9. hangzhou-random-integer-device hangzhou true 2d1h
  10. hangzhou-random-unsignedinteger-device hangzhou true 2d1h
  11. $ kubectl get deviceprofile
  12. NAME NODEPOOL SYNCED AGE
  13. hangzhou-random-binary-device hangzhou true 2d1h
  14. hangzhou-random-boolean-device hangzhou true 2d1h
  15. hangzhou-random-float-device hangzhou true 2d1h
  16. hangzhou-random-integer-device hangzhou true 2d1h
  17. 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
  1. apiVersion: iot.openyurt.io/v1alpha1
  2. kind: DeviceProfile
  3. metadata:
  4. name: openyurt-created-random-boolean-deviceprofile
  5. spec:
  6. description: Example of Device-Virtual Created By OpenYurt
  7. deviceCommands:
  8. - isHidden: false
  9. name: WriteBoolValue
  10. readWrite: W
  11. resourceOperations:
  12. - defaultValue: ""
  13. deviceResource: Bool
  14. - defaultValue: "false"
  15. deviceResource: EnableRandomization_Bool
  16. - isHidden: false
  17. name: WriteBoolArrayValue
  18. readWrite: W
  19. resourceOperations:
  20. - defaultValue: ""
  21. deviceResource: BoolArray
  22. - defaultValue: "false"
  23. deviceResource: EnableRandomization_BoolArray
  24. deviceResources:
  25. - description: used to decide whether to re-generate a random value
  26. isHidden: true
  27. name: EnableRandomization_Bool
  28. properties:
  29. defaultValue: "true"
  30. readWrite: W
  31. valueType: Bool
  32. - description: Generate random boolean value
  33. isHidden: false
  34. name: Bool
  35. properties:
  36. defaultValue: "true"
  37. readWrite: RW
  38. valueType: Bool
  39. - description: used to decide whether to re-generate a random value
  40. isHidden: true
  41. name: EnableRandomization_BoolArray
  42. properties:
  43. defaultValue: "true"
  44. readWrite: W
  45. valueType: Bool
  46. - description: Generate random boolean array value
  47. isHidden: false
  48. name: BoolArray
  49. properties:
  50. defaultValue: '[true]'
  51. readWrite: RW
  52. valueType: BoolArray
  53. labels:
  54. - openyurt-created-device-virtual-example
  55. manufacturer: OpenYurt Community
  56. model: OpenYurt-Device-Virtual-01
  57. 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:

  1. apiVersion: iot.openyurt.io/v1alpha1
  2. kind: Device
  3. metadata:
  4. name: openyurt-created-random-boolean-device
  5. spec:
  6. adminState: UNLOCKED
  7. description: Example of Device Virtual
  8. labels:
  9. - openyurt-created-device-virtual-example
  10. managed: true
  11. nodePool: hangzhou
  12. notify: true
  13. operatingState: UP
  14. profileName: openyurt-created-random-boolean-deviceprofile
  15. protocols:
  16. other:
  17. Address: openyurt-created-device-virtual-bool-01
  18. Port: "300"
  19. serviceName: device-virtual

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

  1. $ kubectl get deviceprofile openyurt-created-random-boolean-deviceprofile
  2. NAME NODEPOOL SYNCED AGE
  3. openyurt-created-random-boolean-deviceprofile hangzhou true 15h
  4. $ kubectl get device openyurt-created-random-boolean-device
  5. NAME NODEPOOL SYNCED AGE
  6. 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:

  1. $ kubectl get device openyurt-created-random-boolean-device -o yaml
  2. apiVersion: iot.openyurt.io/v1alpha1
  3. kind: Device
  4. metadata:
  5. annotations:
  6. kubectl.kubernetes.io/last-applied-configuration: |
  7. {"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"}}
  8. creationTimestamp: "2023-09-14T06:25:10Z"
  9. finalizers:
  10. - iot.openyurt.io/device
  11. generation: 2
  12. name: openyurt-boolean-device
  13. namespace: default
  14. resourceVersion: "1717015"
  15. uid: 6677eb4a-b644-4d5d-970a-1446f141a353
  16. spec:
  17. adminState: UNLOCKED
  18. description: Example of Device Virtual
  19. deviceProperties:
  20. Bool:
  21. desiredValue: "true"
  22. name: Bool
  23. labels:
  24. - openyurt-created-device-virtual-example
  25. managed: false
  26. nodePool: hangzhou
  27. notify: true
  28. operatingState: UP
  29. profileName: openyurt-created-random-boolean-deviceprofile
  30. protocols:
  31. other:
  32. Address: openyurt-created-device-virtual-bool-01
  33. Port: "300"
  34. serviceName: device-virtual
  35. status:
  36. adminState: UNLOCKED
  37. deviceProperties:
  38. Bool:
  39. actualValue: "true"
  40. getURL: http://edgex-core-command:59882/api/v3/device/name/openyurt-boolean-device/Bool
  41. name: Bool
  42. BoolArray:
  43. actualValue: '[true, true, true, false, false]'
  44. getURL: http://edgex-core-command:59882/api/v3/device/name/openyurt-boolean-device/BoolArray
  45. name: BoolArray
  46. edgeId: 5e63effd-deeb-4505-890e-17ec32f02511
  47. operatingState: UP
  48. 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
  1. 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.

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

Set the DeviceProperties to control/update device

  1. 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.

  1. watch "kubectl get device openyurt-created-random-boolean-device -o json | jq '.status.deviceProperties.Bool.actualValue'"
  2. # output
  3. 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
  4. "false"