KubeEdge-Sedna源码解析(转载)

date
Jan 9, 2025
slug
kubeedge-sedna-sourcecode-analysis
status
Published
tags
转载
云原生
KubeEdge
summary
转载:sedna源码解析
type
Post
原文作者:jaypume
转载供自己学习使用,方便查阅。

KubeEdge-Sedna概述

Sedna是在KubeEdge SIG AI中孵化的一个边云协同AI项目。得益于KubeEdge提供的边云协同能力,Sedna可以实现跨边云的协同训练和协同推理能力,如联合推理、增量学习、联邦学习、终身学习等。Sedna支持目前广泛使用的AI框架,如TensorFlow/Pytorch/MindSpore等,现有AI类应用可以无缝迁移到Sedna, 快速实现边云协同的训练和推理,可在降低成本、提升模型性能、保护数据隐私等方面获得提升。
项目主页: https://github.com/kubeedge/sedna
文档参考: https://sedna.readthedocs.io

整体架构

Sedna的边云协同基于KubeEdge提供的如下能力实现 * 跨边云应用统一编排 * Router: 管理面云边高可靠消息通道 * EdgeMesh: 数据面跨边云微服务发现和流量治理
notion image
基本组件
  • GlobalManager
    • 统一边云协同AI任务管理
    • 跨边云协同管理与协同
    • 中心配置管理
  • LocalController
    • 边云协同AI任务的本地流程控制
    • 本地通用管理: 模型,数据集,状态同步等
  • Lib
    • 面向AI开发者和应用开发者,暴露边云协同AI功能给应用
  • Worker
    • 执行训练或推理任务, 基于现有AI框架开发的训练/推理程序
    • 不同特性对应不同的worker组,worker可部署在边上或云上,并进行协同

工程目录

目录
说明
.github
Sedna github CICD流水线配置。
LICENSES
Sedna Licenses以及相关vendor Licenses。
build
GM/LC等管理面构建的Dockersfile;生成的CRD定义yaml文件;CRD样例yaml文件;
cmd
GM/LC管里面的启动函数。
components
监控和图形化展示的组件。
docs
proposals和安装文档。
examples
协同推理、增量学习、终身学习、联邦学习的使用样例。
hack
面向开发者的代码生成工具、及其他开发会用到的脚本。
lib
Sedna Library,用于开发边云协同AI应用的Python依赖库。
pkg
API定义;生成的CRD的client-go代码;Sedna GM/LC 管里面的核心代码。
scripts
面向使用者的安装脚本。
test
E2E测试代码及测试工具。
vendor
依赖的第三方项目源码。

Sedna管理面源码解析(Go)

GM: Global Manager

GM,一个K8S operator

operator是什么?
An Operator is an application-specific controller that extends the Kubernetes API to create, configure and manage instances of complex stateful applications on behalf of a Kubernetes user. It builds upon the basic Kubernetes resource and controller concepts, but also includes domain or application-specific knowledge to automate common tasks better managed by computers. 1
对于Sedna,Sedna控制了边云协同AI应用中,如何配置worker部署启动参数、如何协同、如何流转等,那么我们可以这么定义:Sedna GM是“边云协同AI应用“这个特定领域的控制器
The following components form the three main parts of an operator:
  • API: The data that describes the operand’s configuration. The API includes:
    • Custom resource definition (CRD), which defines a schema of settings available for configuring the operand.
    • Programmatic API, which defines the same data schema as the CRD and is implemented using the operator’s programming language, such as Go.
    • Custom resource (CR), which specifies values for the settings defined by the CRD; these values describe the configuration of an operand.
  • Controller: The brains of the operator. The controller creates managed resources based on the description in the custom resource; controllers are implemented using the operator’s programming language, such as Go. 2
通过上面Redhat的定义,我们可以看到组成一个k8s operator几个重要的概念包括 CRD、API、CR和Controller。
下面是Sedna GM Operator的示意图:
notion image
接下来的章节会按照组成K8S operator的几个组件来展开说明,包括CR、CRD、API、Controller,其中Controller是主要的控制逻辑模块。

CR

Sedna本身支持边云协同推理、增量学习、终身学习、联邦学习,为了方便解读代码,本文结合终身学习具体特性和样例来分析。其他三个特性的代码实现存在共通之处,可以类比参考。
CR样例
这里贴了一段终身学习CR样例,可以基于这个CR通过kubectl来创建对应的终身学习资源对象,详细使用步骤可以参考这里。其中关键的字段解释如下:
  • dataset:指定数据集对象名称,数据集也是一个CR资源。
  • trainSpec:终身学习中,训练worker的启动参数,包括镜像和环境变量等容器配置。
  • trigger:终身学习中,启动训练worker的触发条件。
  • evalSpec:终身学习中,评估work的启动参数,包括镜像和环境变量等容器配置。
  • deploySpec:终身学习中,推理work的启动参数,包括镜像和环境变量等容器配置。
  • outputDir:终身学习中,训练生成的模型文件输出路径。
