使用 K3S 架构云边 Kubernetes 集群,运行边缘图像识别应用,并与云端 IoT 平台建立数据连接
“ 本文将介绍采用 K3S 构建一个跨越 AWS 云端 EC2 主机和个人树莓派设备的单一 Kubernetes 集群,在树莓派(K3S Agent)上运行图像识别应用,并将识别结果传回云端监控应用 InFluxDB/Grafana 和 ElasticSearch,以及发送到 AWS IoT Core 平台进行处理。”
https://gitlab.com/arm-research/smarter/example
Demo 项目实现的是下图右侧的架构方案,将在边缘网关侧进行图像识别和声音识别的计算处理,将处理结果以 MQTT 消息同步到云端。
更具体一点,Demo 项目实现的是在 AWS EC2 主机上运行 K3S Master,在边缘节点上运行 K3S Agent,在边缘节点上通过 Pod 容器运行多个应用模块(包括设备管理模块、图像识别、声音识别、Web 服务端等)。同时在 AWS 云端通过 K3S 集群运行 ElasticSearch、InfluxDB/Grafana 等用于 边缘侧消息的收集、处理和可视化呈现。
所以本文第一、第二部分算是拾人牙慧,把 Smarter Demo 项目进行本地实践,并借此讨论边缘计算的相关热门方案(K3S、KubeEdge、OpenYurt等)。
第三部分将对 Demo 项目 代码进行修改,使边缘节点发送图像识别的结果信息至 AWS IoT Core。我们也将在后续讨论 IoT 数据的处理和分析。
本文将要使用到的设备:树莓派 4 (4GB版)和 PS3 Camera Eye。
01
—
建立跨云边的单 K3S 集群,Setup Demo环境
首先我们需要创建一个 K3S Master 节点。
Demo 项目作者编写了快速启动脚本(当然我们也可以通过 K3S 官方指导文档安装)。K3S的安装比较简单。
git clone https://gitlab.com/arm-research/smarter/example.git
./example/scripts/edge-k3s-setup.sh
当执行以下命令正确返回时,说明 K3S master 已经安装成功。
kubectl cluster-info
Kubernetes master is running at https://3.230.166.4:6443
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'
我们将会在后面需要这个 Master endpoint 的连接信息。
注意这里的 K3S Master 仅仅部署了 Master 角色,实际上是通过下述参数启动了 K3S Master。
/usr/local/bin/k3s server --docker --no-flannel --no-deploy coredns --no-deploy traefik --disable-agent
在这里我们可以看到,项目没有使用 K3S 默认的 Flannel 网络方案,那将采用什么网络方案呢?这正是这个 Demo 项目的有趣之处。
Master节点启动后,就可以将我们的树莓派以 Agent 节点加入这个 K3S 集群了。
关于 Smarter-CNI 网络和边缘计算框架的讨论
我们在这里稍微中断一下实践安装步骤,来讨论一下 Kubernetes 网络方案和边缘计算框架。
Smarter 项目在这里有一个特别的地方,它并没有采用 K3S 默认的 Flannel 网络,而是采用了自己开发的 Smarter-cni 网络,这个网络插件是专门为 IoT 和边缘场景 Kubernetes 跨云边设计的网络方案。
在边缘计算场景中,IoT Endpoint 终端可能没有IP连接,它们直接与边缘网关通信。边缘网关作为一个中继,与云端应用进行通信。边缘网关侧则进行低延迟的计算处理,同时也考虑了安全问题,不必要将未经处理的数据发送到云端。
在这种边缘场景中,边缘网关是不需要和其他边缘网关节点通信的,它只需要和云端通信,这是这个 Smarter 项目没有采用复杂且要求较高的 Flannel 网络方案的原因。Smarter-CNI 网络方案在云端建设 Kubernetes Master,只需要边缘计算设备能向云端 Master 发起连接。Kubernetes Node 之内的 Pod 能够福相通信,与其他节点则不能(也可能不需要)。
Smarter-CNI 网络方案看上去非常简单,但是非常有趣,以一个非常简单且解耦的方式解决了 Kubernetes 集群跨云边构建的问题。它涉及到我们常提及的将 Kubernetes 运用在边缘计算的方案问题。我们这里简单作一些讨论。
#########
KubeEdge 是华为开源的边缘计算框架,比较早开源出来,主要架构特点是云边协同。
K3S 是 Rancher 提出的轻量级 Kubernetes 发行版,大量削减 Kubernetes 非核心代码,保留核心能力,增强边缘能力。
OpenYurt 是阿里巴巴最近开源,还没有全部组件开源出来。主要架构特点是云边一体化。
1、边缘资源上的处理
边缘计算,首先考虑点之一是边缘资源有限,需要尽量减少基础架构对资源的消耗。KubeEdge 和 OpenYurt 的云边协同或一体化架构,只在边缘部署 Agent,这样的方式降低资源消耗。
K3S 的方案是在边缘运行完整的轻量版的 Kubernetes 集群。
2、边缘网络上的处理
将 Kubernetes 应用于边缘,一个比较大的挑战是 Kubernetes 集群对网络的要求比较高,边缘容器方案需要考虑集群网络的问题。KubeEdge 的方案是通过 WebSocket 和消息机制降低网络要求,并通过边缘 EdgeHub 本地化Kubernetes数据。OpenYurt 有类似的方案,通过 Tunnel Server 和 Agent 构建网络通道,以边缘 YurtHub 处理本地 Kubernetes 资源配置。
K3S 与 KubeEdge 和 OpenYurt 不同,因为其在边缘是独立的 Kubernetes 集群,与云端的联系通过 Rancher Server 进行。K3S 本身不处理云边统一网络的问题。
有趣的是,本文实践的 K3S + Smarter-CNI 方案是一个将单 K3S 集群跨云边的边缘方案,通过 K3S 的 Tunnel 设计和简单的 Docker User-defined Network 实现了云边网络的处理。
3、边缘自治的处理
如第 2 点提到,KubeEdge 通过 CloudCore 和 EdgeCore 实现节点边缘自治,而 OpenYurt 通过 Yurt Controller 和 YurtHub 实现边缘自治,OpenYurt 有个 Shadow APIserver 的概念。K3S 本身是一个Kubernetes,故其边缘自治是利用 Kubernetes 集群本身的能力。KubeEdge 也提供了 EdgeSite 轻量级 Kubernetes 的方案。 K3S + Smarter-CNI 方案没有边缘自治的能力。
4、边缘设备的处理
KubeEdge 对边缘设备的管理采用的是 Device CRD 和 Device Controller Manager 的方式,通过 Kubernetes 的CRD 对 DeviceModel 和 DeviceInstance/ DeviceTwin 进行定义。OpenYurt 有个 UnitController 的设计似乎是处理这部分的,不过还没有 Release ,不确定其能力。K3S + Smarter-CNI 的方案,本身不处理设备管理,在本文的实践中,ARM 团队提供了一个 Device Manager (本身是一个 Pod 形式部署)来实现本地设备的发现和管理。当然这个设备管理与 KubeEdge 里 DeviceMapper 等的概念是有差别的。
另外, AWS IoT 也有 Shadow 的概念,在 Azure IoT 有 DeviceTwin 的概念。
#########
回到本文的实践内容。
Smarter-CNI 安装非常简便。在树莓派上执行下列命令。
git clone https://gitlab.com/arm-research/smarter/smarter-cni.git
sudo ./install.sh
执行后,节点上会生成一个名为 mynet 的 Docker network,以及运行一个名为 mydns 的 Docker 容器。
CNI 插件安装完成后,即可以开始将树莓派 加入的该 K3S 集群。树莓派我们采用 Ubuntu 系统,可支持 ARM64 位。
export K3S_VERSION="v1.18.3+k3s1"
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=$K3S_VERSION K3S_URL=https://<myserver>:6443 K3S_TOKEN=XXX sh -s - --docker --no-flannel
K3S_URL 、TOKEN 可以在上述安装 K3S Master 中获取,脚本会在 Master 本地节点生成 TOKEN、Kubeconfig 等文件。(熟悉 K3S 的朋友也可以在默认 K3S 保存对应信息的目录获取)。
kubectl get nodes
NAME STATUS ROLES AGE VERSION
ubuntu Ready <none> 6h38m v1.18.3+k3s1
安装无误,则能查看得树莓派 ubuntu 已成为 K3S Node。
这样,我们就 Setup 起一个跨云边的 K3S 集群。
在开始第二部分部署边缘图像识别应用之前,我们需要部署 Cloud Data Node,这个节点将部署 K3S 服务,并通过 K3S 运行 ElasticSearch、Kibanfa、InFluxDB 和 Grafana, 作为我们刚刚建立的运行边缘应用的业务 K3S 提供监控和日志服务。
作者提供了一键安装的脚本,执行一下命令即可。
git clone https://gitlab.com/arm-research/smarter/example.git
export MY_EMAIL=<A_VALID_EMAIL>
cd example
./scripts/cloud-data-setup.sh
安装无误,能得到如下输出:
=========================================================================
Grafana initial admin password has been set to eSBa0shDNE
NOTE: If using kubectl from this machine, make sure to run 'export KUBECONFIG=/home/ubuntu/example/cloud-kube.config'
REMEMBER: To access grafana use https://grafana-3-236-47-70.nip.io
REMEMBER: To access kibana use https://kibana-3-236-47-70.nip.io
REMEMBER: To access prometheus dashboard use https://prometheus-3-236-47-70.nip.io
NOTE: on your dev machine export SMARTER_DATA_DOMAIN=3-236-47-70.nip.io
Your elasticsearch and kibana username is 'elastic' with password vC2sLOfbuRPsZovBg0K6
=========================================================================
02
—
通过 K3S 部署边缘图像识别应用
首先是在通过 K3S Master 在 树莓派上运行一些基本的工作负载。
在 K3S Master 上执行一下命令。参数在文章第一部分的实践中可以获得。
export KUBECONFIG=<YOUR_KUBECONFIG>
export NODE_TO_USE=<YOUR_NODE_NAME>
export SMARTER_DATA_DOMAIN=<YOUR_K3S_DATA_NODE_IP (DASH SEPARATED)>.nip.io
export SMARTER_EDGE_DOMAIN=<YOUR_K3S_EDGE_MASTER_IP>
然后执行
git clone https://gitlab.com/arm-research/smarter/example.git
cd example/demo
./start_device_mgmt.sh
kubectl get secrets/fluentd-credentials --template={{.data.password}} | base64 -d
kubectl create secret generic fluentd-credentials --from-literal=password=<YOUR FLUENTD PASSWORD>
kubectl label node ${NODE_TO_USE} nodetype=rpi
# Use standard kubernetes CLI to get information about nodes connected to our cluster
kubectl get nodes -o wide
kubectl get node ${NODE_TO_USE} -o wide --show-labels
# Deploy fluent-bit DaemonSets and ConfigMaps
kubectl apply -f ../yaml/fluent-bit/
# Update fluent-bit-ds with server IP locations
envsubst < ../yaml/fluent-bit/fluent-bit-ds.yaml | kubectl apply -f -
kubectl get ds
# Check status of pods using the following command, when pod is running, proceed to the next step
kubectl get pods --field-selector spec.nodeName=${NODE_TO_USE} -o wide
# Label node to run fluent-bit
kubectl label node ${NODE_TO_USE} fluent-bit=enabled
kubectl get node ${NODE_TO_USE} -o wide --show-labels
# Check status of pod using the following command, when pod is running, proceed to the next step
kubectl get pods --field-selector spec.nodeName=${NODE_TO_USE} -o wide
# Deploy Netdata monitoring agent
kubectl apply -f ../yaml/netdata-slave
kubectl label node ${NODE_TO_USE} netdata-monitor=enabled
# Deploy EdgeFaaS DaemonSet
kubectl apply -f ../yaml/faas/edgefaas-ds.yaml
# Label node to run EdgeFaaS runtime
kubectl label node ${NODE_TO_USE} edgefaas=enabled
# Check status of pod using the following command, when pod is running, proceed to the next step
kubectl get pods --field-selector spec.nodeName=${NODE_TO_USE} -o wide
# Deploy Pulseaudio
kubectl apply -f ../yaml/pulseaudio/
# Label node to run Pulseaudio daemon
kubectl label node ${NODE_TO_USE} pulse=enabled
# Deploy json flattening FaaS function
# Make sure that Jobs are deleted before running.
kubectl apply -f ../yaml/faas/install-squasher.yaml
kubectl label node ${NODE_TO_USE} squasher=enabled
# Run function-listing Job to make sure the squasher got installed
kubectl apply -f ../yaml/faas/list.yaml
kubectl label node ${NODE_TO_USE} faas-list=enabled
kubectl logs edgefaas-lis
上述脚本将会在节点上部署devicemanger、 fluentbit、edgefaas、net-data等基础组件。
#########
DeviceManger
DeviceManger 是 ARM 提供的本地设备管理应用。
它能帮助发现和管理节点上的设备。
Allocated resources:
limits may be over 100 percent, i.e., overcommitted.)
Resource Requests Limits
-------- ------
cpu 560m (14%) 850m (21%)
memory 365Mi (48%) 365Mi (48%)
0 (0%) 0 (0%)
0 0
0 0
0 0
0 0
0 0
0 0
1 1
0 0
1 1
0 0
0 0
0 0
0 0
2 2
Events: <none>
Netdata
FluentBit
FlunentBit 可以说是一个轻量版的 Fluentd,比较适合边缘,用来收集边缘节点上所有容器的日志,并发送给云端 ElasticSearch。FluentBit 也接收后续部署的图像识别程序发送的 MQTT 消息,并将消息过滤后发送给 InfluxDB,最终由 Grafana 展示图像识别结果的统计图表。
#########
然后我们再部署图像识别应用。
# Deploy Triton
kubectl apply -f ../yaml/triton/triton-ds.yaml
# Label node to run triton model server
kubectl label node ${NODE_TO_USE} triton=enabled
if kubectl get node ${NODE_TO_USE} --show-labels | grep -q nodetype=rpi; then
# Deploy ambient sound classifier
kubectl apply -f ../yaml/ambient-sound-classifier/ambient-sound-classifier-ds.yaml
# Label node to run ambient sound classifier
kubectl label node ${NODE_TO_USE} ambient-sound-classifier=enabled
fi
# Deploy the Sensor data collection applications
kubectl apply -f ../yaml/image-detector/image-detector-ds.yaml
kubectl apply -f ../yaml/audio-client/audio-client-ds.yaml
# Label node to run image detector and web server
kubectl label node ${NODE_TO_USE} image-detector=enabled
# Check status of pods using the following command, when pods are running, proceed to the next step
kubectl get pods --field-selector spec.nodeName=${NODE_TO_USE} -o wide
# Label node to run sound classifier and audio client
kubectl label node ${NODE_TO_USE} audio-client=enabled
# Check status of pods using the following command, when pods are running, proceed to the next step
kubectl get pods --field-selector spec.nodeName=${NODE_TO_USE} -o wide
# Deploy the updated image detector for both cars and pedestrians
kubectl apply -f ../yaml/image-detector/image-detector-extended-ds.yaml
kubectl get pods --field-selector spec.nodeName=${NODE_TO_USE} -o wide
如安装无误,能够获取如下返回。
$ kubectl get pods --field-selector spec.nodeName=${NODE_TO_USE} -o wide --watch
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
smarter-device-manager-9d4mk 1/1 Running 0 6h42m 192.168.125.112 ubuntu <none> <none>
fluent-bit-4fqmc 1/1 Running 0 6h41m 172.38.0.4 ubuntu <none> <none>
pulseaudio-t6v8q 1/1 Running 0 6h41m 172.38.0.8 ubuntu <none> <none>
edgefaas-5gpqm 1/1 Running 0 6h41m 172.38.0.7 ubuntu <none> <none>
netdata-slave-zr8jr 1/1 Running 0 6h41m 192.168.125.112 ubuntu <none> <none>
edgefaas-list 0/1 Completed 0 6h41m 172.38.0.6 ubuntu <none> <none>
edgefaas-install-squasher 0/1 Completed 0 6h41m 172.38.0.5 ubuntu <none> <none>
ambient-sound-classifier-qqgdn 1/1 Running 0 6h37m 172.38.0.6 ubuntu <none> <none>
triton-armnn-b2tbw 1/1 Running 0 6h37m 172.38.0.5 ubuntu <none> <none>
audio-client-sxdt4 0/1 Running 81 6h37m 172.38.0.10 ubuntu <none> <none>
image-detector-hbflg 0/1 Running 0 55m 172.38.0.9 ubuntu <none> <none>
图像识别的测试
上述所有步骤成功执行的话,到这里我们就已经 Setup 起完整的 Smarter Demo 项目实验环境,可以开始测试了。
Demo 项目在本地启用了 Web 服务器,通过树莓派 8080 端口可以访问 Web,实时查看摄像头拍摄图像和识别的结果(识别为车将显示绿色框框、人则显示红色框框)。
至此,我们重复了 Smarter Demo 项目的 实践内容,并借此简单讨论了边缘计算方案的特点。
下面,将在 Demo 项目基础上进行修改,将边缘计算结果发送到云端 IoT 平台进行处理。
03
—
将边缘图像识别结果发送到 AWS IoT
为什么要发送到 IoT 平台处理?
从上述的项目代码中可以看到,Image-detector 程序通过 TensorFlow 框架持续对输入图像进行识别,并将识别结果向 FluentBit 发送了 topic 为 demo/car_count 和 demo/person_count 的 MQTT 消息,FluentBit 通过 Fluent-plugin-influxdb 向云端 influxDB 转发了过滤后的 MQTT 消息,最终由 Grafana 进行相应的 car 和 person 的实时竖状图表展示。
我个人认为上述处理过程虽比较巧妙地利用了云原生的相关工具来处理 IoT 设备数据,但有失优雅。我更倾向于使用 FluentBit、Netdata、Grafana 来处理基础架构的问题,IoT 数据的处理、清洗、分析等涉及更复杂的内容,应该由其他方式进行。主流公有云厂商提供了非常多的云服务,可以对数据进行更方便和丰富的处理和分析,甚至利用人工智能云服务的能力。
这部分的内容与上一篇文章 相关,下面将结合这篇文章中提到的 AWS IoT Python SDK 进行 MQTT Publish 到 AWS IoT Core 平台。
我们对 image-detector 中的代码进行修改,引入 AWS IoT Python SDK,使其将识别结果通过 MQTT 发送到 AWS IoT Core,并由 AWS IoT 进行后续数据处理。
下面为一段 AWS IoT MQTT 连接代码,需要我们传入证书:
# Connection with AWS IoT
mqtt_connection = mqtt_connection_builder.mtls_from_path(
endpoint=args.endpoint,
cert_filepath=args.cert,
pri_key_filepath=args.key,
client_bootstrap=client_bootstrap,
ca_filepath=args.root_ca,
on_connection_interrupted=on_connection_interrupted,
on_connection_resumed=on_connection_resumed,
client_id=args.client_id,
clean_session=False,
keep_alive_secs=6)
to {} with client ID '{}'...".format(
args.client_id))
connect_future = mqtt_connection.connect()
# Future.result() waits until a result is available
connect_future.result()
print("Connected!")
将完整代码打包成镜像:yejianhonghong/smarter-image-detector-aws:v1.3
部署更新 DaemonSet/image-detector 为上述镜像。
也可以将 K3S 纳管到 Rancher 平台,在平台上进行部署更新。
在 AWS IoT Core Test 功能页 Subscribe 主题 smarter/test (代码中定义)。可见 AWS IoT 已经收到了树莓派上图像识别的结果信息。
我们还可以通过 AWS IoT Action 将数据存储到 AWS DynamoDB 数据库,方便后续的数据处理和分析。
完毕。
IoT 设备数据发送到云端 IoT 平台只是 物联网 方案的第一步,更能体现 IoT 价值的应该是数据的处理、分析和呈现,甚至 AI 分析等。
后续我们将介绍通过 AWS 各类云服务对 IoT 设备数据进行数据的存储、处理、清洗、转换,和可视化呈现。