概念

首先,大家先对齐下概念,避免因对概念理解不一致,而带来误解。

算法

大学中所学的数据结构课本上说:算法是指解决问题或执行任务的有序步骤集合。当然,如此描述的算法,相当准确。但是,对于实际工程落地而言,还是过于:抽象 +
空。这里面描述的算法,是指:算法同学所交付的可执行文件 or
so库,例如:视频压缩领域的:264算法,交付的产品:集成到ffmepg中的libx264。例如:如下命令,是使用:libx264算法真正去压缩一个视频的命令,那么:对于实际工程落地而言,所提供的带上libx264的ffmpeg可执行程序,可以简单地理解成:算法

1
ffmpeg -i source.mov -s 1280x720 -profile:v high444 -c:v libx264 -preset veryslow -crf 30 -r 30 -g 120 -keyint_min 30 -sc_threshold 40 -bf 3 -b_strategy 2 -refs 5 -c:a libfdk_aac -profile:a aac_low -b:a 128k -movflags faststart -max_muxing_queue_size 9999 -f mp4 -loglevel error 720_3.mp4 -y

模型

在计算机科学和机器学习领域,模型是对现实世界问题或系统的一种简化或抽象。如此描述模型,如同算法一样,相当准确,可惜:对于实际工程落地而言,还是:过于:抽象 +
空。因此,站在实际工程落地而言,所谓模型:其实是指:模型技术人员,已经训练好的,可以提供部署的文件zip包。

工程化

基于此,所谓算法、模型工程化,就是:将算法、模型技术人员,交付过来的东西(可执行文件 or so库 or zip包 or docker镜像等等),在cpu机器
or gpu机器部署起来,提供一个http接口,服务于业务。

需求提炼

在实际项目中,算法、模型要能标准化部署起来,并服务于实际业务,应该满足以下条件:

1、标准化部署
算法、模型,的入参、出参,千变万化,但是,期望在部署它们时,能够有一个标准化姿势,以减轻算法、模型的部署成本。

2、成本最低
部署在cpu or gpu上时,期望所花费的成本最低。
a、能够以最快的速度加载模型,避免成本的浪费
b、当流量低时,能够自动回收闲置的CPU、GPU资源
c、多个模型,流量切换时,能够自动释放资源,自动扩容资源

3、调参
算法、模型部署好后,期望能够快速知道它的处理效果如何,输入一批预先设定好的图片、视频 + 相关参数,能够快速得到结果,并且及时反馈:当前算法、模型是否ok

4、版本迭代
算法、模型版本迭代时,能够快速训练,出结果,验证通过后,能够快速发布

下载

对于图片、视频而言,业务方传送过来的一般是:各种各样的url地址,如何基于这些url地址,以快速且以最低的成本下载到本地磁盘?
1、分片下载?form表单下载?
2、公网域名如何转为内网域名下载?公网域名所带来的cdn成本很贵,而一般内网域名下载,不需要钱。
3、有些算法,并不需要下载整个视频 or 整个图片,下载一段视频即可触发cpu计算,获取结果,例如:计算视频的meta信息
4、有些url有鉴权参数 + 有限期限制,如何将过期的url,重新算成有效?
5、多云部署,单元化下载,能够节省非常多的成本

上传

经过算法、模型的计算后,一般会产生:新的图片s or 视频s,如何将结果,快速切最低成本上传到小文件存储系统里(例如:阿里云的oss)
1、分片上传?form表单上传?
2、多云自动切换?
3、如何自动产生不冲突的文件名称?
4、成本怎么管控?

服务镜像如何分发

有些算法、模型执行,依赖的服务镜像非常大,集群分发时,部署有点慢,如何预热加速?

问题分析

当业务方反馈问题,如何快速找到算法、模型的入参 + 出参?协助相关技术人员分析问题。

更新迭代

分析并解决完问题后,如何快速部署新的版本到线上,需设计一套更新迭代机制,快速服务于业务方?

多版本管理

线上部署的函数,最好有多版本,方便快速回滚到旧。

业界方案

faas:提出一个抽象的概念:function as a service,通过函数,对算法、模型,再次封装,并提供http服务,为业务方服务。
当然了,也可以提以下新的概念
1、aaas,算法 as a service
2、maas:模型 as a service,
3、FMaaS:基础模型即服务,

无论叫啥名字,它们的核心本质是一样的。

解决方案

整体系统架构图如下:

函数

通过函数的概念,对算法、模型,再次封装,支持的接入形态:shell、python、so库等

报文设计

算法、模型,只处理本地文件,并产出结果到本地文件中,因此,需要做以下2件事情:
1、需要为:算法、模型发起计算时创建好:本地执行的环境。
2、参数、结果灵活传递。
如下图所示:

业务报文设计

1、请求报文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
"create_time": 111112232,
"msg_id": "19dc3271b5054216a939eb8f029e748c",
"notify_url": "",
"priority": 1,
"input_files": [
{
"url": "",
"type": "",
"cloud": "",
"bucket": "",
"key": "",
"pre_download": true
}
],
"output_files": [
{
"cloud": "",
"bucket": "",
"key": ""
}
],
"flows": [
{
"fn_name": "",
"fn_version": "",
"fn_url": "",
"args": {
}
}
],
"extra": {
}
}

