货拉拉一站式云原生AI平台建设实践

3,218

背景

数据工程、模型训练、在线服务是机器学习技术实现的三驾马车,这个流程中处理的数据量大,计算量大、训练框架和版本多样、底层依赖复杂,资源算力管理,需要人工介入解决很多问题,给AI落地带来了比较困难。针对这一系列的问题,货拉拉大数据智能平台组从2020年开始就在探索提供一套完整的云原生的一站式AI解决方案,并从那时起就自建了Kubernetes 集群,后续提供了GPU按量训练能力、划分独立资源池纳管了其它部门机器,围绕着K8S平台完善了监控告警、容器日志收集能力,打通了大数据底层存储,引入OBS、S3对象存储,保证大数据AI化业务能稳定运行于K8S云原生底座上。

从系统层面来看,大数据智能平台组在云原生资源管理平台上构建了Flink on k8s的特征平台、 notebook on K8S模型训练、数据查询及可视化平台、基于 K8S的在线推理平台。 自建云原生K8S系统还支撑着智能定价、ABTest等系统运行计算任务,利用云原生特性赋能业务系统同时充分利用了集群资源。 Docker镜像解决了大数据AI组建对系统环境的依赖,K8S统一对底层资源管理,方便的构建多租户实例的Notebook、自动扩缩容能够为模型在推理提供自动化伸缩的能力,支持离线批任务、支持定时调度以及在线服务,多种多样的任务类型使用了K8S Deployment、Cronjob、job、DaemonSets任务类型。

整体框架

UML 图 (2).jpg

上图是货拉拉云原生AI系统的整体框架,从数据接入、特征工程、数据分析、数据可视化、notebook在线开发、模型训练、以及模型在线部署,围绕着AI整条链路孵化了专注于于数据ETL处理的特征平台、支持多种计算引擎的notebook在线开发及训练平台、支持模型注册管理及发布于一体的模型在线推理平台,其中的特征平台底层的计算引擎是基于Flink on K8S去跑特征任务,大数据存储计算都可以添加到镜像中可避免宿主机中安装大量依赖包。

特征平台

特征平台主要是给数据科学家、数据工程师、机器学习工程师去使用,能够解决数据存储分散、特征重复、提取复杂、链路过长、使用困难等问题。主要功能是把数据从HBase、Hive 、关系型数据库等各类大数据ODS(Operational Data store )层进行快速的数据 ETL ,将数据抽取到ES、Redis、HBase、Hive,元数据在特征平台进行管理,并统一了数据出口,用来做算法模型的数据测试、训练、推理及其它数据应用。

链路的流批一体

流批一体.png

K8S任务流程

任务流程.png

目前特征平台核心是采用Flink去实现数据的ETL,底层的是基于Kubernetes集群去调度Flink ETL作业。 Flink任务逻辑主要借助pyflink去实现,Pyflink里面包含任务的计算逻辑,把任务从source经过数据处理sink到对应的存储,自定义实现了redis connector、优化ElasticSearch connector。 依据K8S提供的Cronjob对象创建了定时特征任务,可以定时的去做周期性的FLink任务,上层开放API提供外部驱动的特征任务,集群部署EFK在平台层进行日志的查看。

交互式建模

Notebook是大数据智能平台组基于Apache zeppelin进行定制开发,为数据分析师、AI算法开发者量身打造的交互式开发工具,支持多种计算引擎如:Spark、Python、JDBC、Markdown、Shell terminal、Beeline、Hive sql、Tensorflow供用户选择,用户可以使用Notebook完成数据准备、数据预处理、数据分析可视化,算法调试与模型训练。 社区的Zeppelin主要是单机多进程方式运行,K8S部署不完善,不满足可扩展性、隔离性。同时机器学习深度学习对底层环境的依赖,部署到Kubernetes上以镜像容器的方式启动,也就是水到渠成的事情,可以解决Zeppelin的扩展性、安全性、隔离性问题。 部署到K8S 需要解决如下问题: 对接K8S管理Zeppelin服务的生命周期,包括Zeppelin服务的启动、Zeppelin停止、状态更新、资源回收。 每个用户创建独立Zeppelin Server解决多租房问题,并暴露出统一的服务访问入口。 解决了Zeppelin Server停止后,用户创建的Note数据能够持久化不随着服务的停止而删除。 为了满足用户对周期性外部驱动分析训练的需求,需要设计一层计算驱动任务给notebook插上定时调度和外部驱动的翅膀。

