如何在python中的Kubeflow容器化组件之间传递数据或文件

时间:2019-09-28 21:18:13

标签: kubeflow kubeflow-pipelines

我正在探索Kubeflow作为部署和连接典型ML管道的各个组件的一种选择。我将Docker容器用作Kubeflow组件,到目前为止,我一直无法成功使用ContainerOp.file_outputs对象在组件之间传递结果。

根据我对功能的了解,创建并保存到声明为组件file_outputs之一的文件后,该文件将继续存在并可由以下组件读取。

这是我尝试在管道python代码中声明的方式:

import kfp.dsl as dsl 
import kfp.gcp as gcp

@dsl.pipeline(name='kubeflow demo')
def pipeline(project_id='kubeflow-demo-254012'):
    data_collector = dsl.ContainerOp(
        name='data collector', 
        image='eu.gcr.io/kubeflow-demo-254012/data-collector',
        arguments=[ "--project_id", project_id ],
        file_outputs={ "output": '/output.txt' }
    )   
    data_preprocessor = dsl.ContainerOp(
        name='data preprocessor',
        image='eu.gcr.io/kubeflow-demo-254012/data-preprocessor',
        arguments=[ "--project_id", project_id ]
    )
    data_preprocessor.after(data_collector)
    #TODO: add other components
if __name__ == '__main__':
    import kfp.compiler as compiler
    compiler.Compiler().compile(pipeline, __file__ + '.tar.gz')

data-collector.py组件的python代码中,我获取数据集,然后将其写入output.txt。我可以从同一组件内的文件中读取文件,但不能在data-preprocessor.py内的文件中读取文件。

使用FileNotFoundError是否对基于容器的Kubeflow组件无效,还是我在代码中错误地使用了它?如果不是我的选择,是否可以在管道声明python代码内以编程方式创建Kubernetes卷并使用它们代替file_outputs

2 个答案:

答案 0 :(得分:1)

在一个Kubeflow管道组件中创建的文件在容器本地。要在后续步骤中引用它,您需要将其传递为:

data_preprocessor = dsl.ContainerOp(
        name='data preprocessor',
        image='eu.gcr.io/kubeflow-demo-254012/data-preprocessor',
        arguments=["--fetched_dataset", data_collector.outputs['output'],
                   "--project_id", project_id,
                  ]

注意:data_collector.outputs['output']将包含文件/output.txt的实际字符串内容(不是文件的路径)。如果要使其包含文件的路径,则需要将数据集写入共享存储(例如s3或已安装的PVC卷),并将指向共享存储的路径/链接写入/output.txtdata_preprocessor然后可以根据路径读取数据集。

答案 1 :(得分:0)

主要有三个步骤:

  1. 保存一个outputs.txt文件,其中将包含要传递给下一个组件的数据/参数/任何内容。 注意:它应该位于根级别,即/output.txt

  2. 将file_outputs = {'output':'/output.txt'}作为参数显示为示例。

  3. 在container_op内,您将在dsl.pipeline内部传递参数(传递给需要从较早组件输出的组件的相应参数)作为comp1.output(此处comp1是第一个产生输出并将其存储在/中的组件) output.txt)

import kfp
from kfp import dsl

def SendMsg(
    send_msg: str = 'akash'
):
    return dsl.ContainerOp(
        name = 'Print msg', 
        image = 'docker.io/akashdesarda/comp1:latest', 
        command = ['python', 'msg.py'],
        arguments=[
            '--msg', send_msg
        ],
        file_outputs={
            'output': '/output.txt',
        }
    )

def GetMsg(
    get_msg: str
):
    return dsl.ContainerOp(
        name = 'Read msg from 1st component',
        image = 'docker.io/akashdesarda/comp2:latest',
        command = ['python', 'msg.py'],
        arguments=[
            '--msg', get_msg
        ]
    )

@dsl.pipeline(
    name = 'Pass parameter',
    description = 'Passing para')
def  passing_parameter(send_msg):
    comp1 = SendMsg(send_msg)
    comp2 = GetMsg(comp1.output)


if __name__ == '__main__':
  import kfp.compiler as compiler
  compiler.Compiler().compile(passing_parameter, __file__ + '.tar.gz')