2、响应报文

1
2
3
4
5
6
{
"error_code": "",
"error_msg": "",
"msg_id": "",
"success": true
}

3、回调报文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"msg_id": "",
"code": "",
"message": "",
"success": true,
"output_files": [
{
"cmd": "",
"code": "",
"message": "",
"bucket": "",
"key": "",
"extra": {
"file_size": 1111,
"mime": "jpg or png"
}
}
]
}

函数交互报文

1、函数入参报文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"work_path": "/tmp/a59461fac43dc435a916d338c021177cb/{fn_name}",
"input_files": [
{
"key": "video",
"local_path": "./a.mp4",
"remote_url": "",
"pre_download": true
}
],
"arg_map": {
"aformat": "aac",
"vformat": "mp4"
}
}

备注:
a、如果pre_download为false,则local_path为空,remote_url有值,函数基于remote_url进行处理。
b、如果pre_download为true,则local_path一定不为空,宿主程序会帮忙下载图片,函数基于:local_path进行处理即可
c、函数里面的代码逻辑,可处理:pre_download,也可不处理,按照一定的规则即可。

2、函数出参报文

成功:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"error_code": 0,
"error_msg": "success",
"output_files": [
{
"key": "video",
"form": "LocalPath",
"body": "/tmp/a59461fac43dc435a916d338c021177cb/avsate_a.mp4"
},
{
"key": "audio",
"form": "LocalPath",
"body": "/tmp/a59461fac43dc435a916d338c021177cb/avsete_a.aac"
}
],
"arg_map": {
}
}

失败:

1
2
3
4
{
"error_code": 3002,
"error_msg": "Run ffmpeg errors! ffmpeg error info: xxx"
}

MQ代理消费

不建议处理进程直接对接MQ,特别是:kafka时,强烈建议:增加一层:MQ代理消费进程,具体如下图所示:

具体原因:
1、因为处理进程,都是cpu or gpu密集型业务,cpu跑100%是很正常的事情,如果此时,处理进程直怼kafka,容易导致:kafka认为消费进程卡死,触发partition一直reblance。
2、处理进程尽量轻量,若:集成了kafka的SDK,会变的有点重,还不如:使用标准的http协议。

并发度

触发器:可以同时消费1个 or 2个以上的消息,因此,需要实现精准度控制并发消费。举个例子:

功能 并发度 说明 备注
图片处理 2个以上 图片处理,一般不太费cpu,完全可以提高cpu利用率
视频拼接 2个以上 部分场景的视频拼接,仅仅只需要解封装,不怎么费cpu,但是,费:io
视频编解码 1个 非常费cpu,因此,一个pod只能处理一笔业务
视频编解码 1个 非常费cpu,因此,一个pod只能处理一笔业务

备注:
1、并发度的评判标准,可以是:cpu的利用率 + 业务场景(例如:是否是付费用户)等等具体的指标。

多进程通讯

如:工程化所述,交付到工程团队的形态,一般都是:二进制可执行文件 or so库 or
快速开始的代码块等,而整套体系要跑起来,都会涉及:多进程通讯,一般有以下三种进程:

进程 是否存在 负责团队 说明 备注
处理进程 工程团队 1、消费报文
,2、下载文件,创建函数执行的环境信息
3、调用函数
4、收集结果
5、清理环境信息
函数进程 工程团队 + 算法、模型团队一起 一般是:shell、python、go等代码包
算法进程 算法、模型团队 or 业界开源的算法、模型 如果算法同学提供的是:so库,则不一定存在算法进程

入参、出参

方式 作用 说明
管道 通过std in传入入参信息,再通过stdout,传出结果信息
文件 子进程将处理结果写入文件,再通过stdout透传文件地址信息,父进程根据文件地址读取结果信息

父子进程状态控制

状态 作用 说明
优雅关闭 当父进程收到重启等信息号,停止消费,等待:子进程执行完毕后,正常退出
主动强制退出 当父进程收到强制退出信息号, 直接kill -9,强制子进程,异常退出
被动退出 当子进程卡死后,父进程超时退出时,kill -9 触发子进程强制,异常退出

pipeline机制

1、站在业务的视角,函数的功能,一般都比较原子化,而复杂的业务功能,常常需要多个函数串联执行,例如:想要获取某张图片缩略后的宽高信息。
2、站在技术的视角,复杂的业务,是个dag流程,而不同节点执行,若在不同的公有云(或者物理机)上,常常涉及到:多次下载同一个文件,浪费:内网带宽
甚至是公网带宽。如何优化处理耗时 or 节省公网带宽成本?就需要尽量让:数据不动,让业务配置动起来。

基于2点,就需要让处理进程通过类似:pipelie的方式,执行某个业务。

算法镜像

尽量做到:每个函数所依赖的算法,是独立的,自包含的,不太建议:依赖于基础镜像,不然,后续维护成本比较高。