Serverless从入门到进阶:架构、原理与实践
上QQ阅读APP看书,第一时间看更新

4.2 Serverless和存储

本节主要介绍存储服务的基本概念、分类以及和FaaS联动的应用场景。存储服务是BaaS服务中重要的组成部分,FaaS和存储服务相结合,可以在Serverless架构下实现文件存储、缓存共享、文件上传等多个常用场景,进一步完善了Serverless架构所需的数据存储和数据共享能力。

4.2.1 基本概念

如图4-3所示,存储主要分为块存储、文件存储和对象存储三个类型。块存储需要把云盘挂载到主机上,在格式化安装文件系统后才能使用,支持高性能随机读写,但是共享困难,需要分区管理,主要用于数据库、OLTP(On-Line Transaction Processing,联机事务处理过程)等场景。文件存储可以挂载多个客户端环境,无须格式化,可共享存储,方便访问,主要用于文件共享,流媒体处理等场景。对象存储则将非结构化的数据(视频、镜像、软件等)当作完整的对象进行存取,无须挂载,通过HTTP协议可直接发起对象读写,支持大规模并发,但不支持随机写,主要用于视频、文件和应用的存储、备份归档等场景。下文将从和Serverless结合的角度出发,对对象存储和文件存储进行进步说明。

057-1

图4-3 存储产品横向对比

表4-1展示了不同存储类型的功能对比,其中块存储是高度结构化的,因为每个数据块都排列在结构化的固定块中,便于搜索和索引。而文件存储是通过分层的方式被索引和结构化。对象存储则是非结构化的,因为没有用于数据存储的格式或者结构,只是简单的对象列表。

表4-1 不同存储类型功能对比

057-2

4.2.2 对象存储

在云计算时代,对于数据高效存储、迁移的需求越来越大,以AWS S3为标准的对象存储服务因其平滑扩展、无缝接入的能力受到广泛的欢迎,成为业界规范。其他云厂商也相继推出了无目录层次结构、无数据格式的限制、支持HTTP/HTTPS访问的分布式存储服务——对象存储。综合业界较为主流的对象存储服务,可以总结出该服务具有如下特性。

  • 存储可靠:多副本冗余存储,可达12个9(即9999999999.99%)的数据持久性,保障数据耐久性。
  • 高可用:提供特定SLA(如99.95%)的高可用性,支持异地容灾、跨域复制等特性。
  • 开放兼容:支持SDK、API、命令行、GUI等工具,提供批量操作、迁移等能力,让使用和接入更为简单方便。
  • 数据安全:提供防盗链能力,支持多租户隔离、HTTPS加密传输及数据加密等功能。
  • 高并发:可支持上万QPS的请求,保障高并发下的业务稳定。
  • 多种规格:根据业务场景访问频度的高低,提供标准、低频和归档存储等多种类型。提供更具性价比的解决方案。例如低频存储适用于较低访问频率的场景,可用于网盘、大数据分析等场景;归档存储适用于档案、医疗影像、科学资料等适合长期保存,但需要较长解冻时间的数据。

除此之外,对象存储联动其他云上服务,可以支持更多场景,有很大的想象空间,这里简单介绍几个典型的应用场景供读者参考。

1. 数据处理

对于用户传入对象存储的数据,可结合多种数据处理类服务进行编辑、处理和审核,如图4-4所示。针对图片数据,用户可结合数据万象进行裁剪、缩放、转码、锐化、添加水印等操作,还可以进行鉴黄、鉴政、鉴暴恐等内容审核。针对视频数据,用户可进行转码、水印、截帧等处理。针对文档数据,用户可利用第三方服务生成文档的图片或HTML预览,并对预览图添加水印。

059-1

图4-4 数据处理

2. 内容分发

网站服务通常会在动态网页中根据一定的规则,区分开经常变动和长期不变的资源,静态资源是指长期不变的非结构化数据资源,如图4-5所示。对象存储提供了静态资源的存储和分发能力,减轻资源服务器的压力,并利用无限容量、高频读写的特性,为静态资源提供可扩展能力和可靠的存储。用户可以将网站中的静态内容(包括音视频、图片等文件)全部托管在标准存储中,并利用内容分发网络(CDN)分发。利用CDN全球加速节点的能力,可以将热点文件提前下发至边缘节点,降低访问延迟。

