QuXiao's Blog

Life && Tech && Thoughts

私有化 AI 产品交付的一些思考与总结

Written on

最近一年,自己工作负责的主要一项就是私有化 AI 产品的交付和运营体系,真心感受到私有化交付的确是一项极其困难的事情,尤其是在多方的边界不清晰、需求变化快、有定制化需求等背景下。

从团队内部而言,当然是有不错的算法、工程能力,可以对外输出我们的服务,在一开始进行私有化交付的初期,私有化交付的全部含义貌似就是:把服务部署在客户的机器上面,就交付完成了。不过,后期你需要回答一系列的问题:

  • 现在部署的服务是什么版本的?
  • 怎么去验证服务部署的是否正确?
  • 当前服务的性能如何?
  • 当前服务的 API 文档在哪?
  • 出错了去哪看 log?
  • 有没有监控?
  • ...

按照这种交付程度,最后在交付中以及交付后的维护成本,会是十分巨大的,交付中设计到的所有人员(售前、研发、测试)都会牵扯精力在里面,如果同时要排查2、3个交付的问题,团队就没发继续干活儿了。因此,肯定是需要一个产品的交付运营体系,让人力有限的情况下,可以持续将服务交付到客户、并能进行产品的迭代。我打算从产品维度和交付维度分别谈一谈。

产品维度

其实,可能初期交付的并不是产品,只是满足客户需求的一系列服务,但最终肯定会要抽象成不同的产品。比如,审核类产品、人脸检索类产品等。然后再去按照产品的维度去进行需求的整理和功能的迭代,进而维护了一系列的产品版本。

核心产品

产品版本应该(至少)包含:

  • 该版本提供的服务,也就是 API 文档
  • 对应于上个版本的变更(功能的更新、性能的提升等)
  • 性能报告
  • 该版本对应的代码库版本

其中,性能报告是会对应到不同场景的,场景包括了:

  • 基础硬件(CPU 、内存)

  • GPU 硬件,例如: P4*2 / P4*4

  • 内部服务的实例数,例如:每张卡 xx 个推理实例,不同推理实例在卡上的配比

  • 请求并发数

  • 请求数据的集合
    • 不同大小、不同分别率的图片、视频
    • 不同标签的占比,例如集合为1000张图片,其中100张包含人脸

这些,其实就是某个版本产品的 Release Note。

周边管理服务

产品除了包含核心的服务以外,还需要为其配套一些运维管理服务,这样才能让产品在交付之后,能够做到可管理、可运维,同时也方便排查各类问题,理想情况是让客户方的技术人员在现场就可以自己进行一些简单的处理工作,不需要牵扯我们自己的精力。

目前已有的一些管理服务包括:

  • 日志采集 / 清理
  • 资源 / 业务监控
  • 授权服务:规定了外部部署的服务,只能在一些约定范围内运行,约定范围包括:GPU 硬件、网卡、磁盘、过期时间。这与私有化交付的环境、以及收费模式是相关的。
  • 服务状态管理:能够查看服务的当前版本、当前状态、控制服务的启停
  • Demo 系统:能让用户直接在界面上使用交付的 API,可用于展示和验证

工具链

除了核心服务和周边管理服务的开发以外,如何在开发以较低成本验证更新的功能,以及 如何在统一的环境去生成产品部署包 也是十分重要。 归根到底,就是内部是否有足够强大的工具链,以支持快速验证以及快速交付。

统一开发环境

绝大部分的 AI 服务,都是推理服务,因此所运行的环境就需要 GPU 的资源(一般都是 NVIDIA P4),而且除了少量包含 1080 Ti 的台式机可以作为临时开发环境以外,我们所使用的基本都是 P4*4 或者 P4*8 的硬件环境,这样就会导致大家在各自开发时会在共用一台机器,相互干扰。后来就像使用虚拟化技术,看能不能将硬件资源进行隔离,让大家“独立”使用 1~2 张 GPU 卡的开发环境。经过一些调研,最终用 KVM 实现了 GPU 资源的隔离,将几台 GPU 服务器做成许多套独立的开发环境,供大家使用。