build/crd-samples/sedna/lifelonglearningjobv1alpha1.yaml

CRD

CRD可以看作是CR的模板,在k8s集群能创建对应CR之前需要将对应的CRD在k8s集群中进行声明。CRD对应的yaml文件可以手动编写或自动生成,对于一些相对复杂的CRD定义建议采用通过k8s相关工具生成。比如Sedna这里使用的是kubebuilder的controller-gen进行自动生成与更新,Sedna项目提供了封装好的脚本,直接通过make crds命令即可生成和更新对应build/crds/目录下的CRD文件。相关shell脚本可以参考这个文件Makefile中的crds: controller-gen
想要完成一个CRD定义,最重要的是需要指定group、version和kind,通常简称为GVK。而CR资源对象本身称为Resource,相较于面向对象中的概念,Resouce类比为Object,Kind类比于Class,也就可以说Resource是Kind的实例。下表展示了终身学习CRD和CR对应的GVR和GVK:
Group
Version
Resource
Kind
CRD
apiextensions.k8s.io
v1
lifelonglearningjobs.sedna.io
CustomResourceDefinition
CR
sedna.io
v1alpha1
lifelonglearningjob
LifelongLearningJob
在K8S集群中资源是以REST URI的形式来组织的,组织的路径如下:
notion image
了解了上述的规则后,我们可以快速的拼接好要管理的k8s资源对象的REST URI地址,这为某些不能依赖k8s client(kubectl, client-go等)的情况下访问集群资源提供了简便的方式。比如:
通过Rest接口查看终身学习CRD描述:
通过Rest接口查看终身学习CR列表:
比如如果某些编程语言没有官方的k8s client SDK, 那么可以统一采用如上Rest接口形式进行封装。
下面是Sedna 终身学习CRD定义,一些需要关注的字段如下:
  • apiVersion: apiextensions.k8s.io/v1,当前所有的CRD都扩展自apiextensions.k8s.io/v1这个Version。
  • kind: CustomResourceDefinition,当前所有的CRD都继承自CustomResourceDefinition这个Kind。
  • spec.group: sedna.io,自定义资源的Group名称为sedna.io。
  • spec.names.kind: LifelongLearningJob, 自定义资源新增加的类型,这里是LifelongLearningJob。
  • spec.names.shortNames: - ll,在使用kubectl可以使用这个缩写”ll“查询到LifelongLearningJob资源。
build/crds/sedna.io_lifelonglearningjobs.yaml

API

上面提到我们的CRD是自动生成的,那生成这些CRD所需要的API基础定义在哪里呢?
pkg/apis/sedna/v1alpha1/lifelonglearningjob_types.go
上面的代码片段中,补充了额外的说明,需要注意的有如下几点:
  • // +kubebuilder... :注释是给kubebuilder等代码自动生成工具的配置参数,会被这些工具解析。
  • type LifelongLearningJob struct{...}:定义了终身学习CRD整体API,主要包含Spec和Status定义,分别代表期望状态和实际状态。
  • type LLJobSpec struct {...}:在创建LifelongLearningJob CR时需要配置的参数;如果需要扩展终身学习字段的接口,可以在这里修改。
其他协同推理、增量学习、联邦学习相关的API定义都可以在pkg/apis/sedna/v1alpha1/这个目录下找到。
更新client-go代码
一旦新增或者更新了*_types.go中的定义,则需要执行如下命令进行client-go代码更新:
生成的代码位于pkg/client
client-go中的代码会在后面的Contrller逻辑中用到。
更新CRD定义
一旦新增或者更新了*_types.go中的定义,则需要执行如下命令进行CRD代码更新:
生成的CRD定义yaml文件位于build/crds。更新这些定义之后,也需要同步在K8s集群中重新kubectl apply一下,以将新的CRD在集群中生效。

Controller

终身学习最主要的控制逻辑在这个pkg/globalmanager/controllers/lifelonglearning/lifelonglearningjob.go文件里面,包括训练评估Worker什么时候触发、Worker参数如何同步到边缘等。
在进入到终身学习的控制逻辑之前,整体的调用流程可以参考下面伪代码:
下面为了讲解Sedna LifelongLeraningJob Controller的控制逻辑,也按照上面1~10的标号来讲解:

【1】main函数入口

sedna-gm.go是GM模块的启动入口,主要包括日志初始化配置、app.NewControllerCommand()中执行了参数的解析、启动GM对应的controller。
cmd/sedna-gm/sedna-gm.go

【2】GM系统配置加载

GM加载系统配置,包括K8S集群配置、启动监听的Websocket地址端口、KB服务的地址等。
pkg/globalmanager/controllers/manager.go
pkg/globalmanager/config/config.go

【3】GM整体初始化

GM整体初始化的步骤如下,包括初始化Sedna CRD client、绑定并启动边云消息通信处理函数、启动各个特定对应的controller、启动websock开始监听消息。
pkg/globalmanager/controllers/manager.go

【4】CRD client初始化

clientset.NewForConfig()调用的原始函数位于pkg/client/clientset/versioned/clientset.go,前面提到这里是由client-go工具根据Sedna CRD定义自动生成的代码,通过go语言调用CRD定义的资源对象的增删改查。
下面代码是LifelongLearningJob Controller初始化的函数,其中就依赖client-go生成的CRD client代码。主要做了这么几件事:
  • 获取LifelongLearningJob的Informer。Informer可以看作是controller的K8S api-server的”本地缓存“,用来减少api-server的数据读取压力。
  • 配置LifelongLearningJob Controller的参数或成员变量,包括k8s client、sedna client、GM controller通用配置。
  • 绑定LifelongLearningJob CRD资源的Add、Update、Delete对应事件的回调函数。
pkg/globalmanager/controllers/lifelonglearning/lifelonglearningjob.go
下面截图也展示了Sedna CRD client在其他模块的一些引用。
notion image

【5】消息处理初始化

uc.Run()里面会初始化UpstreamController,UpstreamController用来处理边缘发送过来的所有消息。
for循环持续的监听context.upstreamChannel, 一旦有消息则通过uc.updateHandlers[kind]根据kind类型获取对应的handler,并调用此handler回调函数进行消息处理。uc.updateHandlers是一个map,里面存储了协同推理、增量学习、联邦学习、终身学习对应的updateHandlers.
pkg/globalmanager/controllers/upstream.go
ReceiveFromEdge提供一个阻塞的通道,来接收来自边缘节点LC发送的消息,消息的类型为nodeMessage
pkg/globalmanager/messagelayer/ws/context.go

【6】Controller注册

NewRegistry()函数注册了所有特性初始化函数,如果想扩展新的边云协同特性,需要在这里添加对应的New函数。
pkg/globalmanager/controllers/registry.go

【7】云端消息同步到边缘

f.SetDownstreamSendFunc()绑定了各个特性对应的边缘同步消息函数syncToEdge()
对于终身学习来说,其同步消息的步骤主要包括:
  • 获取到对应的数据集指定的节点,Dataset CRD对象中有一个字段记录了Node名称)。
  • 获取到训练、评估、部署对应的节点名称,这些名称基于Annotation记录。
  • 根据LifelongLearningJob所处训练、评估、部署阶段不同,发送消息到不同的节点上。
pkg/globalmanager/controllers/lifelonglearning/downstream.go

【8】边缘消息同步到云端

f.SetUpstreamHandler() 绑定了各个特性对应的云端消息消息同步函数updateFromEdge()
对于终身学习来说,其同步消息主要做了几件事:
  • 根据不同的边缘节点任务完成情况,变更当前LifelongLearningJob的整体状态。
  • 将当前LifelongLearningJob的整体状态写回k8s,也就是LifelongLearningJob这个CR的Status字段。
  • 解析边缘消息结构体,当前是以json的形式定义的,消息体示例如下:
GM接收到的消息体示例:
pkg/globalmanager/controllers/lifelonglearning/upstream.go

【9】Controller核心处理逻辑