059-2

图4-5 内容分发

3. 大数据分析

无论用户存储的是医疗或财务方面的数据还是照片和音视频之类的多媒体文件,对象存储都可以作为数据源进行大数据分析,如图4-6所示。对象存储支持存储EB级别的非结构化数据,具备高可用、高可靠、高安全和可扩展性,结合大数据服务,可以快速构建和部署分析应用程序。在实现高性能计算需求后,可以将数据转换为归档存储,降低服务使用成本,以便长期存储数据。

060-1

图4-6 大数据分析

4.2.3 文件存储

随着业务的蓬勃发展,对于数据存储和管理的需求越来越明显。企业主要面临着存储容量不足、资源配置复杂、采购运营成本高、利用率低及不能满足大数据分析需求等几大挑战。而NAS(Network Attached Storage)存储以其灵活扩展、高性能、易用等优势,广泛应用在多个行业中。文件存储作为云上NAS存储的代表服务,和各个计算服务如云服务器、容器和FaaS函数等搭配使用,可以为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。当前NAS文件存储服务普遍支持下列功能和特性。

  • 支持多种协议:支持NFSv3.0/NFSv4.0、SMB协议,支持POSIX访问语义,兼容POSIX接口,可跨平台访问,保证文件数据的一致性。
  • 共享访问:多台主机或容器服务可以共享同一个文件系统,运行在不同可用区的计算节点也可以通过VPC网络使用同一个文件系统,实现多计算节点的协同工作及数据共享。
  • 弹性挂载/卸载:支持上万节点并发挂载,文件系统性能会随存储容量线性增长,用户可以灵活挂载或卸载文件系统。
  • 弹性扩容:单文件系统支持PB级存储,文件系统容量可随用户使用而弹性扩容,无须提前分配资源。
  • 安全控制:具有极高的可用性和持久性,支持细粒度权限控制,支持VPC网络及基础网络的网络隔离及来访用户白名单访问控制。
  • 传输加密:支持在挂载文件系统时启用传输层安全性(TLS),实现数据传输加密。
  • 数据备份及冗余:数据3份冗余,具备高可靠、高可用的特性;提供数据备份的能力,用户可以根据业务需求定期进行数据备份。

NAS文件系统结合其他服务,可以支持以下常用的应用场景。

1. 企业文件共享

企业员工办公时通常需要共享和访问相同的数据集,管理员可以通过对文件系统进行管理和设置,授获组织中的个人访问有关资源,还可以在文件系统中对特定的用户组统一设置权限,如图4-7所示。

061-1

图4-7 企业文件共享

2. 流媒体处理

文件存储支持CIFS/SMB协议,兼容Windows 7、Windows Server 2008/2012等多种操作系统,支持数万客户端并发访问,提供高吞吐量、毫秒级响应等特性,为吞吐量及延迟要求高的视频、图像渲染场景提供保障,如图4-8所示。

062-1

图4-8 流媒体处理

3. 大数据分析

文件存储支持数万客户端并发访问,具备超大容量、高吞吐、NFS协议的文件锁特性,为数据读写一致性需求强的大数据分析场景提供了保障,如图4-9所示。

062-2

图4-9 大数据分析

4. Web服务及内容管理

文件存储作为一种持久性强、吞吐量高的文件系统,可用于各种内容管理场景,例如为网站、在线发行、存档等各种应用服务提供数据源文件,如图4-10所示。

062-3

图4-10 Web服务及内容管理

4.2.4 存储和FaaS的联动

1. 对象存储

对象存储和云上多服务联动能够提供丰富的场景支持,云函数也是其中之一。对象存储支持以触发器的方式,通过JSON事件和函数进行交互,触发函数并支持用户自定义触发事件和处理逻辑。例如可以通过定义对象存储中的文件上传或删除等操作触发函数,并增加条件进行过滤。函数被触发后,也可以在代码中自定义处理逻辑。

在指定的COS Bucket发生对象创建或对象删除事件时,会将JSON格式的事件数据发送给绑定的FaaS函数,如代码清单4-2所示。

