云原生边缘设备解决方案Akri on k3s初体验

时间:2022-11-22 14:09:41

云原生边缘设备解决方案Akri on k3s初体验

作者:

涂家英,SUSE 资深架构师,专注 Cloud-Native 相关产品和解决方案设计,在企业级云原生平台建设领域拥有丰富的经验。

写在前面

k3s 是 SUSE 推出的为物联网和边缘计算构建的经过认证的 Kubernetes 发行版,它可以帮助用户在资源受限的场景下使用 kubernetes,并结合 SUSE Rancher 实现云边协同。

将 k3s 与微软开源的 kubernetes 边缘项目 Akri 结合使用,用户就拥有了在边缘环境发现和使用各种 IOT 设备的能力。

架构

Akri 目前是 CNCF 的一个开源项目,其功能简单地说就是可以根据配置自动地寻找到相应的 iot 设备,为其创建关联的工作负载,并且通过不断地检测实现工作负载的动态变化。引用一句官方的描述为:You name it, Akri finds it, you use it.

架构如下:

云原生边缘设备解决方案Akri on k3s初体验

包含了 Controller 服务、Agent 服务和 Discovery Handlers 服务以及两个 CRD 资源。

具体的组件解析可以查看官方文档:https://docs.akri.sh/architecture/architecture-overview

下面我们通过一个示例来更好地理解 Akri 的工作方式。

体验

示例使用了官方提供的一个 OPC UA 温度计 Demo,OPC UA 是现在使用比较广泛的工业自动化通信协议,我们可以通过 Akri 自动发现使用 OPU CA 协议的温度计设备,并为其创建工作负载,采集和使用其产生的温度数据。

示例中体现的大致工作流程如下:

云原生边缘设备解决方案Akri on k3s初体验

首先需要模拟出两台 OPC UA 的服务设备,然后在 k3s 集群上部署 Akri 服务,基础工作完成后:

  1. 向集群配置用于发现 OPC UA 设备的 CRD(Configuration)
  2. CRD 下发后,Akri 的相应 Discovery 服务会按照规则寻找 OPC UA 设备
  3. 在找到 OPC UA 设备后,Agent 服务会生成设备对应的 CRD(Instance),Controller 服务查看到 Instance CRD 资源后,将创建对应的工作负载

OPC UA 设备模拟

使用了一个.NET 类型的开源项目模拟实现 OPU CA 设备,项目地址为:https://gitee.com/leotuss/opcua-donet

克隆到本地后,需要修改以下文件:

# /opcua-dotnet/Applications/ConsoleReferenceServer/Quickstarts.ReferenceServer.Config.xml文件第76、77行
将<node-ip style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">替换为节点地址

#/opcua-dotnet/Applications/ReferenceServer/Quickstarts.ReferenceServer.Config.xml文件第77、78行
将<node-ip style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">替换为节点地址</node-ip></node-ip>` 

Linux 要运行这个程序,需要安装.NET core 的 SDK 和 runtime

可以使用以下命令运行此程序:

dotnet run --project /opcua-dotnet/Applications/ConsoleReferenceServer/NetCoreReferenceServer.csproj

运行后,可以看到如下提示:

root@edge-iot1:~/opcua-dotnet/Applications/ConsoleReferenceServer# dotnet run --project NetCoreReferenceServer.csproj
.Net Core OPC UA Reference Server
opc.tcp://192.168.6.151:62541/Quickstarts/ReferenceServer
https://192.168.6.151:62540/Quickstarts/ReferenceServer/
Server started. Press Ctrl-C to exit...

至此程序运行成功,应用可以通过订阅opc.tcp://:62541/Quickstarts/ReferenceServer获得数据

安装 Akri

可以通过 Helm 安装 Akri:

# 添加Akri repo
helm repo add akri-helm-charts https://project-akri.github.io/akri/

# 部署Akri
helm install akri akri-helm-charts/akri\
    --set kubernetesDistro=k3s \
    --set opcua.discovery.enabled=true

关于 kubernetesDistro 配置,Akri 依赖 crictl 跟踪 pod 信息,所以必须知道容器运行时 socket 的位置。目前 Akri 支持四种类型:

  • 标准 kuberentes,对应配置为:--set kubernetesDistro=k8s
  • k3s,对应配置为:--set kubernetesDistro=k3s
  • microk8s,对应配置为:--set kubernetesDistro=microk8s
  • 其它,对应配置为:--set agent.host.containerRuntimeSocket=/container/runtime.sock

关于 xxx.discovery.enabled 配置,Akri 目前支持三种设备发现:

  • onvif:IP Cameras 的主流协议
  • opc ua:工业自动化通信协议
  • udev:linux 设备管理器