f.run()会调用各个特性对应Controller的处理函数,下面是LifelongLearningJob的run()函数。
先会通过WaitForNamedCacheSync去等待Pod和LifelongLearningJob资源对象是否已经同步到Informer中。如果已经同步,则会启动指定数量的worker对LifelongLearningJob进行处理。
pkg/globalmanager/controllers/lifelonglearning/lifelonglearningjob.go
c.worker方法中,会调用processNextWorkItem()去处理对应的LifelongLearningJob资源对象,
pkg/globalmanager/controllers/lifelonglearning/lifelonglearningjob.go
c.processNextWorkItem()会调用c.sync()函数来处理特性相关的逻辑。
pkg/globalmanager/controllers/lifelonglearning/lifelonglearningjob.go
sync是对具体的LifelongLearningJob进行逻辑处理了,主要做了这么几件事:
  • 通过SplitMetaNamespaceKey将LifelongLearningJob的key切分为namespace和name。
  • 通过c.jobLister获取LifelongLearningJob的资源对象。
  • 通过transitJobState来分析当前job应该进入到训练、评估、部署阶段了。
  • 如果LifelongLearningJob的Status更新了,那么需要通过c.updateJobStatus()写回k8s资源对象中,这样通过kubectl查询到的就是最新的状态了,比如说当前在评估阶段、生成的模型路径在哪里等信息。
  • 任务失败等异常处理。
其中transitJobState()是终身学习任务流转的核心逻辑,包括训练、评估、部署分别在什么时候启动、停止等的控制,详细流转逻辑可以结合下图对应的状态流转图进行分析。
notion image

【10】websocket监听启动

启动一个websocket地址,用于接收【8】中提到的边侧传过来的消息,默认启动的IP端口是0.0.0.0:9000
pkg/globalmanager/controllers/manager.go

LC: Local Controller

LC是部署在边缘节点,主要负责本地的任务管理和消息代理。LC入口函数在cmd/sedna-lc/sedna-lc.go,相关入口分析可以参考GM章节。下面这里贴一下本地任务管理注册的函数入口:
cmd/sedna-lc/app/server.go

本地任务管理

Manager这个对象负责了在边缘的任务管理,下面是其数据结构定义:
pkg/localcontroller/managers/lifelonglearning/lifelonglearningjob.go
主要的管理流程可以参考如下startJob()代码,其中主要:
  • 监控并处理同步到边缘的Dataset对象,比如监听数据集样本数量是否达到阈值,达到则启动训练。
  • 根据当前任务不同阶段,触发不同阶段的训练、评估、部署任务。本地不直接起任务,而是把对应的状态上报到GM,由GM统一调度来启动对应的训练、评估、部署任务。
pkg/localcontroller/managers/lifelonglearning/lifelonglearningjob.go
除了整体的任务流程管理,其他功能还包括数据集的监控、模型的下载、任务本地数据库备份等功能。

消息代理

LC除了把状态变化的消息往云端传之外,还会在本地0.0.0.0:9100端口启动一个HTTP Server,用来接收把Lib库传输过来的消息整合并统一传输给GM,起到消息代理的作用。下面是注册的rest接口路由和消息处理函数。
pkg/localcontroller/server/server.go
pkg/localcontroller/server/server.go

Sedna Lib源码解析(Python)

Lib是面向AI开发者和应用开发者的Python 库,方便开发者把自己已有的代码改造成边云协同的。
下面是Lib的工程目录结构:
下面摘取每个部分比较典型的代码进行分析讲解。

core

core部分主要是封装了用户的回调函数,比如下面的train,主要是回调用户封装的tensorflow、pytorch、mindspore的train函数。
  • 配置后处理函数。
  • 调用云端知识库进行训练、推理等。
  • 更新云端知识库。在终身学习中,云端知识库用来保存新的模型和样本,会被不断更新。
  • 将当前训练任务执行的情况发送给LC,比如训练任务是否完成、训练后的指标是多少。
lib/sedna/core/lifelong_learning/lifelong_learning.py

backend

MSBackend这里定义了一种Sedna支持的后端框架Mindspore,如果对于某个框架定义好典型的train、predict、evaluate函数,那么Sedna Lib 将能够支持将其作为backend,能够实现把现有AI代码进行简单的封装即可实现边云协同的能力。
lib/sedna/backend/mindspore/__init__.py

datasource

datasource中封装了常用的数据集格式处理函数,这样就不需要通过
lib/sedna/datasources/__init__.py

algorithms

Sedna面向边云协同AI场景,需要有针对这种场景下的算法。本身也集成了若干典型的难例识别算法,比如下面的交叉熵阈值的算法,能够在边侧模型不自信的时候识别到对应的样本。
Sedna不仅是为了集成这些基础的算法,更是为了支持边云协同框架下能扩展更多实用的算法来优化边云整体训练、推理性能。这才是Sedna Lib的这套框架所希望实现的。
lib/sedna/algorithms/hard_example_mining/hard_example_mining.py

  1. https://www.redhat.com/en/topics/containers/what-is-a-kubernetes-operator
  1. https://developers.redhat.com/articles/2021/06/22/kubernetes-operators-101-part-2-how-operators-work
 

© Shemol 2022 - 2025