代码清单4-2 对象存储触发FaaS的事件消息结构

{
    "Records": [{
        "cos": {
            "cosSchemaVersion": "1.0",
            "cosObject": {
                "url": "http://testpic-1253970026.cos.ap-chengdu.myqcloud.com/testfile",
                "meta": {
                    "x-cos-request-id": "NWMxOWY4MGFfMjViMjU4NjRfMTUyMVxxxxxxxxx=",
                    "Content-Type": "",
                    "x-cos-meta-mykey": "myvalue"
                },
                "vid": "",
                "key": "/1253970026/testpic/testfile",
                "size": 1029
            },
            "cosBucket": {
                "region": "cd",
                "name": "testpic",
                "appid": "1253970026"
            },
            "cosNotificationId": "unkown"
        },
        "event": {
            "eventName": "cos:ObjectCreated:*",
            "eventVersion": "1.0",
            "eventTime": 1545205770,
            "eventSource": "qcs::cos",
            "requestParameters": {
                "requestSourceIP": "192.168.15.101",
                "requestHeaders": {
                    "Authorization": "q-sign-algorithm=sha1&q-ak=xxxxxxxxxxxxxx&q-sign-time=1545205709;1545215769&q-key-time=1545205709;1545215769&q-header-list=host;x-cos-storage-class&q-url-param-list=&q-signature=xxxxxxxxxxxxxxx"
                }
            },
            "eventQueue": "qcs:0:scf:cd:appid/1253970026:default.printevent. $LATEST",
            "reservedInfo": "",
            "reqid": 179398952
        }
    }]
} 

通过FaaS和对象存储产品进行联动,可以快速部署轻量级的业务处理逻辑,实现自动化处理,整个联动流程如图4-11所示。通过一键配置对象存储作为事件的监听器,并在FaaS函数中基于不同的编程语言和第三方库自定义处理逻辑,可以实现业务的自动化处理。通过FaaS平台的弹性伸缩能力,完美应对流量负载的波峰、波谷。

064-1

图4-11 FaaS函数和对象存储结合场景

图4-12所示是对象存储触发FaaS的一些典型场景,如音视频转码回传。

065-1

图4-12 FaaS函数和对象存储结合场景

某家用摄像头提供商基于Serverless架构,实现了摄像头视频回传→处理(拼接、转码)→存储方案。结合对象存储+云函数+ Serverless DB,首先将回传的视频存储到COS,然后自动触发函数做拼接处理,最后通知DB做数据写入。整个流程支持毫秒级弹性伸缩,即使遇到流量洪峰,也能完美承载。后来业务不断发展(C端售卖量上升),整套方案无须二次开发,几乎无容量上限,而Serverless按需付费的能力也极大地节约了机器资源和运维成本。

和用户自建方案进行对比,在开发流程方面,云函数FaaS更加简单高效,云函数自带能力较完善;在运维方面,云函数更加易用和省心,降低了运维成本;在费用方面,云函数相比自建服务可节省30%以上的费用。总体而言,使用Serverless云函数实现音视频转码服务的优势有下列几点。

  • FaaS函数提供了标准的运行环境,并保障资源的高可用和弹性伸缩,无须专人维护。
  • FaaS函数根据实际业务消耗收费,不存在资源浪费。
  • FaaS函数的开发调试流程更加高效,依赖和业务解耦,可以单独更新,支持实时热更新。
  • 运行环境隔离,单次请求失败不影响正常执行其他请求。

2. 文件存储

基于FaaS平台“无状态性”的运行原理,各云厂商FaaS的运行环境都有较为严格的限制。一般临时缓存空间为512MB,并且只能存到/tmp目录中。在这种情况下,随着函数实例的销毁(一般FaaS平台的实例回收时间为30分钟),这部分缓存也会被销毁。为了确保这些数据能够持久存储,一般会引入对象存储或数据库将数据落盘。但在这种解决方案中,对象存储主要用来存储静态资源。此外,文件存储的读写速度不够快,延迟较高。因此,在多函数之间的文件共享、大文件处理和缓存等场景中,需要引入CFS文件系统的挂载。当前各云厂商的解决方案是,支持在FaaS函数中配置对应的文件系统进行挂载,实现多函数对文件的共享存储和访问。