如下所示,notebook实现的架构图

nodebook架构图.png

架构主要分为三部分:

  1. Zeppelin服务的启动,停止和状态获取,这块主要包含调用k8s Api去创建Namespace、ConfigMap、Service、RBAC、PV、PVC以及Zeppelin Server Deployment等Zeppelin启动相关的k8s对象,这里我们做了相关个性化开发,首先是通过调用JAVA K8S API去创建Zeppelin K8S对象,其次就是通过挂载NFS或S3解决 notebook 无法持久化存储问题、接着就是另外新增init container容器去实现多个用户demo note的拷贝操作,其中还会涉及以configmap和环境变量 实现相关动态参数的传递。
  2. Zeppelin 服务的访问,这块主要设计了为每个用户创建独立的namespace,并启动一个独立zeppelin server 去做多租户, 每个用户暴露不同的访问url,然后部署一个nginx 并创建暴露NodePort 类型nginx Service去代理不同用户的访问链接 ,通过nginx解析url location获取不同用户namespace及具体的location 拼装成最终不同用户反向代理的DNS地址。
  3. 计算框架支持,为了更好的落实降本增效,充分利用大数据原有的yarn计算集群,通过部署livy服务,zeppelin notebook中对接livy服务,使得spark任务能够通过livy提交到大数据yarn计算机群中。另外一个就是通过继承jdbc 实现Hive SQL、OLAP引擎的支持。

打通计算和存储

Zeppelin 支持许多大数据计算引擎,需要解决依赖包安装、环境变量配置、解释器配置等工作,作为notebook平台这些功能在Zeppelin Server启动后就可以直接使用,不需要用户在配置这类繁琐的工作,我们在设计开发的时候,打通底层spark 访问存储HDFS、新建了Hive SQL解释器,镜像中内置大数据客户端、配置DNS域名解析、解决了权限认证、包依赖等问题,用户最终可以直接访问生产Hive 、HDFS。

资源隔离及回收

资源回收.png

k8s集群CPU、GPU、内存是有限制的,纳管其它部门的机器资源想单独隔离使用,如果没有调度和隔离策略的话,当申请的资源过多,影响到其他任务的调度,同时也会跑到其它私有资源,更进一步的如果k8s分配出去的资源无法回收,总有一天会有资源用尽导致其他任务无法调度的情况。 这里我们对每个namespace 增加了Resource Quota限制了每个用户最大的资源量,资源隔离统一对K8S集群的计算节点打上不同的标记,规定了一些节点是notebook 公用的节点,一些节点是其它人独有的资源,上层在创建任务的时候可以指定资源组,默认创建的Zeppelin notebook会调度到公用资源组节点上去,如果有些notebook比较重要就会指定到另外的资源组中,针对资源组增加了审批。

资源回收主要是前端选择空闲停止时间,后端服务进程获取配置参数,在Zeppelin server 与解释器交互请求的地方启动线程,不停更新交互时间,直到判断空闲超过配置时间,触发进程退出操作。

模型训练gpu管理

异构集群中如何对机器进行管理是一个比较复杂的问题,存在不同的云厂商以及自有机器混用的情况下,解决的方案就是分为不同的的集群,通过多集群进行管理,还有就是不同地点位置甚至网络环境混用,通过一层调度层进行管理,这里主要的架构如下:

GPU架构图.png

K8S 集群主要构建在华为云,在GPU场景下会单独抽离一层GPU调度层,不同地区或者不同规格的GPU划分到不同的资源组,数据库中对这些GPU机器进行类型区分。 比如针对GPU按量付费场景,在启动GPU任务时候,会选择不同的资源组,从资源组中选定特定的GPU节点,会为GPU和notebook建立绑定关系,通过调用运维提供的GPU实例开机接口启动GPU节点,在任务空闲回收或者手动停止实例的时候调用运维停机接口,对GPU机器进行关机处理。

数据持久化和多计算框架数据共享

没有数据持久化,容器中用户创建zeppelin notebook重启后notebook文件数据就会消失 ,不同计算任务运行在不同的的K8S pod之间如何共享文件?

