使用python sdk比较容易构建pipeline workflow,在此对workflow的构建流程做解析。
由于sdk生成workflow yaml文件后,argo crd直接通过yaml就能完成workflow在k8s集群创建任务,流程解析会停留在workflow提交之前。
在创建工作流时可以使用临时组件,也可以使用复用组件。
分别介绍这两种方式的创建过程。
Component
使用临时组件构建的workflow包括3个要素:
- ContainerOp
- pipeline func
- pipeline decorator
一个简单的例子:
@ep.pipeline(
name='demo-pipelines',
description='build pipeline with tmp components using easy-kubeflow.'
) # 装饰器函数
def PipelinesTest(): # workflow核心方法
# workflow组件
# build load data op
load_data = Component(...)
# build train op
train = Component(...)
# build predict op
predict = Component(...)
多个临时组件op之间的依赖关系构建有两种方式:
- 显示的配置临时组件op之间的依赖关系
eg. train.after(load_data) predict.after(load_data, train)
- 配置op的输入数据中指定依赖关系
train op配置输入的数据为load_data op输出
eg. train = Component(arguments={ '--x_train': ep.input_arg_path(load_data.outputs['x_train'])})
ContainerOp
关于ContainerOp sdk文档的描述:
ContainerOp Represents an op implemented by a container image.
这个类和他的父类,对标容器的配置,做了对应参数的实现。
临时组件构造直接调用dsl.ContainerOp
类即可
实现的参数:
- 容器名称
- 镜像名称
- 数据卷名称
- 挂载卷名称
- 启动命令
- …
workflow中每一个模块的镜像,存放了模块的具体实现逻辑。在工作流启动后,会让事先约定的顺序执行每一个模块。
pipeline func
pipeline func负责归拢workflow中的模块,将workflow中的模块准备成一个方法,便于pipeline装饰器理解。
pipeline decorator
pipeline装饰器负责将包含workflow模块的方法,装饰成pipeline编译器能理解的方法。方法最终会被编译成operator能解析的.yaml文件。
Reuse Component
使用可复用组件构建的workflow也需要注意,同样的3个要素:
- ContainerOp
- pipeline func
- pipeline decorator
一个简单的例子:
@ep.pipeline(
name='resuse-components',
description='build pipeline with resue components using easy-kubeflow.'
)
def PipelinesTest(): # workflow核心方法
# workflow组件
# load data op
load_data = ReuseComponent(...)
# train op
train = ReuseComponent(...)
# predict op
predict = ReuseComponent(...)
多个复用组件op之间的依赖关系构建方式同临时组件。
ContainerOp
复用组件构造与临时组件构造方法差异较大,在这里主要解析一下复用组建从.yaml文件生成dsl.ContainerOp
的流程。
通过.yaml文件构造ContainerOp分为:
- 构建component_spec
- 通过component_spec构建工厂方法
加载component spec
从.yaml或者.zip文件加载component相关spec字段:
def _load_component_spec_from_component_text(text) -> ComponentSpec:
component_dict = load_yaml(text)
component_spec = ComponentSpec.from_dict(component_dict)
# Calculating hash digest for the component
import hashlib
data = text if isinstance(text, bytes) else text.encode('utf-8')
data = data.replace(b'\r\n', b'\n') # Normalizing line endings
digest = hashlib.sha256(data).hexdigest()
component_spec._digest = digest
return component_spec
ComponentSpec类相关定义:
class ComponentSpec(ModelBase):
'''Component specification. Describes the metadata (name, description, annotations and labels), the interface (inputs and outputs) and the implementation of the component.'''
def __init__(
self,
name: Optional[str] = None, #? Move to metadata?
description: Optional[str] = None, #? Move to metadata?
metadata: Optional[MetadataSpec] = None,
inputs: Optional[List[InputSpec]] = None,
outputs: Optional[List[OutputSpec]] = None,
implementation: Optional[ImplementationType] = None,
version: Optional[str] = 'google.com/cloud/pipelines/component/v1',
#tags: Optional[Set[str]] = None,
):
核心方法:
从component文件中创建复用组件工厂方法
def _create_task_factory_from_component_spec(
component_spec:ComponentSpec, # component定义的字段信息
component_filename=None, # 存放component相关的.yaml或者.zip文件
component_ref: ComponentReference = None
)
参考信息:
- 博客中涉及到例子:https://github.com/Crazybean-lwb/kubeflow-examples/tree/master/mnist_stage
- 便捷创建2种component的工具包:https://pypi.org/project/easy-kubeflow/