如何用Shifu来接入一个私有驱动的物联网设备

在上一篇《如何写一个树莓派的驱动来控制GPIO LED》中我们分享了驱动的编写方法。虽然实现了控制,但是驱动只能在本地执行,不具有任何的可扩展性。尤其是当设备数量多了以后管控难度将急剧增加。
这一直是一个物联网开发者的难题。那么有什么办法可以大批量,模块化的接入类似驱动的设备呢?
欢迎来到边无际的这一系列的第二篇分享——如何用Shifu Framework来接入一个私有驱动物联网设备。
简介
本文是一个使用Shifu接入树莓派Python驱动的指南,其中包含Shifu Framework, Docker, Linux, Kubernetes的基本操作,任何开发者都可以阅读本文来学习Shifu Framework的开发方法。
文中的Shifu Framework架构如下:如何用Shifu来接入一个私有驱动的物联网设备
文章图片

北向通过”deviceshifu-http-http”向上开放HTTP API接口,南向通过”rpio-gpio-driver”来和实际设备交互。
目标

  1. 在树莓派上安装k3s集群并安装Shifu Framework
  2. 打包树莓派LED驱动到一个容器镜像
  3. 在Shifu中部署树莓派LED的数字孪生
  4. 实现对树莓派LED的远程自动化管控
本次分享中用到的设备有:
  1. 树莓派 (本文中用到的为Raspberry Pi 3B+),运行着64位的Raspberry Pi OS
  2. 上篇文章连接的电路
需要的基本知识:
· 基本的Python
· Linux命令行基本操作(创建一个文件,安装应用,SSH,运行一个程序)
· Docker/containerd基本操作
· K8s/K3s基本操作
步骤
第一步:安装K3s
首先我们要在树莓派中运行一个Kubernetes集群,这里并不限制用户使用的版本,但是为了节省资源本文中使用的是k3s
本文参考的是安装教程
https://rancher.com/docs/k3s/...
具体步骤本文将不涉及。在今后的分享中将会带着大家进行详细的安装讲解,尽情期待。
安装完毕后,执行“kubectl version”查看当前kubernetes版本:
如何用Shifu来接入一个私有驱动的物联网设备
文章图片

利用“kubectl get nodes”查看当前集群的状态,显示”Ready”即表示集群可以使用:
如何用Shifu来接入一个私有驱动的物联网设备
文章图片

至此,k3s安装结束。
第二步:安装Shifu
首先将Shifu项目克隆到本地,项目地址为: https://github.com/Edgenesis/...
执行命令为:git clone https://github.com/Edgenesis/...
下面通过“kubectl apply -f shifu/k8s/crd/install/shifu_install.yml”即可一键将Shifu部署到k3s集群中:如何用Shifu来接入一个私有驱动的物联网设备
文章图片

再次执行“kubectl get pods -A”,即可看到Shifu Framework的控制器被部署到集群中:如何用Shifu来接入一个私有驱动的物联网设备
文章图片

我们也可以通过“edgedevices”这个CRD来管理设备资源(当前没有设备):如何用Shifu来接入一个私有驱动的物联网设备
文章图片

至此,Shifu安装完毕。
第三步:打包驱动
我们需要利用Shifu提供的一个小工具来实现可以远程操纵本地驱动,详细的教程请看:
https://github.com/Edgenesis/...
这个小工具实现了将用户/程序发送来的HTTP请求转换到本地命令行来执行。
教程里面提供了一个驱动示例,路径为
https://github.com/Edgenesis/...
内容如下:如何用Shifu来接入一个私有驱动的物联网设备
文章图片

可以看到实例Dockerfile分两部分,首先是用“golang”这个镜像来编译Shifu提供的“http_to_ssh_stub.go”来实现HTTP到SSH命令行的转换。接着是利用一个空的“alpine”镜像,配置SSH来供演示。
接下来让我们来正式操作。
考虑到树莓派的性能局限,本次编译将从电脑端执行,将编译好的镜像推送到Docker Hub来供远程调用即可。
首先,我们建立一个新的文件夹,这里用的是“dev”,然后将上一篇文章中使用到的树莓派LED驱动保存到该目录:
-- led_driver.py

驱动内容不变
如何用Shifu来接入一个私有驱动的物联网设备
文章图片

从Shifu项目的“driver_util/examples/simple-alpine/”目录下将“Dockerfile.sample”复制到“dev”目录下:
dev/ ├── Dockerfile.sample └── led_driver.py