S3持久化.png 这里为每个启动的Zeppelin Server及其创建的计算pod挂载统一的分布式存储,通过subpath参数为每个用户创建不同的子目录解决用户数据隔离的问题。不同用户挂载到容器中就是同一个网络存储不同的子目录。 这里面的实现一个是利用K8S DaemonSet 这样每个k8S的计算节点中会运行一个pod用来,POD中采用s3fs对对象存储进行挂载,该Pod会把宿主机中的目录挂载到容器中,挂载程序把对象存储挂到当前目录,这样宿主机就有了对应对象存储的文件目录结构,这样zeppelin运行启动的计算pod都去挂载宿主机的这个目录,就可以达到多POD文件共享及数据持久化的功效。

Notebook任务驱动

为了满足用户对周期性外部驱动分析训练的需求,同步了notebook脚本到notebook管理平台,可以对单独的notebook脚本 配置一个计算驱动任务,定时任务可以在k8s中绑定一个cronjob到了预定时间去启动任务调用zeppelin server的接口调度运行对应的notebook脚本,外部驱动任务通过管理平台暴露任务创建API,可以供其它第三方平台通过api调用去启动notebook服务,这样就给notebook插上了定时调度和外部驱动的翅膀。

模型在线推理

AI整个流程中,模型在线推理是AI应用的最后一环,模型比较小的情况下可以直接嵌入到后端业务代码中,作为一个常见的接口对外提供服务,但是随着模型越来越大、模型更新越来越频繁,有必要对模型服务进行独立拆分,独立的对模型进行更新和发布,模型训练框架众多、模型格式多样以及在线服务对服务稳定性要求高、对服务延迟比较敏感,但总的来说大部分模型的模型管理、模型部署、服务的更新具有通用性,具体的不同在于可以借鉴底层镜像进行区别的处理。因此有一个通用的模型部署管理平台能够避免业务在应用模型推理时所带来的重复性建设,大大加快模型服务的落地。 为公司AI应用能够大量落地,大数据智能平台组开发了统一的模型在线服务平台。一线互联网企业主要是基于云原生、服务网格去打造统一的模型在线推理平台,因此智能平台组也同样是基于云原生K8S去实现基础设施到服务监控告警链路的整个流程的建设。

整体框架

流程图 (1).jpg

模型管理

模型管理主要是针对外部训练模型提供模型上传、模型注册、模型版本管理等对模型进行增删改的管理功能,针对线下大模型模型注册线下提供一个GO开发的二进制工具,让用户可以按照工具提示上传模型到工具中封装的OSS统一目录中,后续用户可以复制工具生成的模型路径注册模型到模型管理平台,平台对模型格式进行校验,避免无效的模型注册。

模型服务

模型服务主要是把静态的模型部署成在线服务,用户在模型服务平台中可以选择模型仓库对应的模型版本、设定模型运行的实例个数,选择是否开启HPA自动扩缩容。 模型部署具体实现诗通过构建K8S deployment对象模版,其中的Pod 包含三个容器解耦了三块功能,这三个容器分别是初始化容器去实现oss模型文件挂载、接入服务用来埋点指标收集 java 网络代理、TfServing推理框架的封装,实现模型的拷贝、服务监控指标的收集、推理加载在线服务。 通过平台部署的每个模型服务,在K8S中会创建一个类型为LB的Service,服务部署平台会获取LB的地址拼接成服务url暴露给服务使用方。 对于部署的服务,通过个性化网络代理埋点服务状态、服务请求qps、服务耗时的指标信息,共同汇聚到公司统一的监控系统,监控系统可以通过服务ID等信息查询各个模型服务的运行状况。

总结

以上简单的介绍了K8S在货拉拉大数据领域的应用与实践,对于一个AI平台来说云原生容器化应该是业界标配,尤其是在面对机器学习、深度学习、分布式训练、GPU算力管理调配这种复杂的场景。大数据智能平台项目组前期通过自建华为云环境K8S集群,到针对线上服务的阿里云场景,采用阿里云ACK K8S集群,双集群跨云使用。

云原生AI机器学习系统的设计到实现涉及大数据、云原生、AI三个交叉领域的知识和技能体系,构建一个一站式的完善的机器学习平台需要算法、工程、基础架构的合力,目前货拉拉大数据云原生机器学习平台只是覆盖AI的机器学习的链路,一些基础能力还不支持,比如说分布式机器学习、GPU分布式训练、GPU分布式推理,相比于投入资源构建系统,赋能业务才是最重要的,打造强大的系统,最终赋能业务可以说任重而道远。

笔者介绍:张浩|资深大数据工程师,曾任职腾讯TEG数据平台部,从事大数据AI平台开发,现就职于货拉拉智能平台组从事AI平台开发