背景

公司业务需要引入边缘计算终端,并做边缘管理与监控,故调研并最终选定 KubeEdge

技术选型

  1. 常规条件下,云边不在同一网段,边缘侧到云侧单向可达。
  2. 云侧作为管理端,需要能够将命令下达到边缘侧。包括新增/更新/删除容器实例、新增/更新/删除配置等
  3. 云侧作为管理端,需要能够主动与边缘侧建立连接。包括查看边缘侧容器实例日志,远程操作边缘侧容器实例等
  4. 边缘侧作为工作端,需要能够推送监测指标给到云侧,作为心跳等信息依据。
  5. 非技术性需求:运维友好、可靠性与通信安全

KubeEdge 架构

KubeEdge 架构图

KubeEdge 架构图

云上部分

  • CloudHub: CloudHub 是一个 Web Socket 服务端,负责监听云端的变化,缓存并发送消息到 EdgeHub。
  • EdgeController: EdgeController 是一个扩展的 Kubernetes 控制器,管理边缘节点和 Pods 的元数据确保数据能够传递到指定的边缘节点。
  • DeviceController: (暂未涉及)DeviceController 是一个扩展的 Kubernetes 控制器,管理边缘设备,确保设备信息、设备状态的云边同步。

边缘部分

  • EdgeHub: EdgeHub 是一个 Web Socket 客户端,负责与边缘计算的云服务(例如 KubeEdge 架构图中的 Edge Controller)交互,包括同步云端资源更新、报告边缘主机和设备状态变化到云端等功能。
  • Edged: Edged 是运行在边缘节点的代理,用于管理容器化的应用程序。(裁剪版的 kubelet)
  • EventBus: (暂未涉及)EventBus 是一个与 MQTT 服务器 (mosquitto) 交互的 MQTT 客户端,为其他组件提供订阅和发布功能。
  • ServiceBus: (暂未涉及)ServiceBus 是一个运行在边缘的 HTTP 客户端,接受来自云上服务的请求,与运行在边缘端的 HTTP 服务器交互,提供了云上服务通过 HTTP 协议访问边缘端 HTTP 服务器的能力。
  • DeviceTwin: (暂未涉及)DeviceTwin 负责存储设备状态并将设备状态同步到云,它还为应用程序提供查询接口。
  • MetaManager: MetaManager 是消息处理器,位于 Edged 和 Edgehub 之间,它负责向轻量级数据库 (SQLite) 存储/检索元数据。

云端对边缘侧常规管理

云端对边缘侧的所有常规管理均基于 Kubernetes 对象实现。

云端 CloudCore 容器实例启动时 (keadm init —advertise-address=218.108.216.78):

  1. CloudHub 组件开始监听 10000 端口
  2. CloudHub 组件开始监听 10002 端口
    1. https://IP:10002/ca.crt: 提供云端根 CA 证书
    2. https://IP:10002/edge.crt: 为边缘侧签署并提供新证书

边缘侧 EdgeCore 实例启动时 (keadm join —token=404653ac1d6d82d37fa3180da1542468f7d41856e1c48c11139775941bd92e5e.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NjAyODExNTR9.thxhGiJuCAF_HsPdsn8YdxYAlY6hVRmjJMcqT3D6PaI) :

  1. EdgeHub 组件以不验证服务器侧证书的方式向 https://IP:10002/ca.crt 请求云端根 CA 证书,并做 SHA256 校验是否匹配 token 前半部分
  2. EdgeHub 组件生成 CSR 请求 https://IP:10002/edge.crt 进行签署,生成边缘侧证书
  3. EdgeHub 组件基于边缘侧证书向云端 10000 端口以 mTLS 方式发起并建立 WebSocket 连接
    1. 接受云端消息,路由分发到 EdgeCore 目标组件
    2. 集合 EdgeCore 其他组件的消息并推送到云端
    3. 心跳维持连接
  4. 特别地,云端对边缘侧的常规管理将通过 CloudHub → EdgeHub → MetaManager → EdgeD 链路下发指令