如我们需要 Akri 发现 onvif 设备,就可以配置 --set onvif.discovery.enabled=true,配置后 Akri 会在集群中部署相应的 Discovery 服务,以 Daemonset 的方式运行,支持叠加部署,如需要发现上述三种类型设备,部署命令可以修改为:

helm install akri akri-helm-charts/akri\
    --set kubernetesDistro=k3s \
    --set opcua.discovery.enabled=true \
    --set onvif.discovery.enabled=true \
    --set udev.discovery.enabled=true

部署完成后查看集群 Pods 可以看到:

root@edge-k3s:~# kubectl get pods
NAME                                         READY   STATUS    RESTARTS       AGE
akri-controller-deployment-d4f7847b6-rlgrr   1/1     Running   11 (25h ago)   4d2h
akri-agent-daemonset-9s9m9                   1/1     Running   10 (25h ago)   3d23h
akri-opcua-discovery-daemonset-tv84d         1/1     Running   8 (25h ago)    3d17h

部署 CRD

使用 Akri 发现设备需要部署类型为 Configuration 的 CRD:

apiVersion: akri.sh/v0
kind: Configuration
metadata:
 name: akri-opcua-monitoring
 namespace: default
spec:
 brokerProperties:
   IDENTIFIER: Thermometer_Temperature
   NAMESPACE_INDEX: "2"
 brokerSpec:
   brokerPodSpec:
     containers:
     - image: ghcr.io/project-akri/akri/opcua-monitoring-broker:latest
       name: akri-opcua-monitoring-broker
       resources:
         limits:
           '{{PLACEHOLDER}}': "1"
           cpu: 30m
           memory: 200Mi
         requests:
           '{{PLACEHOLDER}}': "1"
           cpu: 9m
           memory: 76Mi
 capacity: 1
 configurationServiceSpec:
   ports:
   - name: grpc
     port: 80
     protocol: TCP
     targetPort: 8083
   type: ClusterIP
 discoveryHandler:
   name: opcua
   discoveryDetails: |+
     opcuaDiscoveryMethod:
       standard:
         discoveryUrls:
         - opc.tcp://192.168.6.151:62541/Quickstarts/ReferenceServer/
         - opc.tcp://192.168.6.152:62541/Quickstarts/ReferenceServer/
     applicationNames:
       action: Exclude
       items: []
   name: opcua
 instanceServiceSpec:
   ports:
   - name: grpc
     port: 80
     protocol: TCP
     targetPort: 8083
   type: ClusterIP

需要关注的配置:

  • spec.brokerProperties: 用于定义需要采集数据的 ID 信息,本演示中 opcua 程序中添加了 IDENTIFIER:为Thermometer_TemperatureNAMESPACE_INDEX: "2"的数据输出,数据输出内容为 70-80 的随机数,并且在随机到 75 时,将 75 替换为 120
  • spec.brokerSpec.brokerPodSpec.containers.image: 设备对应工作负载的镜像,演示使用的是官方提供的镜像,作用是订阅 opcua 所产生的相应数据,此镜像是可以自定义的,实现更多可能
  • spec.capacity:针对设备的工作负载副本数量,用于实现工作负载高可用
  • spec.discoveryHandler: 这部分主要定义了发现 opuca 设备的规则,支持一些过滤规则
  • spec.configurationServiceSpec:Akri Controller 服务会为所有设备的工作负载创建一个总 svc,这段用于定义相应的 svc 的配置
  • spec.instanceServiceSpec: Akri Controller 服务会为每一个工作负载创建 svc,这段用于定义相应 svc 的配置

示例中可以看到 opuca 的发现规则是具体的服务地址,如果要支持批量的 opuca 设备发现,可以使用 Local discovery server(LDS),将 opcua 的设备注册到 LDS 中,然后在 discoveryUrls 配置中使用 LDS 的地址。采用 LDS 方式的话,可以实现过滤能力,如排除掉哪些 opcua 服务或者包含哪些 opcua 服务,示例如下:

# 发现LDS中的所有opcua设备,除了<someserver style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">以外</someserver>
discoveryDetails: |+
 opcuaDiscoveryMethod:
   standard:
     discoveryUrls:
     - opc.tcp://<lds服务器地址 style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">:4840</lds服务器地址>
 applicationNames:
   action: Exclude
   items:
   -

将 akri-opcua-monitoring 的 crd 部署到集群后,可以通过 kubectl get akric 查看:

root@edge-k3s:~/akric-crd# kubectl get akric
NAME                    CAPACITY   AGE
akri-opcua-monitoring   1          2m13s

查看 Discovery 的服务日志可以看到,两个 opcua 设备已经被发现:

