在 Kubernetes 中部署并使用 KubeEdge
边缘计算在广泛制造业、工业、零售和金融等行业,随着云原生应用的兴起,不可变基础设施和快速的应用交付等特性很适用于边缘计算场景。因此在 Kubernetes 上使用边缘计算框架是近年很火热的一个方向。本篇会介绍下边缘计算的场景和架构,并以一个 Demo 示例展示如何运行一个边缘应用到边缘节点上。
边缘计算痛点和场景
首先,边缘计算是云计算的延伸,云计算按需和资源池化的特性可以满足资源利用率的提升和计算资源的集中供给,但边缘测的应用场景决定不可能什么应用都丢到数据中心里。比如
- 低延迟处理。车联网场景如果要进行数据共享和指令下发需要极低延迟的通信保障和计算处理速度。
- 本地处理数据。有些数据较敏感不能什么都传到云端(如用户照片、密码)
- 离线自治。很多边端设备不一定有可靠的连接保持和云端的通信。如农业、地理科学的传感器。
因此对于边缘节点和边缘设备来说,需要统一管理和本地计算的能力,来实现云端负责总体决策,边端负责本地执行的效果。这样处理的好处就是数据处理效率高、减少云边带宽、降低运营成本,却又不缺少云端的资产管理、调度监控、安全管控。
了解边缘计算含义后,就有提问了,既然边缘计算和云计算都是一种概念,势必市场上有众多边缘计算的产品和标准,为什么要在 Kubernetes 上使用边缘计算呢?
这个我个人理解是双向奔赴的,kubernetes 本身比较适合基础架构标准化和应用交付,可以帮助云端统一管理边缘节点和边缘设备,也适合于标准应用的云端下发,生态丰富且开源开放的可观测体系也适合于不同的企业用管数据中心的方法实现边端可观测性。而 Kubernetes 也需要边缘计算将自己的能力得到更多的延伸,去实现更多的平台能力和平台标准,毕竟云-网-边-端是当代云计算需要涵盖的每个方向~
常见边缘计算框架
对于产品选型来说,云原生领域最方便的就是打开 landscape 了。我们来看看 Automation&Configuration 这栏:
我们常见的有三种,如 KubeEdge、OpenYurt 和 SuperEdge。本篇先拿 KubeEdge 这个孵化项目分享下。
KubeEdge 架构
KubeEdge 分为云端和边端。云端核心组件 CloudCore 和边端核心组件 EdgeCore 联合实现边缘计算框架的众多功能,如云边通信、设备管理、离线自治等。还有一些辅助组件如 EdgeMesh 实现边端通信和服务治理,Sedna 提供边端 AI 框架等。
而到具体的 CloudCore 和 EdgeCore 的组成,可从下图详细学习架构设计:
云端
CloudCore 由 CloudHub 和 EdgeController、DeviceController 组成。
- CloudHub。主要观察云边变化,读写 Edge 消息,缓存数据后通过 WebSocket/QUIC(K8s 的 listwatch 机制太耗资源)发送给 EdgeHub,还要把和 Edge 通信得到的一些消息发送给 Controller。
- EdgeController。作为 ApiServer 和 EdgeCore 的桥梁,管理常用的配置、Pod、缓存等事件,把 EdgeCore 订阅到的 Pod 的众多事件信息同步状态到 ApiServer。也把 ApiServer 的 ADD/UPDATE/DELETE 等事件同步到 EdgeCore。
- DeviceController。通过 EdgeCore DeviceTwin 同步设备更新,总体过程是 Mapper—>MQTT—>EventBus—>DeviceTwin->EdgeHub->CloudHub—>Deviceontroller->APIServer。另一方面就是云端创建的 Device,下发到边端得到元数据进行设备端更新。
边端
- EdgeHub。云边通信边端,同步资源更新。
- EventBus。发送 / 接收 MQTT 消息
- MetaManager。在 SQLlite 存储数据,是 Edged 和 EdgeHub 的消息处理器。
- Edged。边端裁剪版 kubelet。管理 Pod、configmap、volume 等资源的生命周期。还包含一个 StatusManager 和 MetaClient 组件。前者每 10s 将本地数据库存储的状态信息上传至云,后者作为 client 和本地迷你 Etcd(MetaManager)交互,如读取云端下发的 ConfigMap、Secret,写 Node、Pod Status。
- DeviceTwin。存储设备属性和状态,创建 Edge 设备和节点关系,同步设备属性到云。
- ServiceBus: 接收云上服务请求和边缘应用进行 http 交互
安装部署
安装 Cloudcore
KubeSphere 已经集成了 KubeEdge,可提供边缘节点纳管、应用下发、日志监控等功能。接下来将在 KubeSphere 上演示边缘计算 Demo。
KubeSphere 上启用 KubeEdge,编辑 clusterconfiguration,设置 edgeruntime enabled 为 true,kubeedge enabled 为 true,设置 advertiseAddress IP 为“master_ip”
设置完成后,在集群管理-> 节点中会出现“边缘节点”:
此时查看 kubeedge 命名空间下的工作负载和配置,可以熟悉下部署架构。
CloudCore 作为一个 Deployment 运行,IptablesManager 会帮助处理云边通道的 Iptables 规则下发。
查看 CloudCore 挂载的 ConfigMap 和 Secret,ConfigMap 主要挂载 cloudcore.yaml 配置文件到 /etc/kubeedge/config, 可灵活修改 CloudCore Modules 配置。Secret 挂载 CloudCore 和 EdgeCore 需要用到的一些 TLS 证书。
添加边缘节点
进入 KubeSphere Console,导航到节点-> 边缘节点,添加边缘节点:
填写边缘节点的名字和边缘节点的 IP,生成边缘节点配置命令,粘贴到边缘节点执行:
由于我们使用的 cloudcore 的服务是通过 NodePort 暴露出来的,所以在边缘节点注册 Cloudcore 时,需要使用 NodeIP:NodePort 形式,此处将 10000-10004 替换为 30000-30004 端口。
install MQTT service successfully. kubeedge-v1.9.2-linux-amd64.tar.gz checksum: checksum_kubeedge-v1.9.2-linux-amd64.tar.gz.txt content: [Run as service] start to download service file for edgecore [Run as service] success to download service file for edgecore kubeedge-v1.9.2-linux-amd64/ kubeedge-v1.9.2-linux-amd64/edge/ kubeedge-v1.9.2-linux-amd64/edge/edgecore kubeedge-v1.9.2-linux-amd64/version kubeedge-v1.9.2-linux-amd64/cloud/ kubeedge-v1.9.2-linux-amd64/cloud/csidriver/ kubeedge-v1.9.2-linux-amd64/cloud/csidriver/csidriver kubeedge-v1.9.2-linux-amd64/cloud/admission/ kubeedge-v1.9.2-linux-amd64/cloud/admission/admission kubeedge-v1.9.2-linux-amd64/cloud/cloudcore/ kubeedge-v1.9.2-linux-amd64/cloud/cloudcore/cloudcore kubeedge-v1.9.2-linux-amd64/cloud/iptablesmanager/ kubeedge-v1.9.2-linux-amd64/cloud/iptablesmanager/iptablesmanager KubeEdge edgecore is running, For logs visit: journalctl -u edgecore.service -b
查看 KubeSphere 控制台的边缘节点,已经可以看到边缘节点注册上来:
使用 kubectl 查看节点情况:
Metrics& 日志
此时我们发现节点的 CPU 内存信息无法统计,需要开启 KubeSphere Metrics_Server 并在 Edge 端开启 EdgeStream:
编辑 cc,开启 metrics-server:
编辑边缘节点 /etc/kubeedge/config/edgecore.yaml 文件,搜索 edgeStream,将 false 更改为 true:
edgeStream: enable: true handshakeTimeout: 30 readDeadline: 15 server: 192.168.100.7:30004 tlsTunnelCAFile: /etc/kubeedge/ca/rootCA.crt tlsTunnelCertFile: /etc/kubeedge/certs/server.crt tlsTunnelPrivateKeyFile: /etc/kubeedge/certs/server.key writeDeadline: 15
此处 server 字段设置端口为 30004,因为我们使用 NodePort 端口和云端通信
重启 edgecore.service:
$ systemctl restart edgecore.service
查看节点监控信息:
我们上面章节中已经在 edgecore 开启 edgestream,实现了云端收集 Edge 节点的 Metrics 功能,这个操作同时也实现了边端日志查看的能力。
一般来说,当我们 kubectl logs pod -n namespace 后,kubectl 会请求 kube-apiserver 查询 pod 是否存在以及 pod 里是否含有多个容器,再检索 Pod 所在的 Node 的 Kubelet Server 信息。这个信息一般可以通过 kubectl describe 或 get node 查到:
$ kubectl get node ks2 -oyaml addresses: - address: 192.168.100.7 type: InternalIP - address: ks2 type: Hostname daemonEndpoints: kubeletEndpoint: Port: 10250 kubectl get node edge-node-1 -oayml addresses: - address: 192.168.100.6 type: InternalIP - address: edge-node-1 type: Hostname daemonEndpoints: kubeletEndpoint: Port: 10352
InternalIP+kubeletEndpoint 组成 kubelet server 的地址,kubectl logs 就可以请求这个地址得到相关日志信息。但对于边缘端来说,大多数情况这个 internalIP 云端是无法访问的。
此时就需要 CloudCore 的 CloudStream 和 EdgeCore 的 EdgeStream 建立一个云边通道,在 CloudCore 和 EdgeCore 建立云边 WebSocket 通信后,将请求的 Edge kubelet server 的日志信息能通过通道返回给云端。这个通道需要两边开启 CloudStream 和 EdgeStream,并通过 TLS 证书进行验证。
边缘端在上面已经开启 EdgeStream,云端部署 CloudCore 后会自动开启。
查看云端挂载的 CloudCore.yaml 配置文件:
当然,云边通道只能将日志从消息返回,具体返回到 CloudCore 的 CloudStream,还需设置一个 NAT 规则:
$ iptables -t nat -A OUTPUT -p tcp --dport 10350(edge kubelet 端口)-j NAT --to $ClOUDCOREIPS:10003
这个操作已经让自动部署的 iptables-manager 完成了~
$ iptables -t nat -L Chain TUNNEL-PORT (2 references) target prot opt source destination DNAT tcp -- anywhere anywhere tcp dpt:10351 to:10.20.253.88:10003 DNAT tcp -- anywhere anywhere tcp dpt:10352 to:10.20.253.127:10003
进入边缘节点,查看容器组,我们可以看到有几个 daemonset 有强容忍度,调度到了边缘节点,由于边缘端很多情况存在不稳定通信,不适合运行 Calico 这种 CNI 组件,更多使用 EdgeMesh 进行云边通信和服务发现,我们可以手动 Patch Pod 以防止非边缘节点调度至工作节点:
#!/bin/bash NoShedulePatchJson='{"spec":{"template":{"spec":{"affinity":{"nodeAffinity":{"requiredDuringSchedulingIgnoredDuringExecution":{"nodeSelectorTerms":[{"matchExpressions":[{"key":"node-role.kubernetes.io/edge","operator":"DoesNotExist"}]}]}}}}}}}' ns="kube-system" DaemonSets=("nodelocaldns" "kube-proxy" "calico-node") length=${#DaemonSets[@]} for((i=0;i