云端对边缘侧的常规管理流程

云端对边缘侧临时性管理

云端对边缘侧临时性管理动作,需要临时建立从云端到边缘侧的连接。但客观条件下,边缘侧与云端普遍单向可达。

Q: 怎么才能让云端发起的网络请求,顺利到达边缘侧设备?

A: 虚拟专用网络(VPN, Virtual Private Network)技术将专用网络延伸到公共网络上。用户能够在公共网络上访问部署在专用网络内的计算设备。

云端对边缘侧临时性管理

为使云端可以主动向边缘侧建立连接。CloudCore 与 EdgeCore 分别提供 CloudStream 与 EdgeStream 组件,并通过 TunnelServer 模块由边缘侧发起向云端建立一条 WebSocket 隧道,云端 TunnelServer 同时负责维护众多边缘侧设备的隧道会话。

  1. 用户利用 kubectl 向 Kubernetes API Server 发起查询某边缘侧容器实例日志的请求。
  2. Kubernetes API Server 检索 Node 资源,组装日志查询请求:https://IP:PORT/containerLogs/{podNamespace}/{podID}/{containerName} 。请求被主机捕获并做 DNAT 转发到 10003 端口。其中 IP:PORT 提取自
    1. .status.addresses[?].address
    2. .status.daemonEndpoints.kubeletEndpoint.Port
  3. CloudStream 组件 StreamServer 模块接收请求,组装成消息对象(组装时会改写请求 SCHEMA://IP:PORT[http://127.0.0.1:10350](http://127.0.0.1:10350) ),交给 TunnelServer 模块经路由下发到指定边缘侧设备
  4. 某边缘侧设备 EdgeStream 组件 TunnelServer 模块接收消息并推送给 StreamServer 模块
  5. 某边缘侧设备 EdgeStream 组件 StreamServer 模块接收消息并重新组装 HTTP 请求,最终向 EdgeD 组件发起查询日志请求并回复响应。

包括获取容器日志、在容器中执行命令、获取监测指标三类动作都利用这套隧道链路完成。其中在容器中执行命令稍显复杂,表现为 WebSocket over WebSocket。

KubeEdge 云边安装

获取 keadm 工具

docker run --rm kubeedge/installation-package:v1.11.1 cat /usr/local/bin/keadm > /usr/local/bin/keadm && chmod +x /usr/local/bin/keadm

云侧配置

keadm init 支持一键将 KubeEdge 云上部分部署到 Kubernetes 集群中

keadm init --advertise-address=${公网IP} --profile version=v1.11.1 --kube-config=$HOME/.kube/config --set cloudCore.modules.cloudHub.dnsNames[0]="'*.local.ffutop.com'"

云侧部署完成后

kubectl -n kubeedge get pods

可以查看部署后的容器实例 cloudcore-${HASH}-${HASH}

额外地:

  1. 需要将部署后的服务端口 100001000210004 映射到公网 IP
  2. 利用 Kubernetes 集群的 rootCA 签署新的证书(需包含 alternativeNames: *.local.ffutop.com ),改写 Kubernetes Secret 资源 cloudcore ,后重启 cloudcore 容器实例
  3. 需配置将 Node.status.addresses[?].address 的域名指向 cloudcore 10003 端口绑定的 IP(需保证 Kubernetes API Server 可达)

边缘侧配置

在云侧执行 keadm gettoken ,可以获得供边缘侧节点首次连接所需的令牌

keadm gettoken

在边缘侧执行 keadm join 将一键安装 edgecoremqtt

keadm join --cloudcore-ipport=${公网IP}:10000 --edgenode-name=${唯一标识}.local.ffutop.com --token=27a37ef16159f7d3be8fae95d588b79b3adaaf92727b72659eb89758c66ffda2.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTAyMTYwNzd9.JBj8LLYWXwbbvHKffJBpPd5CyxqapRQYDIXtFZErgYE

边缘侧一键启动无法改写默认配置项,故需要执行下述步骤启用 EdgeStream 组件,以实现对边缘侧的临时性管理

sed -i "s/edgeStream:\n\t\tenable: false/edgeStream:\n\t\tenable: true/" /etc/kubeedge/config/edgecore.yaml
systemctl restart edgecore.service

实际效果

可实现对节点的常规远程管控,包括:

列表查询所有边缘节点

[root@kubernetes ~]# kubectl get node --show-labels
NAME                     STATUS   ROLES        AGE    VERSION                    LABELS
192.168.100.27           Ready    master       2d8h   v1.24.2                    beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=192.168.100.27,kubernetes.io/os=linux,kubernetes.io/role=master
hz001.local.ffutop.com   Ready    agent,edge   26h    v1.22.6-kubeedge-v1.11.1   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=hz001.local.ffutop.com,kubernetes.io/os=linux,node-role.kubernetes.io/agent=,node-role.kubernetes.io/edge=,type=edge
hz002.local.ffutop.com   Ready    agent,edge   26h    v1.22.6-kubeedge-v1.11.1   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=hz002.local.ffutop.com,kubernetes.io/os=linux,node-role.kubernetes.io/agent=,node-role.kubernetes.io/edge=,type=edge

获取节点监测指标

[root@kubernetes ~]# kubectl top nodes
NAME                     CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
192.168.100.27           189m         4%     4152Mi          55%
hz001.local.ffutop.com   75m          0%     1612Mi          10%
hz002.local.ffutop.com   37m          0%     487Mi           6%

向指定边缘节点部署容器实例

[root@kubernetes ~]# kubectl get pods -o wide
NAME                         READY   STATUS    RESTARTS        AGE    IP               NODE                     NOMINATED NODE   READINESS GATES
busybox-67f548f75b-tgvq7     1/1     Running   2 (6h57m ago)   26h    192.168.0.200    hz001.local.ffutop.com   <none>           <none>
cloudcore-5f48f6bcc6-npctm   1/1     Running   0               131m   192.168.100.27   192.168.100.27           <none>           <none>

可实现对节点的临时性远程管控,包括:

在容器中执行命令

[root@kubernetes ~]# kubectl exec -it busybox-67f548f75b-tgvq7 -- sh
/ # ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq qlen 1000
    link/ether 00:a5:12:b8:5f:f4 brd ff:ff:ff:ff:ff:ff
    inet **192.168.0.200**/24 brd 192.168.0.255 scope global noprefixroute enp1s0
       valid_lft forever preferred_lft forever
    inet6 fe80::c48f:2fad:58b:23cb/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
3: enp2s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq qlen 1000
    link/ether 00:a5:12:b8:5f:f5 brd ff:ff:ff:ff:ff:ff
4: enp3s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq qlen 1000
    link/ether 00:a5:12:b8:5f:f6 brd ff:ff:ff:ff:ff:ff
5: enp4s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq qlen 1000
    link/ether 00:a5:12:b8:5f:f7 brd ff:ff:ff:ff:ff:ff
6: enp5s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq qlen 1000
    link/ether 00:a5:12:b8:5f:f8 brd ff:ff:ff:ff:ff:ff
7: enp6s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq qlen 1000
    link/ether 00:a5:12:b8:5f:f9 brd ff:ff:ff:ff:ff:ff
8: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue qlen 1000
    link/ether 52:54:00:7e:16:52 brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
       valid_lft forever preferred_lft forever
9: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast master virbr0 qlen 1000
    link/ether 52:54:00:7e:16:52 brd ff:ff:ff:ff:ff:ff
10: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue
    link/ether 02:42:3a:64:de:cc brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:3aff:fe64:decc/64 scope link
       valid_lft forever preferred_lft forever
56: veth856637a@if55: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue master docker0
    link/ether fe:7c:a3:af:33:49 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::fc7c:a3ff:feaf:3349/64 scope link
       valid_lft forever preferred_lft forever

查看容器日志

[root@kubernetes ~]# kubectl logs busybox-67f548f75b-tgvq7