系列文章:
Flyte工作流(Workflow)平台调研(一)——整体架构
Flyte工作流(Workflow)平台调研(二)——核心概念说明
Flyte工作流(Workflow)平台调研(三)——核心组件原理
Flyte工作流(Workflow)平台调研(四)——服务部署
Flyte工作流(Workflow)平台调研(五)——扩展集成
Flyte工作流(Workflow)平台调研(六)——跟Ray框架对比
Flyte工作流(Workflow)平台调研(七)——FlyteAdmin核心源码走读
Flyte工作流(Workflow)平台调研(八)——FlytePropeller核心源码走读
正文:
上一篇文章中整体介绍了Flyte项目,并且详细分析和走读了FlyteAdmin的源代码,解读了一个工作流到FlyteAdmin后端的业务转换流程。本文要详细分析和走读FlytePropeller的源代码,帮助大家了解工作流到了FlytePropeller后,在K8S中生效执行的全过程。
FlytePropeller 是 Flyte 工作流执行框架的核心组件之一,负责管理和执行 Flyte 工作流。它通过与 Kubernetes 紧密集成,实现对FlyteAdmin提交过来的复杂任务 DAG(有向无环图)的调度和执行,并确保任务执行的可靠性和可扩展性。
上一篇文章讲到FlyteAdmin接收到工作流(Workflow)后,经过校验、转换等一系列操作把用户请求的工作流(Workflow)转化为一个可以被FlytePropeller识别和执行的FlyteWorkflow对象,这个对象是一个K8S的CRD(Custom Resource Definition)资源实例。本文就会详细介绍,FlytePropeller是怎么实现一个工作流专用的K8S Operator,通过 Kubernetes CRD 和控制器机制实现工作流的动态调度和执行。
项目结构如下:
代码的整体逻辑架构为:
代码包功能简介:
Workflow
CR 的事件。以及工作流状态的调度、更新和同步逻辑。leaderelection包,FlytePropeller 的多个实例(Pod)需要通过选主机制确保只有一个实例作为主节点,负责调度和管理工作流。其他 Pod 处于备用状态,等待接替主节点的角色,以实现高可用性和调度逻辑的一致性。
utils包,代码的公共工具包,避免循环调用
下面分别介绍代码的核心原理、功能和逻辑实现。
Kubernetes CRD 驱动
Workflow
CR 对象,存储在 Kubernetes 中。工作流 DAG 执行
任务容器化
状态管理与持久化
controller
包是 Flyte 项目中核心的控制器逻辑实现部分,负责管理和调度 Flyte 工作流的执行。它的主要职责是根据用户定义的 Flyte 工作流描述,将任务分解为更小的单元并调度执行,同时追踪其状态,确保任务的正确性和一致性。它的设计基于 Kubernetes 控制器模式,具有高扩展性和可靠性。
flytepropeller/pkg/controller/controller.go是一个 Flyte 控制器的完整实现,它主要用于管理 Flyte 工作流(FlyteWorkflow)的生命周期。控制器与 Kubernetes 集群的资源交互,以实现工作流的自动化管理。下面是对该控制器实现的原理总结。
Flyte 控制器负责管理和执行 Flyte 工作流的任务,确保资源的一致性与正确性。具体来说,它通过监听 Kubernetes 中 FlyteWorkflow 资源的变化,自动执行相应的操作,如启动工作流、更新工作流状态、清理不再需要的资源等。
控制器启动的主要流程:
启动代码如下:
// 负责初始化控制器的所有依赖与核心组件
func New(ctx context.Context, cfg *config.Config, kubeClientset kubernetes.Interface, flytepropellerClientset clientset.Interface,
flyteworkflowInformerFactory informers.SharedInformerFactory, informerFactory k8sInformers.SharedInformerFactory,
kubeClient executors.Client, scope promutils.Scope) (*Controller, error) {
adminClient, signalClient, authOpts, err := getAdminClient(ctx)
if err != nil {
logger.Errorf(ctx, "failed to initialize Admin client, err :%s", err.Error())
return nil, err
}
sCfg := storage.GetConfig()
if sCfg == nil {
logger.Errorf(ctx, "Storage configuration missing.")
}
// 创建一个数据存储,用于管理工作流的元数据
store, err := storage.NewDataStore(sCfg, scope.NewSubScope("metastore"))
if err != nil {
return nil, errors.Wrapf(err, "Failed to create Metadata storage")
}
// 负责将事件推送到 Flyte 的外部系统
logger.Info(ctx, "Setting up event sink and recorder")
eventSink, err := events.ConstructEventSink(ctx, events.GetConfig(ctx), scope.NewSubScope("event_sink"))
if err != nil {
return nil, errors.Wrapf(err, "Failed to create EventSink [%v], error %v", events.GetConfig(ctx).Type, err)
}
// 初始化垃圾回收器(GC)负责清理过期或无效的工作流
gc, err := NewGarbageCollector(cfg, scope, clock.RealClock{}, kubeClientset.CoreV1().Namespaces(), flytepropellerClientset.FlyteworkflowV1alpha1())
if err != nil {
logger.Errorf(ctx, "failed to initialize GC for workflows")
return nil, errors.Wrapf(err, "failed to initialize WF GC")
}
eventRecorder, err := utils.NewK8sEventRecorder(ctx, kubeClientset, controllerAgentName, cfg.PublishK8sEvents)
if err != nil {
logger.Errorf(ctx, "failed to event recorder %v", err)
return nil, errors.Wrapf(err, "failed to initialize resource lock.")
}
controller := &Controller{
metrics: newControllerMetrics(scope),
recorder: eventRecorder,
gc: gc,
numWorkers: cfg.Workers,
}
// 设置领导选举,确保在分布式环境中,只有一个控制器实例处于活动状态(通过 Kubernetes 的资源锁实现)
lock, err := leader.NewResourceLock(kubeClientset.CoreV1(), kubeClientset.CoordinationV1(), eventRecorder, cfg.LeaderElection)
if err != nil {
logger.Errorf(ctx, "failed to initialize resource lock.")
return nil, errors.Wrapf(err, "failed to initialize resource lock.")
}
if lock != nil {
logger.Infof(ctx, "Creating leader elector for the controller.")
controller.leaderElector, err = leader.NewLeaderElector(lock, cfg.LeaderElection, controller.onStartedLeading, func() {
logger.Fatal(ctx, "Lost leader state. Shutting down.")
})
if err != nil {
logger.Errorf(ctx, "failed to initialize leader elector.")
return nil, errors.Wrapf(err, "failed to initialize leader elector.")
}
}
// WE are disabling this as the metrics have high cardinality. Metrics seem to be emitted per pod and this has problems
// when we create new pods
// Set Client Metrics Provider
// setClientMetricsProvider(scope.NewSubScope("k8s_client"))
// obtain references to shared index informers for FlyteWorkflow.
flyteworkflowInformer := flyteworkflowInformerFactory.Flyteworkflow().V1alpha1().FlyteWorkflows()
controller.flyteworkflowSynced = flyteworkflowInformer.Informer().HasSynced
podTemplateInformer := informerFactory.Core().V1().PodTemplates()
// set default namespace for pod template store
podNamespace, found := os.LookupEnv(podNamespaceEnvVar)
if !found {
podNamespace = podDefaultNamespace