文件系统联动FaaS的常用场景有机器学习、音视频文件处理、数据共享、内容管理系统等。

(1)机器学习

机器学习场景需要大量的数据存储和较大的依赖库,因为FaaS平台的限制,所以这种场景难以在单个云函数中实现(由上文可知,平台对单个函数的限制一般为500 MB,而机器学习的依赖库常达到GB级别)。但引入了文件存储做BaaS服务后,可以通过文件存储承载较大的依赖库,例如TensorFlow等,从而让函数可以执行机器学习模型。当然,这种情况也需要考虑因过大的依赖包引入冷启动的问题,预置并发实例是当前比较好的一种解决方案。FaaS函数结合文件存储的架构如图4-13所示。

066-1

图4-13 FaaS函数和文件存储结合场景:大体积依赖或代码包

(2)音视频文件处理

在音视频文件的处理场景中,主要基于FFmpeg等通用开源库对视频文件进行转码等处理,通过Serverless实现该场景可以达到降低成本、弹性扩缩容的效果。通过将视频源文件存放在文件存储中,配合视频切片,可以对大文件进行处理。函数平台可以直接挂载文件存储,对其中的文件进行处理,如图4-14所示。

067-1

图4-14 FaaS函数和文件存储结合场景:音视频文件处理

(3)数据共享场景

由于文件存储支持多计算资源共享访问,对应FaaS平台,不同的函数可以以不同的权限访问不同的文件存储路径。FaaS结合文件存储能够支持一些涉及共享数据的场景,例如需要持久化的用户登录session信息的存储和共享,或者多个函数获取不同的测试数据,进行黑盒测试后,将结果写入回文件,文件存储则可以根据对应的结果进行模型调整和结果分析。

3. Serverless结合存储实现文件上传

文件上传和下载是网页端网站建设和开发中十分典型的场景,在图片识别、视频上传等业务场景中有广泛的应用。在不同的语言环境中,处理HTTP上传、下载的方法有很多,结合Serverless架构进行实现时,根据不同的场景和成本预估,能够选择不同的解决方案。由于各个云厂商对HTTP请求和响应的内容限制为6MB,因此对于大小不同的文件大小,也有不同的实现方式。

  • 文件小于6MB:结合API网关做Base64解码。
  • 文件大于6MB:由于FaaS函数本身无法对文件做持久化,函数实例生命周期结束后会被回收和销毁,因此需要结合存储服务实现文件上传能力。这种情况下又有以下三种实现方式。
    • 分片上传,将大文件切分成小块,完成上传后再拼接起来。
    • 借助COS对象存储功能,先将文件上传至COS,调用函数从COS下载文件,处理完之后进行回传。
    • 借助CFS文件存储功能,将大文件存放在CFS盘中,通过函数挂在CFS,可以像读写普通文件系统一样访问CFS盘中的文件。

接下来,分别介绍上述上传方案,详解其实现方式、适用场景和优缺点。

(1)文件小于6MB

在本地开发时,如果要实现文件上传功能,我们通常会用content-type:multipart/form-data类型作为请求头实现HTTP文件上传,或者将文件进行base64编码之后再上传。在文件不超过FaaS平台限制的情况下,这种思路依然可以通过网关结合函数实现,即将小文件通过网关传到FaaS函数平台,并由函数将结果在对象存储COS中做持久化。但这种实现方式也有诸多弊端。一方面,将文件直接从网关传给函数时,需要用户将文件转换为base64编码后再传输。函数通过网关收到数据后,再将base64编码的文件进行解码,之后进行持久化存储。这一过程依赖用户对代码进行额外改造。此外,由于FaaS平台的接入层,也就是API网关的数据包有6MB的限制,超过限制的文件会被裁剪或拦截。最后,通过API网关进行文件传输,需要较高的API网关和对象存储COS的流量费,因此,传输大文件时,不建议直接通过网关,可以结合函数及存储服务,对文件进行持久化处理。

(2)文件大于6MB