[2022-11-10T08:26:26Z TRACE akri_opcua::discovery_impl] get_discovery_url_from_application - found server : Quickstart Reference Server
[2022-11-10T08:26:26Z TRACE akri_opcua::discovery_impl] get_discovery_url_from_application - server has [UAString { value: Some("https://192.168.6.151:62540/Quickstarts/ReferenceServer/discovery") }, UAString { value: Some("opc.tcp://192.168.6.151:62541/Quickstarts/ReferenceServer") }] DiscoveryUrls
[2022-11-10T08:26:26Z TRACE akri_opcua::discovery_impl] get_discovery_urls - Server at opc.tcp://192.168.6.152:62541/Quickstarts/ReferenceServer/ responded with 1 Applications
[2022-11-10T08:26:26Z TRACE akri_opcua::discovery_impl] get_discovery_url_from_application - found server : Quickstart Reference Server
[2022-11-10T08:26:26Z TRACE akri_opcua::discovery_impl] get_discovery_url_from_application - server has [UAString { value: Some("https://192.168.6.152:62540/Quickstarts/ReferenceServer/discovery") }, UAString { value: Some("opc.tcp://192.168.6.152:62541/Quickstarts/ReferenceServer") }] DiscoveryUrls
[2022-11-10T08:26:26Z TRACE akri_opcua::discovery_handler] discover - found OPC UA server at DiscoveryURL opc.tcp://192.168.6.151:62541/Quickstarts/ReferenceServer
[2022-11-10T08:26:26Z TRACE akri_opcua::discovery_handler] discover - found OPC UA server at DiscoveryURL opc.tcp://192.168.6.152:62541/Quickstarts/ReferenceServer

可以通过 kubectl get akrii 查看由 Agent 自动生成的 opcua 的 instance crd 资源:

root@edge-k3s:~/akric-crd# kubectl get akrii
NAME                           CONFIG                  SHARED   NODES          AGE
akri-opcua-monitoring-7aa6fb   akri-opcua-monitoring   true     ["edge-k3s"]   5m10s
akri-opcua-monitoring-20f7e0   akri-opcua-monitoring   true     ["edge-k3s"]   5m9s` 

于此同时,使用 kubectl get pods 可以查看到为设备自动创建的工作负载:

NAME                                         READY   STATUS    RESTARTS       AGE
akri-controller-deployment-d4f7847b6-rlgrr   1/1     Running   11 (27h ago)   4d4h
akri-agent-daemonset-9s9m9                   1/1     Running   10 (27h ago)   4d1h
akri-opcua-discovery-daemonset-tv84d         1/1     Running   8 (27h ago)    3d19h
edge-k3s-akri-opcua-monitoring-7aa6fb-pod    1/1     Running   0              6m44s  <---
edge-k3s-akri-opcua-monitoring-20f7e0-pod    1/1     Running   0              6m43s  <---

部署数据展示服务

部署数据展示服务查看效果:

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

部署完成后,查看一下展示服务 SVC 的 NodePort 端口:

root@edge-k3s:~# kubectl get svc
NAME                               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes                         ClusterIP   10.43.0.1       <none>        443/TCP        5d6h
akri-opcua-monitoring-7aa6fb-svc   ClusterIP   10.43.152.214   <none>        80/TCP         13m
akri-opcua-monitoring-svc          ClusterIP   10.43.242.118   <none>        80/TCP         13m
akri-opcua-monitoring-20f7e0-svc   ClusterIP   10.43.22.196    <none>        80/TCP         13m
akri-anomaly-detection-app         NodePort    10.43.248.164   <none>        80:32007/TCP   7s <---

访问 NodePort 端口,查看效果:

云原生边缘设备解决方案Akri on k3s初体验

这个展示服务原理是通过连接工作负载的 SVC 获取工作负载采集到的设备数据,当值为 70-80 中任意数值时表示正常,用黑体展示;当值为 120 时表示异常,用红体展示

当设备下线时,Akri 会自动删除设备对应的工作负载,删除的时间大约为 5 分钟,以便应对可能出现的临时网络故障。

总 结

Akri 其实可以理解为是一种设备自动发现的框架,它可以通过云原生的方式帮助我们发现并使用 IOT 设备,目前支持 onvif、udev、opcua 三种类型。其它包括 Bluetooth、CoAP、IP、LoRaWAN、Zeroconf、acpid、MQTT 也正在开发中。

使用 k3s 可以帮助用户实现在边缘侧使用 kubernetes 的能力,通过 Akri 可以解决边缘场景下发现和使用设备的问题,这样用户就能将更多的精力专注在数据处理的应用上。