内部部署工具

一开始迭代私有化产品的时候,想把开发的代码转换为测试环境的服务启起来,是需要一些门槛的,必须是很了解私有化产品部署服务细节的人才能操作的。这样很多情况就是开发人员完成了开发,需要另外的同事帮忙部署环境,这样后者就成为了瓶颈。

后来,开发了一套内部部署工具,将环境初始化、代码版本、服务选择、部署方式、服务部署、服务打包等涉及到的步骤,逐一转换为一些工具的自动化操作。这样,研发、测试、或者实施的同事,都可以使用该套工具在测试环境自助的部署服务,验证效果。随着内部部署工具的开发,还不定期的组织了一些内部培训,介绍工具的使用。

之前写的内部部署工具的一些文档

CI/CD

除了内部部署工具可以在本地进行部署验证之后,也需要一些 CI/CD 工作流,能将私有化产品在一个统一的环境中进行验证,用于作为最终出 Release 的依据。(虽然本地环境也能部署、也能验证,但是怎么证明你的环境、你的验证方法是符合标准的,缺乏一个大家都认可的机制)

目前内部使用 Jenkins 作为 CI/CD 的服务,虽然还无法做到完全自动化,但也是可以通过手动生成一系列的 Jenkins job,串联起整个开发 -> 测试(功能+性能) -> 生成部署包 -> 交付的流程。

jenkins pipeline 样例:

有了一整套内部环境之后,我们的 AI 私有化产品的产出,就逐渐归纳成一些标准化的步骤,例如:

  1. 准备阶段
    1. 确认产品的范围(内容审核、人脸...),以及该版本升级的功能、整体架构
    2. 确认产品对应构建、配置流程的完整性(jenkins job 以及私有化服务是否需要升级)
  2. 工程 Release 阶段
    1. 功能开发、使用内部部署工具进行本地测试
    2. jenkins job 功能测试,确认 test case 通过,记下 jenkins job URL
    3. jenkins job 性能测试,确认性能指标需要预期,记下 jenkins job URL
    4. 代码打 tag,确定版本
  3. 私有化部署包 Release 阶段
    1. 确认该版本产品的公共服务是否需要升级(例如,服务依赖的 DB、file server 等服务,是否要更改)
    2. 确认工程提供的功能/性能测试结果、以及工程代码版本,做最后的集成测试
    3. 私有化相关代码打 tag,确定版本
    4. 生成最终该版本产品的私有化离线部署包
    5. 使用私有化离线部署包,将服务部署至内部的 PreRelease 环境,使用持续的请求压力,进行观察
    6. 经过3~7天的观察、确认没有其他异常情况,表示该版本可以对外交付
    7. 将服务部署在内部的 Release 环境,用于内部或者外部可以的演示或测试。

交付维度

交付前

交付前,我们首先是要知道我们要交付的到底是什么,或者说交付的形式是什么,一般就这么几类:

  1. 之前没有接过的某种需求,需要按照项目的形式进行开发
  2. 部分需求是我们之前做过的,可以按照“产品 + 定制化开发”的形式进行
  3. 我们已有的某个产品的功能覆盖了客户的全部需求,可以直接交付产品

由上到下,团队需要耗费的精力应该是越来越少的。

对应需求一,是要持谨慎态度的,关键看后续这类的需求是否足够普遍,我们是否可以总结为某类产品,之后是否可以按照下面产品的形式走。(不过,肯定不会有凭空出来的产品,都是从项目中逐渐抽象出来的。) 对于需求二和三,可以统一为在之前已出 release 的产品的基础上,进行交付,其中可能涉及到二次定制化开发。