更改以下字段将第二部的镜像从“alpine”改为“python:alpine”,安装“RPi.GPIO”的Python库
最后将Python驱动拷贝到运行容器中,新的Dockerfile如下,改动的地方已用红字标记出来
FROM golang:1.17.1 as builder WORKDIR / ENV GOPROXY=https://goproxy.cn,direct ENV GO111MODULE=on ENV GOPRIVATE=github.com/Edgenesis COPY driver_util driver_util WORKDIR /driver_util RUN go mod download # Build the Go app RUN CGO_ENABLED=0 GOOS=$(go env GOOS) GOARCH=$(go env GOARCH) gobuild -a -o /output/http2ssh-stub http_to_ssh_stub.go FROM python:alpine RUN apk add --no-cache --update openrc openssh \ && mkdir -p /run/openrc \ && touch /run/openrc/softlevel \ && sed -ie "s/#PubkeyAuthentication/PubkeyAuthentication/g"/etc/ssh/sshd_config \ && sed -ie "s/#PasswordAuthenticationyes/PasswordAuthentication no/g" /etc/ssh/sshd_config \ && sed -ie "s/AllowTcpForwardingno/AllowTcpForwarding yes/g" /etc/ssh/sshd_config \ && echo"PubkeyAcceptedKeyTypes=+ssh-rsa" >>/etc/ssh/sshd_config\ && ssh-keygen -A \ && passwd -d root \ && mkdir ~/.ssh \ && while ! [ -e/etc/ssh/ssh_host_rsa_key.pub ]; do sleep 1; done \ && cp /etc/ssh/ssh_host_rsa_key.pub~/.ssh/authorized_keys RUN apk add --no-cache -Uu --virtual .build-dependencies libffi-devopenssl-dev build-base musl \ && pip3 install --no-cache --upgrade RPi.GPIO\ && apk del --purge .build-dependencies \ && apk add --no-cache --purge curlca-certificates musl \ && rm -rf /var/cache/apk/* /tmp/* WORKDIR /root/ COPY --from=builder /output/http2ssh-stub http2ssh-stub COPY --from=builder/driver_util/examples/simple-alpine/docker-entrypoint.sh docker-entrypoint.sh COPY dev/led_driver.py led_driver.py RUN chmod +x docker-entrypoint.sh # Command to run the executable ENTRYPOINT ["./docker-entrypoint.sh"]

接下来我们来打包封装Docker镜像,因为树莓派的CPU是ARM64的处理器,本文中编译使用的电脑为x86,所以我们需要使用Docker的buildx功能来进行镜像构建,有关buildx的教程本文就不再叙述,需要的话可以移步:
https://docs.docker.com/build...
利用“docker buildx build --platform=linux/arm64 -f dev/Dockerfile.sample . -t edgehub/rpi-gpio-driver:v0.0.1 --push”来构建镜像并推送到docker hub中。
至此,镜像打包部分完成。
【如何用Shifu来接入一个私有驱动的物联网设备】后期我们将提供一键打包模块,让这一步简单到填写一个配置文件+一行命令即可,尽情期待
第四步:部署设备孪生到树莓派中
有了镜像以后,我们可以将数字孪生部署到集群中,下面我们来准备部署所需要的文件。
首先是一个Kuberenetes Deployment YAML文件,用来运行deviceShifu和驱动的Pod:
apiVersion: apps/v1 kind: Deployment metadata: labels: app: edgedevice-rpi-gpio-deployment name: edgedevice-rpi-gpio-deployment namespace: default spec: replicas: 1 selector: matchLabels: app: edgedevice-rpi-gpio-deployment template: metadata: labels: app: edgedevice-rpi-gpio-deployment spec: containers: - image: edgehub/deviceshifu-http-http:v0.0.1 name: deviceshifu-http ports: - containerPort: 8080 volumeMounts: - name: edgedevice-config mountPath: "/etc/edgedevice/config" readOnly: true env: - name: EDGEDEVICE_NAME value: "edgedevice-rpi-gpio" - name: EDGEDEVICE_NAMESPACE value: "devices" - image: edgehub/rpi-gpio-driver:v0.0.1 name: driver volumeMounts: - mountPath: /dev/gpiomem name: gpiomem securityContext: privileged: true ports: - containerPort: 11112 env: - name: EDGEDEVICE_DRIVER_SSH_KEY_PATH value: "/etc/ssh/ssh_host_rsa_key" - name: EDGEDEVICE_DRIVER_HTTP_PORT value: "11112" - name: EDGEDEVICE_DRIVER_EXEC_TIMEOUT_SECOND value: "5" - name: EDGEDEVICE_DRIVER_SSH_USER value: "root" volumes: - name: edgedevice-config configMap: name: rpi-gpio-configmap-0.0.1 - name: gpiomem hostPath: path: /dev/gpiomem serviceAccountName: edgedevice-sa

