Device Controller 源码阅读
文章目录
设备控制器
设备控制器概述
设备控制器是KubeEdge的云组件,负责设备管理。KubeEdge中的设备管理是通过使用Kubernetes 自定义资源定义(CRD)来描述设备元数据/状态和设备控制器以在边缘和云之间同步这些设备更新来实现的。设备控制器启动两个独立的goroutine upstream controller
,分别称为和downstream controller
。这些不是单独的控制器,而是为了清楚起见在此命名。
设备控制器利用设备模型和设备实例来实现设备管理:
-设备模型:Adevice model
描述设备公开的设备属性以及访问这些属性的属性访问者。设备模型就像一个可重用的模板,使用它可以创建和管理许多设备。可以在此处找到有关设备型号定义的详细信息。
-设备实例:device
实例代表实际的设备对象。就像是device model
并引用模型中定义的属性。设备规格是静态的,而设备状态包含动态变化的数据,例如所需的设备属性状态和设备报告的状态。设备实例定义的详细信息可以在这里找到。
注意:可以在$ GOPATH / src / github.com / kubeedge / kubeedge / build / crd-samples / devices中找到一些协议的示例设备模型和设备实例。
设备控制器执行的操作
以下是设备控制器执行的功能:
–下游控制器:通过在K8S API服务器上进行监视,将设备更新从云同步到边缘节点
-上游控制器:使用以下命令将设备更新从边缘节点同步到云端设备双组件
上游控制器:
上游控制器从边缘节点监视更新,并将这些更新应用于云中的API服务器。更新和上游控制器可以采取的可能措施如下:
| 更新类型| 动作| | ————————————- | —————————————— |设备孪生报告状态已更新| 控制器在云中修补设备孪生属性的报告状态。|
将报告的设备孪生属性更新从边缘同步到云
映射器监视设备的更新,并通过MQTT代理将其报告给事件总线。事件总线将报告的设备状态发送到设备孪生,设备孪生将其存储在本地,然后将更新同步到云。设备控制器从边缘(通过cloudhub)监视设备更新,并更新云中报告的状态。
下游控制器:
下游控制器根据K8S API服务器监视设备更新。以下是更新的分类,以及下游控制器可以采取的措施:
更新类型 | 行动 |
---|---|
创建新设备型号 | 不适用 |
创建新设备 | 控制器创建一个新的配置图,以存储设备属性和在与设备关联的设备模型中定义的访问者。此配置映射存储在etcd中。边缘控制器中现有的配置映射同步机制用于将配置映射同步到egde。在容器中运行的映射器应用程序可以获取更新的配置映射,并使用属性和访问者元数据来访问设备。设备控制器还向边缘节点报告设备双元数据更新。 |
设备节点成员资格已更新 | 设备控制器将成员资格更新事件发送到边缘节点。 |
设备双期望状态已更新 | 设备控制器将孪生更新事件发送到边缘节点。 |
设备已删除 | 控制器发送设备孪生删除事件以删除与该设备关联的所有设备孪生。它还删除与设备关联的配置映射,并且此删除事件将同步到边缘。映射器应用程序有效地停止在设备上运行。 |
使用配置映射存储设备属性和访问者的想法是,这些元数据仅在边缘节点上运行的映射器应用程序才需要,以便连接到设备并收集数据。如果映射器作为容器运行,则可以将这些属性作为配置映射加载。下游控制器会监视云中对属性,访客等的任何添加,删除或更新,并在etcd中更新配置映射。如果映射器想要发现设备支持的属性,则可以从设备实例获取模型信息。同样,它可以获取协议信息以从设备实例连接到设备。一旦可以访问设备模型,就可以获取设备支持的属性。为了访问该属性,映射器需要获取相应的访客信息。可以从propertyVisitors列表中检索。最后,使用visitororConfig,映射器可以读取/写入与属性关联的数据。
将所需的设备双属性更新从云同步到边缘
设备控制器监视云中的设备更新并将其中继到边缘节点。这些更新由设备孪生本地存储。映射器通过MQTT代理获取这些更新,并根据更新在设备上运行。
源码阅读
1、代码入口
Device Controller在Cloudcore启动时注册,通过beehive消息通信框架调用Start()函数启动devicecontroller模块。
devicecontroller.Register(c.Modules.DeviceController, c.KubeAPIConfig)
2、结构定义、初始化
// DeviceController use beehive context message layer
type DeviceController struct {
enable bool
}
func newDeviceController(enable bool) *DeviceController {
return &DeviceController{
enable: enable,
}
}
3、启动
// Start controller
func (dc *DeviceController) Start() {
downstream, err := controller.NewDownstreamController()
if err != nil {
klog.Fatalf("New downstream controller failed with error: %s", err)
}
upstream, err := controller.NewUpstreamController(downstream)
if err != nil {
klog.Fatalf("new upstream controller failed with error: %s", err)
}
if err := downstream.Start(); err != nil {
klog.Fatalf("start downstream failed with error: %s", err)
}
// wait for downstream controller to start and load deviceModels and devices
// TODO think about sync
time.Sleep(1 * time.Second)
if err := upstream.Start(); err != nil {
klog.Fatalf("start upstream failed with error: %s", err)
}
}
4、上游控制器
4.1 构造
// UpstreamController subscribe messages from edge and sync to k8s api server
type UpstreamController struct {
crdClient *rest.RESTClient
messageLayer messagelayer.MessageLayer
// message channel
deviceStatusChan chan model.Message
// downstream controller to update device status in cache
dc *DownstreamController
}
4.2 初始化
// NewUpstreamController create UpstreamController from config
func NewUpstreamController(dc *DownstreamController) (*UpstreamController, error) {
config, err := utils.KubeConfig()
if err != nil {
klog.Warningf("Failed to create kube client: %s", err)
return nil, err
}
crdcli, err := utils.NewCRDClient(config)
if err != nil {
klog.Warningf("Failed to create crd client: %s", err)
return nil, err
}
uc := &UpstreamController{
crdClient: crdcli,
messageLayer: messagelayer.NewContextMessageLayer(),
dc: dc,
}
return uc, nil
}
4.3 启动
// Start UpstreamController
func (uc *UpstreamController) Start() error {
klog.Info("Start upstream devicecontroller")
uc.deviceStatusChan = make(chan model.Message, config.Config.Buffer.UpdateDeviceStatus)
go uc.dispatchMessage()
for i := 0; i < int(config.Config.Buffer.UpdateDeviceStatus); i++ {
go uc.updateDeviceStatus()
}
return nil
}
func (uc *UpstreamController) dispatchMessage() {
for {
select {
case <-beehiveContext.Done():
klog.Info("Stop dispatchMessage")
return
default:
}
msg, err := uc.messageLayer.Receive()
if err != nil {
klog.Warningf("Receive message failed, %s", err)
continue
}
klog.Infof("Dispatch message: %s", msg.GetID())
resourceType, err := messagelayer.GetResourceType(msg.GetResource())
if err != nil {
klog.Warningf("Parse message: %s resource type with error: %s", msg.GetID(), err)
continue
}
klog.Infof("Message: %s, resource type is: %s", msg.GetID(), resourceType)
switch resourceType {
case constants.ResourceTypeTwinEdgeUpdated:
uc.deviceStatusChan <- msg
default:
klog.Warningf("Message: %s, with resource type: %s not intended for device controller", msg.GetID(), resourceType)
}
}
}
func (uc *UpstreamController) updateDeviceStatus() {
for {
select {
case <-beehiveContext.Done():
klog.Info("Stop updateDeviceStatus")
return
case msg := <-uc.deviceStatusChan:
klog.Infof("Message: %s, operation is: %s, and resource is: %s", msg.GetID(), msg.GetOperation(), msg.GetResource())
msgTwin, err := uc.unmarshalDeviceStatusMessage(msg)
if err != nil {
klog.Warningf("Unmarshall failed due to error %v", err)
continue
}
deviceID, err := messagelayer.GetDeviceID(msg.GetResource())
if err != nil {
klog.Warning("Failed to get device id")
continue
}
device, ok := uc.dc.deviceManager.Device.Load(deviceID)
if !ok {
klog.Warningf("Device %s does not exist in downstream controller", deviceID)
continue
}
cacheDevice, ok := device.(*v1alpha2.Device)
if !ok {
klog.Warning("Failed to assert to CacheDevice type")
continue
}
deviceStatus := &DeviceStatus{Status: cacheDevice.Status}
for twinName, twin := range msgTwin.Twin {
for i, cacheTwin := range deviceStatus.Status.Twins {
if twinName == cacheTwin.PropertyName && twin.Actual != nil && twin.Actual.Value != nil {
reported := v1alpha2.TwinProperty{}
reported.Value = *twin.Actual.Value
reported.Metadata = make(map[string]string)
if twin.Actual.Metadata != nil {
reported.Metadata["timestamp"] = strconv.FormatInt(twin.Actual.Metadata.Timestamp, 10)
}
if twin.Metadata != nil {
reported.Metadata["type"] = twin.Metadata.Type
}
deviceStatus.Status.Twins[i].Reported = reported
break
}
}
}
// Store the status in cache so that when update is received by informer, it is not processed by downstream controller
cacheDevice.Status = deviceStatus.Status
uc.dc.deviceManager.Device.Store(deviceID, cacheDevice)
body, err := json.Marshal(deviceStatus)
if err != nil {
klog.Errorf("Failed to marshal device status %v", deviceStatus)
continue
}
result := uc.crdClient.Patch(MergePatchType).Namespace(cacheDevice.Namespace).Resource(ResourceTypeDevices).Name(deviceID).Body(body).Do(context.Background())
if result.Error() != nil {
klog.Errorf("Failed to patch device status %v of device %v in namespace %v", deviceStatus, deviceID, cacheDevice.Namespace)
continue
}
//send confirm message to edge twin
resMsg := model.NewMessage(msg.GetID())
nodeID, err := messagelayer.GetNodeID(msg)
if err != nil {
klog.Warningf("Message: %s process failure, get node id failed with error: %s", msg.GetID(), err)
continue
}
resource, err := messagelayer.BuildResource(nodeID, "twin", "")
if err != nil {
klog.Warningf("Message: %s process failure, build message resource failed with error: %s", msg.GetID(), err)
continue
}
resMsg.BuildRouter(modules.DeviceControllerModuleName, constants.GroupTwin, resource, model.ResponseOperation)
resMsg.Content = "OK"
err = uc.messageLayer.Response(*resMsg)
if err != nil {
klog.Warningf("Message: %s process failure, response failed with error: %s", msg.GetID(), err)
continue
}
klog.Infof("Message: %s process successfully", msg.GetID())
}
}
}
5、下游控制器
5.1 构造
// DownstreamController watch kubernetes api server and send change to edge
type DownstreamController struct {
kubeClient *kubernetes.Clientset
messageLayer messagelayer.MessageLayer
deviceManager *manager.DeviceManager
deviceModelManager *manager.DeviceModelManager
configMapManager *manager.ConfigMapManager
crdClient *rest.RESTClient
}
5.2初始化
// NewDownstreamController create a DownstreamController from config
func NewDownstreamController() (*DownstreamController, error) {
cli, err := utils.KubeClient()
if err != nil {
klog.Warningf("Create kube client failed with error: %s", err)
return nil, err
}
config, err := utils.KubeConfig()
if err != nil {
klog.Warningf("Get kubeConfig error: %v", err)
return nil, err
}
crdcli, err := utils.NewCRDClient(config)
if err != nil {
klog.Warningf("Failed to create crd client: %s", err)
return nil, err
}
deviceManager, err := manager.NewDeviceManager(crdcli, v1.NamespaceAll)
if err != nil {
klog.Warningf("Create device manager failed with error: %s", err)
return nil, err
}
deviceModelManager, err := manager.NewDeviceModelManager(crdcli, v1.NamespaceAll)
if err != nil {
klog.Warningf("Create device manager failed with error: %s", err)
return nil, err
}
dc := &DownstreamController{
kubeClient: cli,
deviceManager: deviceManager,
deviceModelManager: deviceModelManager,
messageLayer: messagelayer.NewContextMessageLayer(),
configMapManager: manager.NewConfigMapManager(),
}
return dc, nil
}
5.3 启动
// Start DownstreamController
func (dc *DownstreamController) Start() error {
klog.Info("Start downstream devicecontroller")
go dc.syncDeviceModel()
// Wait for adding all device model
// TODO need to think about sync
time.Sleep(1 * time.Second)
go dc.syncDevice()
return nil
}
// syncDeviceModel is used to get events from informer
func (dc *DownstreamController) syncDeviceModel() {
for {
select {
case <-beehiveContext.Done():
klog.Info("stop syncDeviceModel")
return
case e := <-dc.deviceModelManager.Events():
deviceModel, ok := e.Object.(*v1alpha2.DeviceModel)
if !ok {
klog.Warningf("object type: %T unsupported", deviceModel)
continue
}
switch e.Type {
case watch.Added:
dc.deviceModelAdded(deviceModel)
case watch.Deleted:
dc.deviceModelDeleted(deviceModel)
case watch.Modified:
dc.deviceModelUpdated(deviceModel)
default:
klog.Warningf("deviceModel event type: %s unsupported", e.Type)
}
}
}
}
// syncDevice is used to get device events from informer
func (dc *DownstreamController) syncDevice() {
for {
select {
case <-beehiveContext.Done():
klog.Info("Stop syncDevice")
return
case e := <-dc.deviceManager.Events():
device, ok := e.Object.(*v1alpha2.Device)
if !ok {
klog.Warningf("Object type: %T unsupported", device)
continue
}
switch e.Type {
case watch.Added:
dc.deviceAdded(device)
case watch.Deleted:
dc.deviceDeleted(device)
case watch.Modified:
dc.deviceUpdated(device)
default:
klog.Warningf("Device event type: %s unsupported", e.Type)
}
}
}
}
klog.Warningf(“Object type: %T unsupported”, device)
continue
}
switch e.Type {
case watch.Added:
dc.deviceAdded(device)
case watch.Deleted:
dc.deviceDeleted(device)
case watch.Modified:
dc.deviceUpdated(device)
default:
klog.Warningf(“Device event type: %s unsupported”, e.Type)
}
}
}
}
参考链接:https://kubeedge.io/en/docs/components/cloud/device_controller/