最终,在交付之前,我们总结下来需要确定的交付物有这么几个:

  • 离线交付部署包 * 镜像、模型、部署程序 * 产品版本号 * 代码版本(通用部分 + 定制化部分)
  • 离线测试包 * 测试图片、视频 * 测试程序
  • 客户现场信息 * 机器配置 * 机器列表 * 针对客户现场的各服务的配置,因为不同客户的硬件以及需求,对应的服务配置以及实例数是不尽相同的
  • 客户授权 licence * 按照机器硬件以及授权时间,限制客户对于服务的部署以及使用
  • 文档 * API 文档 * 运维管理文档(给去现场部署的同事、以及客户的技术人员作参考)

交付中

如果交付前的准备工作做得足够充足的话,交付中的工作是相对简单的:就是按照之前准备的运维管理文档,一步一步把服务部署至客户现场。

大致的流程如下:

  1. 确认并备份在客户现场上次部署的部署包

  2. 下载本次离线部署包(+测试包),确认版本

  3. 修改需要更新的部署机器列表,以及服务配置、实例数

  4. 环境预检,检查部署的环境,是否满足要求,例如:OS 版本、磁盘空间、某些库的版本等等

  5. 部署服务

  6. 验证服务,使用离线测试包确认服务可以跑过所有的 test case

  7. 验证周边管理服务,例如:资源/业务监控服务,log 采集清理等

  8. 服务安装或者验证失败的措施
    1. 实施在部署服务时,如果以上某一步骤操作未能成功,请将操作结果、服务日志进行保存,之后立即联系对应团队成员
    2. 如果团队在 30 分钟内未能定位到问题并提出现场解决方案的话,实施进行上一个版本的回滚
    3. 记录上线失败的 issue,和相关研发继续定位问题,修复问题之后,将新修复的版本交付给实施,下一次再在客户现场进行部署

交付后

服务交付完成之后,主要的工作是处理客户(集成商)在使用我们服务时遇到的各种问题,大部分情况都是针对 API、调用方式的一些问题,但也不乏一些奇葩问题。例如有次客户反映我们的服务调用返回 connection refused,需要我们立即解决,最后发现是客户现场不知道谁把那台机器关了……

虽然看起来都是小问题,但其实交付之后的“售后”工作,是极其耗费人力的,经常会出现因为处理一个问题,就把一个交付同事 + 一个研发同事搭进去一天的情况,更严重的还需要人直接去现场进行排查的。究其原因,我想有至少这么几个原因:

团队在规划时,只考虑到了前期研发成本,没有考虑或者低估了后期维护成本

团队虽然看起来规模不小了,但是分到各个产品之后,其实每个产品也就是2~3个全职研发 + 0.5~1 个测试 + 不知道 0.x 个交付同事了。除了交付服务,研发的大部分经理还是要花在产品的迭代演进上面的,如果每个2天就会被拉过去处理客户的问题,研发效率是可想而知的。可能有些问题,交付同事可以在前面挡一挡,但是如果是比较细节的技术或者业务逻辑的话,还是需要找研发同事的。

没有和集成商确定服务验证的条件

服务交付并不仅仅是“服务跑起来了,调用 API 没问题了”这么简单。之前所说的,我们准备的离线测试包,就是用来验证我们的服务的逻辑是否是符合预期的。但是在客户现场要做的,还需要更多一些。比如,人脸检索服务,在什么规模的人像库的时候,CPU/GPU 的检索性能的要求是什么,类似于这样的某种场景下的性能要求。经常就发生了,我们部署好服务之后,集成商只是调用下服务看是否成功就结束了,然后最终上线,客户开始使用起来了,结果发现了各种问题。

现场排查服务功能的不足与缺失

有些现场的问题,可能通过查看服务日志或者操作系统状态就可以初步判断问题的。虽然已有有了一些服务可以用于排查,但是易用的程度还无法达到预期,需要对整套服务架构有个大致的了解,才能利用这些服务进行排查,还做不到“傻瓜式”使用。而通常集成商对于我们交付的服务,只是把他当做一个黑盒来看,不了解其内部的架构。

comments powered by Disqus