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

时间:2022-11-22 12:02:59

作者:

涂家英,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.cnotallow=/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_Temperature​​​和​​NAMESPACE_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 可以解决边缘场景下发现和使用设备的问题,这样用户就能将更多的精力专注在数据处理的应用上。