请注意在Deployment文件中我们为了在容器中使用树莓派的GPIO,需要在容器的“securityContext”中加入“privileged: true”再通过volume的形式将树莓派的“/dev/gpiomem”挂载到容器中。
一个Kubernetes Service YAML文件,用来将deviceShifu的请求从域名代理到真正的Pod:
apiVersion: v1 kind: Service metadata: labels: app: edgedevice-rpi-gpio-deployment name: edgedevice-rpi-gpio namespace: default spec: ports: - port: 80 protocol: TCP targetPort: 8080 selector: app: edgedevice-rpi-gpio-deployment type: LoadBalancer

一个Kubernetes ConfigMap YAML文件,用来配置deviceShifu:
apiVersion: v1 kind: ConfigMap metadata: name: rpi-gpio-configmap-0.0.1 namespace: default data: driverProperties: | driverSku: RaspberryPiB+ driverImage: edgenesis/rpi-gpio-python:v0.0.1 driverExecution: "python led_driver.py" instructions: | pin: operate: help: # Telemetries are configurable health checks of the EdgeDevice # Developer/user can configure certain instructions to be usedas health check # of the device. In this example, the device_health telemetry ismapped to # "get_status" instruction, executed every 1000 ms telemetries: | device_health: properties: instruction: help initialDelayMs: 1000 intervalMs: 1000

在ConfigMap中我们需要配置驱动的执行路径,因为在生成镜像时我们将Python文件直接放到了默认路径下,在这里填写“python led_driver.py”即可。如果驱动是一个二进制文件的话这里直接填写二进制的目录即可。
一个Shifu EdgeDevice YAML文件,用来生成设备孪生:
apiVersion: shifu.edgenesis.io/v1alpha1 kind: EdgeDevice metadata: name: edgedevice-rpi-gpio namespace: devices spec: sku: "RaspberryPi 3B+" connection: Ethernet address: 0.0.0.0:11112 protocol: HTTPCommandline

将这四个文件放到树莓派中,目录内容如下:
led-deploy/ ├──deviceshifu-rpi-gpio-configmap.yaml ├──deviceshifu-rpi-gpio-deployment.yaml ├──deviceshifu-rpi-gpio-service.yaml └──edgedevice-rpi-gpio-edgedevice.yaml

利用“kubectl apply -f” 即可将deviceShifu部署到k3s集群中:
如何用Shifu来接入一个私有驱动的物联网设备
文章图片

接着通过“kubectl get pods”来查看运行状态:
如何用Shifu来接入一个私有驱动的物联网设备
文章图片

通过“kubectl get edgedevices -n devices”来查看集群中的所有设备孪生:
如何用Shifu来接入一个私有驱动的物联网设备
文章图片

再通过describe,即可查看数字孪生的详细信息:
如何用Shifu来接入一个私有驱动的物联网设备
文章图片

Note:在这里你可以注意到代表设备状态的字段”Edgedevicephase”是”Failed”的状态,这是一个目前已知的bug,我们将会尽快修复!
接下来我们就可以和设备互动了,在这里我们部署一个nginx容器来代表实际场景中的应用
部署命令为:“kubectl run nginx --image=nginx”
接着执行“kubectl exec -it nginx -- bash”进入nginx的命令行:
如何用Shifu来接入一个私有驱动的物联网设备
文章图片

最后,利用curl来给设备发送命令,驱动接受的命令格式为:
`python led_driver --pin --operate
利用Shifu来发送命令的话将由HTTP转换到命令行,请求地址写法为:
最后一步:运行效果程序通过直接给设备的域名发送HTTP请求,即可操控LED灯泡的亮/灭至此,将树莓派驱动接入Shifu的教程编写完毕**总结**在此篇文章中,我们实现了将一个用于操控树莓派GPIO的Python本地驱动接入Shifu Framework,实现可以用HTTP来远程直接操控。可以看到,对于私有,甚至于二进制文件Shifu也可以轻松接入。Shifu Framework的延展性可以让物联网开发者拥有无限的可能。非常感谢您看到了这里,我们期待您的反馈,如果觉得文章写得不错或者有任何建议请毫不犹豫地留言。

    推荐阅读