本周完成第三部分:
通过Elyra完成Pipeline的图形化构建。
Elyra的构建逻辑:如何在每个环节替代原生构建方法?
组件的运行环境
如上图,将Pipeline分为三个部分:
- 下载数据
- 训练模型
- 上传模型
不再需要为每个部分组件单独构建镜像,直接将ipynb或py文件拖拽上画布即可。
运行时,每部分仍运行在独立的容器中,可以为各部分设置它们独立执行环境的基础镜像。
Pipeline的自定义参数
在(二)中,最终pipeline有一些自定义参数,需要我们手动设置控制:
当时是通过自定义pipeline函数参数实现的:
在Elyra中,这点是通过设置环境变量实现的:
在脚本中,通过 os.getenv()设置参数及其默认值:
在该Notebook的Properties中,则可以编辑其环境变量:
若某个环境变量仅想保持其默认值,则直接在Properties中删除该环境变量即可。(不然它就等于”了)
环境变量在不同的Notebook中并无继承关系;某个Notebook的环境变量在上一个Notebook中已经出现,若想保持一致,则仍需要在该Notebook中手动设置。
Pipeline的文件输出
在运行Pipeline时,某个组件可能会输出新的文件,并将作为下一个组件的输入。
在原生方法中,我们为每个组件都挂上了同一个PV卷;因此,每个组件虽然运行在各自的独立环境中,但又享有共同的存储空间。
但在Elyra中,我们必须明确定义其输出文件,该输出文件才可被下一个组件所访问。
如上图,我们在load_data的Properties中定义了该ipynb文件的输出文件为:
dataset/objectname(object_name(object_name为需要我们手动设置的环境变量),则之后的组件都可以访问该文件。
注:
感觉,Output Files 得这样定义才是对的…:
不然,其他组件连dataset/都没看到,应该更看不到dataset/$object_name了。
Pipeline的文件输入(文件依赖)
在原生方法中,组件在封装成镜像时,除了主要的逻辑代码外,我们可能还会在镜像中额外打包一些辅助脚本?如utils.py等;
而这在Elyra中,是通过File Dependencies实现的。
与原生方法一致的是,所依赖的文件需要放在脚本的文件目录或子目录下。
编写脚本并使用Elyra替代原生方法
环境变量的设置
在上文中,我们解决了自定义参数、文件输出共享、文件依赖的问题,而脚本的代码逻辑是不变的;
因此,我们可以使用Elyra替代原生方法了。
在load_data.ipynb(load_data.py)中,我们使用os.getenv替代argparse的自定义参数方法:
原生:
Elyra:
在train_model.ipynb,load_model.ipynb中同样进行该操作。
各组件中Pipeline的文件输入输出关系
在原生方法中,我们在组织Pipeline时,定义了共享PV卷,并在PV卷中新建了dataset,model文件夹,各组件可以通过访问所需文件夹获取相应文件:
但是,在Elyra方法中,我们需要通过显式定义以共享不同组件的输出文件。
在load_data的Properties中,设置数据集保存路径:
之后,在train_model将访问路径为dataset/$object_name的数据集以用于模型训练;
在train_model中,设置训练模型的保存路径:
之后,在upload_model中,该模型将被上传至MinIO中。
在Jupyterlab本地环境中,跑通该Pipeline:
成功将模型上传至MinIO。
Elyra的不足
Elyra的运行环境问题
注意,刚才我们跑通时,选择的Runtime Platform为:
Run in-place locally
这样做有什么问题呢?这样的跑通其实不是真正的导通;因为它其实只是按你的Pipeline编排顺序将Pipeline中的所有ipynb文件依次跑通了一遍,只能证明其代码逻辑,环境变量等设置是对的;
然而,首先是ipynb节点的Runtime Image:
你即使选了Pytorch的Image,
放心,待会儿import torch肯定是会报错的,因为它的Runtime环境还是Jupyter lab。
是的,如果选择Run in-place locally,你的所有节点Runtime环境都是在这个Jupyter lab内的,不受Runtime Image影响;
同样的,还有一个问题:即使你的节点没有显式指定的Output Files,你的下一个节点仍能访问该文件;因为所有节点的Runtime环境都是在这个Jupyter lab内的,ipynb文件直接在自己运行环境的当前文件目录下找需要的文件就好了,根本不需要你显式指定。
所以,我们还是得将Runtime Platform改为Kubeflow Pipelines。
Elyra的文件存储、管理问题
若要使Elyra能在Kubeflow中跑通,还要进行如下设置:
分别配置Kubeflow Pipelines和Cloud Object Storage;
这就涉及到一个很有趣的问题:配置Kubeflow Pipelines是很正常的;但为什么要配置Cloud Object Storage?
因为:
Elyra 利用对象存储在管道处理期间持久化工件。
notebook(或py)处理完成后,对文件系统的所有更改(例如新文件或修改的文件)都将被丢弃。若要保留这些文件,必须在其节点属性中将这些文件声明为OutFiles,将这些文件存储在云存储中。
我们先配置完一下:
这边还有个有意思的点,就是Cloud Object Storage Endpoint也是有要求的:
我们在本地配置了一个MinIO,但它目前只能通过IP地址访问,即:
xx.xx.xxx.xx:xxxx/
这样配置是不能通过的,
它说,这个endpoint不是一个”uri”.
所以只好用我们的腾讯云MinIO来配置,至少这样endpoint是个”uri”:
cos.ap-shanghai.myqcloud.com
配置完后,我们就可以将Runtime Platform改为Kubeflow Pipelines了。
噩梦就来了:
这是我提交后的结果:
发现了两个大问题:
-
相比于原生的镜像构建Pipeline组件方法,这个方法明显要慢了很多;
-
网络问题
先说第一个问题:为什么会慢很多?
查看一下已成功的load-data的log:
可以看到,不仅是需要持久化存储的输出文件,组件运行时,部分必要文件同样要存放在MinIO中;
上图我用绿色所圈的部分,Pipeline进行了如下操作:
- 对MinIO发起Connect连接
- 发送Head请求
- 通过Get请求得到load_data.xxx.tar.gz
- 解压,得到load_data.ipynb
大量时间花在了网络通信上。
上图我用绿色所圈的部分,则通过PUT和POST请求将需要持久化存储的输出文件dataset/$obeject_name,慢慢提交至MinIO的bucket内。
登录腾讯云MinIO查看,同样能验证我们的想法:
除了显式定义的输出文件外,以load_data部分为例,该MinIo除了存储了load_data的ipynb文件外,还存储了html,及tar.gz。
再来看第二个问题:
这里为什么报错?注意我用绿色所圈的部分:
在Pipeline执行:
Command '['/opt/conda/bin/python3', '-m', 'pip', 'install', 'minio==7.1.2', 'nbclient==0.4.1', 'papermill==2.1.2', 'urllib3==1.26.5']'
最后发生了ReadTimeoutError错误。估计网络也不大行?我感觉可能是因为腾讯云MinIO在墙内,Kubeflow或Elyra服务在墙外的原因?
顺带一提第三个问题:
在选择镜像时,依然遇到了网络问题:因为镜像也是Pipeline提交后Run实时拉取的;因此经常出现在镜像拉取时就报错的问题。除了第一个镜像可以拉取下来,后面几个镜像都会报网络问题的错。