在文件大于6MB的情况下,需要结合FaaS函数和存储服务进行文件持久化。其中最直观的一种方式是直接传输文件到对象存储中。在这个方案里,客户端需要分别发起请求,获得临时上传的地址,将文件上传到COS,获取处理结果。这种方式在二进制上传、文件大小及成本控制方面都能提供更好的支持。以OCR文字识别为例,用户上传图片,调用OCR接口将图片转换为文字并展示。这种场景更适合直接将文件上传到COS对象存储中,如代码清单4-3所示。

代码清单4-3 结合COS直传文件

/** index.js
        * get cos temporary credential for uploading picture to cos
        * @param {string} uuid uuid
        */
    async getCosTmpCredential(uuid) {
    const { Response } = await this.capi.request(
        {
            Action: 'GetFederationToken',
            Version: '2018-08-13',
            Name: uuid,
            // 上传策略
            Policy: JSON.stringify({
                version: '2.0',
                statement: [
                    {
                        effect: 'allow',
                        action: [
                            'name/cos:PutObject',
                        ],
                        resource: [
'qcs::cos:${this.REGION}:uid/${this.TENCENT_APP_ID}:prefix//${this.TENCENT_APP_ID}/${this.BUCKET}/*',
                        ],
                    },
                ],
            }),
            DurationSeconds: 7200,
        },
        {
            host: 'sts.tencentcloudapi.com',
        },
    );
        Response.StartTime = Math.round(Date.now() / 1000);
        Response.Region = this.REGION;
        Response.BucketName = '${this.BUCKET}-${this.TENCENT_APP_ID}';
        return Response;
    }

    /**
        *  获取OCR结果
        * @param {string} imgUrl image url
        * @param {string} lang language, default zh
        */
    async getOCRResult(imgUrl, lang = 'zh') {
        const { Response } = await this.capi.request(
            {
                Action: 'GeneralBasicOCR',
                Version: '2018-11-19',
                ImageUrl: imgUrl,
                LanguageType: lang,
            },
            {
                host: 'ocr.tencentcloudapi.com',
            },
        );
        return Response;
    }
}
module.exports = CloudApi;

/*sls.js*/
app.post('/token', async (req, res, next) => {
    const uuid = req.body.uuid;
    const result = await apis.getCosTmpCredential(uuid);
    if (result.Error) {
        res.send({
            code: 1,
            error: result.Error,
        });
    } else {
        res.send({
            code: 0,
            data: result,
        });
    }
});

app.post('/ocr', async (req, res, next) => {
    const imgUrl = req.body.imgUrl;
    const result = await apis.getOCRResult(imgUrl);
    if (result.Error) {
        res.send({
            code: 1,
            error: result.Error,
        });
    } else {
        res.send({
            code: 0,
            data: result.TextDetections,
        });
    }
});

可以看到,通过/PutBucket方法将图片直接上传到COS桶中,之后通过getOCRResult获取imgUrl,即文件地址,将其作为输入进行OCR识别,即可解决该场景下大文件上传的问题,并且传输速度更加稳定、成本也更低廉。

使用对象存储做文件上传,需要额外注意下面两点。

  • Web服务可能存在跨域问题,因此需要对COS桶进行跨域设置,即开启CORS的支持。
  • 因为客户端直接将文件上传到对象存储中可能有较大的安全风险,所以可以考虑在服务端做签名,通过客户端获取签名结果,上传文件到对象存储的指定位置。

该项目的完整代码地址为https://github.com/serverless-components/tencent-ocr。

另外一种方式是借助CFS文件存储功能,对文件进行持久化,这种方式支持多个函数共享访问。在这种实现方式中,COS通过HTTP进行上传、下载,而CFS则需要FaaS函数进行内网(VPC私有网络)配置,并挂载对应的文件盘,之后才能将文件写入对应的路径,如代码清单4-4所示。

代码清单4-4 结合CFS文件存储实现文件上传

/* index.js */
'use strict';
var fs = requiret('fs');
exports.main_handler = async (event, context) => {
    await fs.promises.writeFile('/mnt/myfolder/filel.txt', JSON.stringify(event)); 
    return event;
};

由上述方案可知,当前在Serverless架构下,对于大文件的上传依然需要做代码改造或适配,才是较为理想且安全的方案。因此,Serverless架构下的持久化和文件上传特性也需要持续优化,相信未来会出现更多适配Serverless架构的存储方案。