Serverless 的资源评估与成本探索 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
scf10cent
V2EX    推广

Serverless 的资源评估与成本探索

  •  
  •   scf10cent 2020-02-13 16:20:12 +08:00 2488 次点击
    这是一个创建于 2067 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Serverless 布道师在讲解 Serverless 架构和云主机等区别的时候,总会有类似的描述:

    传统业务开发完成想要上线,需要评估资源使用。根据评估结果,购买云主机,并且需要根据业务的发展不断对主机等资源进行升级维。而 Serverless 架构,则不需要这样复杂的流程,只需要将函数部署到线上,一切后端服务交给运营商来处理。即使是瞬时高并发,也有云厂商为您自动扩缩。

    但是在实际生产中,Serverless 真的无需评估资源么?还是说在 Serverless 架构下,资源评估的内容、对象发生了变化,或者简化呢?

    腾讯云云函数 中,我们创建一个云函数之后,有这么几个设置项:

    设置项

    内存设置范围为 64~1536M,超时时间范围为 1~900s。这些设置项其实已经涉及到了资源评估。

    超时时间

    先说超时时间,一个项目或者一个函数,一个 Action 都是有执行时间的。如果超过某个时间没执行完,就可以评估其为发生了「意外」,可以被「干掉」了,这个就是超时时间。

    例如一个获取用户信息的简单请求,假设 10s 内没有返回,证明已经不满足业务需求,此时就可以将超时设置为 10s。如果有另一个业务,运行速度比较慢,至少要 50s 才能执行完,那么这个值的设置就要大于 50,否则程序可能因为超时被强行停止。

    内存

    内存是一个有趣的东西,可能衍生两个关联点。

    关联点 1:

    程序本身需要一定的内存,这个内存要大于程序本身的内存。

    以 Python 语言为例:

    # encoding=utf-8 import jieba def main_handler(event, context): jieba.load_userdict("./jieba/dict.txt") seg_list = jieba.cut("我来到北京清华大学", cut_all=True) print("Full Mode: " + "/ ".join(seg_list)) # 全模式 seg_list = jieba.cut("我来到北京清华大学", cut_all=False) print("Default Mode: " + "/ ".join(seg_list)) # 精确模式 seg_list = jieba.cut("他来到了网易杭研大厦") # 默认是精确模式 print(", ".join(seg_list)) seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后在日本京都大学深造") # 搜索引擎模式 print(", ".join(seg_list)) 

    注:为了让结果更直观,差距更加大,这里每次都重新导入了自带了 dict,该操作本身就是相对浪费时间和内存的。在实际使用中 jieba 自带缓存,并且无需手动导入本身的 dict。

    当导入一个自定义的 dict 到 jieba 中,如果此时用默认设置:128M 内存限制 + 3s 超时限制,就会这样:

    结果

    可以看到,在导入自定义 dict 的时候,内存消耗过大, 默认值 128M 不能满足需求,此时将其修改成最大:

    修改为最大值

    系统提醒时间超时,因此还需要再修改超时时间为适当的数值(此处设定为 10s ):

    时间设置

    因此,在关注程序本身的前提下,内存需要设置到一个合理范围内。这个范围是 >= 程序本身需要的内存数值。

    关联点 2:计费相关。

    在云函数的 计费文档 中,有如下描述:

    云函数 SCF 按照实际使用付费,采用后付费小时结,以为单位进行结算。 SCF 账单由以下三部分组成,每部分根据自身统计结果和计算方式进行费用计算,结果以为单位,并保留小数点后两位。

    • 资源使用费用
    • 调用次数费用
    • 外网出流量费用

    调用次数和出网流量这部分,都是程序或者使用相关了,而资源使用费用则有一些注意点:

    资源使用量 = 函数配置内存 × 运行时长 用户资源使用量,由函数配置内存,乘以函数运行时的计费时长得出。其中配置内存转换为 GB 单位,计费时长由毫秒 (ms) 转换为秒 (s) 单位,因此,资源使用量的计算单位为GBs( GB-秒)。 例如,配置为 256MB 的函数,单次运行了 1760 ms,计费时长为 1760 ms,则单次运行的资源使用量为( 256/1024 )×( 1760/1000 ) = 0.44 GBs。 针对函数的每次运行,均会计算资源使用量,并按小时汇总求和,作为该小时的资源使用量。

    这里有一个非常重要的公式,那就是函数配置内存运行时长。

    函数配置内存就是刚才所讲:我们为程序选择的内存大小。运行时长,就是我们运行程序之后得到的结果:

    计费时间

    以该程序为例,用的是 1536MB,则使用量为 (1536/1024) * (3200/1000) = 4.8 GBs

    当然,250MB 的情况下,程序也可以运行:

    250M

    此时的资源使用量为 (256/1024) * (3400/1000) = 0.85GBs

    相对比上一次,程序执行时间增加了 0.2s ,但是资源使用量降低了将近 6 倍!

    尽管 GBs 的单价很低,但是当业务量上来之后,也不能忽略。刚才的只是一个单次请求,如果每天有 1000 此次请求,那:

    • 1536 MB:4.810000.00011108 = 0.5 元
    • 25 MB:0.8510000.00011108 = 0.09442 元

    仅计算资源使用量费用,而不计算调用次数 /外网流量)

    如果是一万次调用,那就是 50 元和 9 元的区别。随着流量越大,差距越大。

    当然很多时候函数执行时间不会这么久,以个人的某函数为例:

    一个例子

    计费时间均是 100ms,每日调用量在 6000 次左右:

    详细数据

    按照 64M 内存来看,单资源费用只要 76 元一年,如果内存都设置称为 1536,则一年要 1824 元!这个费用相当于:

    等效费用

    所以说,「超时时间」的设置需要评估代码和业务场景,它关系到程序运行的稳定性和功能完整性。同时,「内存」也不仅仅影响程序的使用层面,还关乎费用成本。那么,既然资源评估如此重要,如何评估呢?

    还是以上述代码为例,在本地进行简单的脚本编写:

    from tencentcloud.common import credential from tencentcloud.common.profile.client_profile import ClientProfile from tencentcloud.common.profile.http_profile import HttpProfile from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException from tencentcloud.scf.v20180416 import scf_client, models import json import numpy import matplotlib.pyplot as plt try: cred = credential.Credential("", "") httpProfile = HttpProfile() httpProfile.endpoint = "scf.tencentcloudapi.com" clientProfile = ClientProfile() clientProfile.httpProfile = httpProfile client = scf_client.ScfClient(cred, "ap-shanghai", clientProfile) req = models.InvokeRequest() params = '{"FunctionName":"hello_world_2"}' req.from_json_string(params) billTimeList = [] timeList = [] for i in range(1,50): print("times: ", i) resp = json.loads(client.Invoke(req).to_json_string()) billTimeList.append(resp['Result']['BillDuration']) timeList.append(resp['Result']['Duration']) print("计费最大时间", int(max(billTimeList))) print("计费最小时间", int(min(billTimeList))) print("计费平均时间", int(numpy.mean(billTimeList))) print("运行最大时间", int(max(timeList))) print("运行最小时间", int(min(timeList))) print("运行平均时间", int(numpy.mean(timeList))) plt.figure() plt.subplot(4, 1, 1) x_data = range(0, len(billTimeList)) plt.plot(x_data, billTimeList) plt.subplot(4, 1, 2) plt.hist(billTimeList, bins=20) plt.subplot(4, 1, 3) x_data = range(0, len(timeList)) plt.plot(x_data, timeList) plt.subplot(4, 1, 4) plt.hist(timeList, bins=20) plt.show() except TencentCloudSDKException as err: print(err) 

    执行代码会得到这么一张图:

    资源评估图

    从上到下分别是不同次数计费时间图、计费时间分布图、不同次数运行时间图和运行时间分布图。通过对 256M 起步,1536M 终止,步长 128M,每个内存大小串行靠用 50 次,统计表:

    统计表

    注:为了让统计结果更加清晰,差异性更大,在程序代码中进行了部分无用操作用来增加程序执行时间。正常使用 jieba 基本都是毫秒级的。

    测试结果

    通过表统计可以看到,在满足程序内存消耗的前提下,内存大小对程序执行时间的影响并不是很大,反而对计费影响很大。

    函数并发量

    除了超时时间和运行内存,用户还需要评估一个参数:函数并发量。在项目上线之后,需要对项目的并发量进行评估。当并发量超过默认值,要及时联系售后同学或者提交工单进行最大并发量的提升。

    小结

    综上,Serverless 架构也是需要资源评估的,而且资源评估同样和成本是直接挂钩。只不过这个资源评估的对象逐渐发生了变化,相对之前的评估维度、难度而言,都是大幅度缩小或者降低的。

    传送门:

    欢迎访问:Serverless 中文网,您可以在 最佳实践 里体验更多关于 Serverless 应用的开发!

    15 条回复    2020-04-13 16:10:02 +08:00
    yyfearth
        1
    yyfearth  
       2020-02-13 16:49:19 +08:00
    我觉得这里内存设置不应该直接用来计费 应该和时间一样 要看用量来计费比较合适
    可以给一个最小内存和最大内存的设置会更加合理一些 实际使用内存不小于最小内存 不能大于最大内存

    因为如果是用容器或者轻量 VM 实现 内存应该都是共享的 那么设置一个初始内存 加上一个最大内存限制 应该可以实现
    akira
        2
    akira  
       2020-02-13 17:35:41 +08:00
    综上,Serverless 架构也是需要资源评估的,而且资源评估同样和成本是直接挂钩。只不过这个资源评估的对象逐渐发生了变化,相对之前的评估维度、难度而言,都是大幅度缩小或者降低的。
    ---------------------
    我怎么觉得是反过来,评估难度更大了呢
    whileFalse
        3
    whileFalse  
       2020-02-13 17:53:20 +08:00
    你好。我刚才浏览了一下 serverless.com ,没有找到答案,故来询问。

    我对 AWS Lambda 和 CloudFormation 十分熟悉。我也具有一定的后端 web 开发经验。
    但是我对于传统 Web 开发工程师如何在 Serverless 环境下(并非特指 serverless 框架)进行高效业务开发没有一个明确的方向。我稍微浏览了一下 serverless 框架的简单 example,发现框架本身似乎没有提供类似 flask 或 express 那样强大的功能。那么 serverless 对于我这样的人来说有什么益处呢?(我不需要多平台部署)
    rayhy
        4
    rayhy  
       2020-02-13 18:05:20 +08:00 via Android
    @whileFalse flask 也不是不能用。比如 tencent-flask 这个官方组件。只是传统的框架移植上去总有一些很麻烦的地方,用的不是那么舒服
    whileFalse
        5
    whileFalse  
       2020-02-13 18:28:22 +08:00
    @rayhy #4 嗯,但我希望用相对比较原生的方式。我特别讨厌整个业务都绑定到同一个 fuction 中的这种半半落落的方案。
    gwy15
        6
    gwy15  
       2020-02-13 19:07:51 +08:00
    @whileFalse 我使用的是阿里云 FC,它的 python3 环境是基于 WSGI 接口的,因此可以直接这样用 flask 框架:
    handler = app

    但是对非 ascii 字符有 bug,可以这样进行 workaround:
    https://www.gwy15.com/2019/12/17/%E9%98%BF%E9%87%8C%E4%BA%91%E5%87%BD%E6%95%B0%E8%AE%A1%E7%AE%97%E7%9A%84-bug-%E4%B8%8E-workaround/

    对我来说,面向 serverless (阿里云 FC )环境开发和面向 VPS 环境开发没什么区别,无非就是加个 __import__('sys').path.append('lib') 然后导出第三方库到 lib 文件夹、打包上传。不过目前阿里云 FC 不支持限速、不支持自定义 header (默认全部 host 都做了 CORS,没法白名单黑名单)、不支持方便的自定义域名,还有待改善。
    whileFalse
        7
    whileFalse  
       2020-02-13 22:59:56 +08:00
    @gwy15 #6 用 Flask 冷启动很慢吧。
    scf10cent
        8
    scf10cent  
    OP
       2020-02-14 11:40:29 +08:00
    @yyfearth 这里确实可以这样考虑。但实际上 SCF 目前是有热启动的。如果函数使用频繁,那么很可能就会有很多容器被启动起来。那么这个时候内存设置的多少可能就涉及到多少台上层机器待命。例如说宿主机是 4G 内存,用户设置的是 1G 的配置和设置 128M 的配置,我们在这个宿主机上,同时启动的容器数量应该是不同的,如果因为共享原因,启动相同数量的机器,很可能在并发上来的时候就挂掉了。所以,这个时候让用户配置更合理的内存,是合理的,毕竟直接涉及到占用的资源多少。
    scf10cent
        9
    scf10cent  
    OP
       2020-02-14 11:43:42 +08:00
    @akira 相对来说,我觉得评估难度量是降低了,虽然说,可能每个接口都要评估工作量,但是实际上并不用这样的,例如我近期开发了一个博客系统,前端有 10 个接口,涉及到了 10 个函数,每个函数都是增删改查,没有太复杂的逻辑,所以每个函数我都设置了 64M 的内存。这样就足够了。
    scf10cent
        10
    scf10cent  
    OP
       2020-02-14 11:57:03 +08:00
    @whileFalse 是这样的,Serverless 架构可以认为还是“新的东西”,既然是新的东西那么就会有一定的发展坎坷。首先这对你说的,Serverless 开发貌似没有 Flask 这些框架好用。是的,可能在刚接触的过程中,确实 Flask 这样的框架用顺手了,感觉更爽,更方便,尤其是保持登录态等很多地方,也确实是 Serverless 架构的缺点,这个不得不承认的。
    但是:1 ) Flask 这样的框架,是可以部署到 Serverless 架构上的; 2 ) Serverless 架构也会逐渐的有很多自己框架诞生; 3 )使用 Serverless 架构很多的好处并不是单单说,他比 Flask 框架开发方便多少(其实本身也没复杂多少),而是说你在开发项目过程中关注点是否发生了变化,项目运营过程中,运维工作是否发生转变......
    一个简单的例子,传统的项目上线了,要有人看着你的机器的,如果某一段时间并发突然增多,要有人做扩容,如果流量下来,要有人做缩容,如果有一个机器挂了,要有人把它剔除,修好,再加回。那么在 Serverless 架构下,这些事情,否不需要用户自己考虑,他完全可以把这些工作交给服务上来解决。
    关于开发,传统情况下,我们开发一个 web 服务,或者说,我么就要一个接口,我们要做什么操作?有一个服务器,安装服务器软件,(以 Flask 为例)安装 Python,安装 Flask,开发完成,运行;那么在 Serverless 架构下,我要做一个接口呢?写函数代码,部署上线。如果是简单一些的代码,都可以不用再本地写,可以直接在线上写。
    所以说其实 Serverless 架构下的方便与不方便是相对的。如果你说我在 flask 取传递的参数(例如 post,get 等传递的内容),我保持登录态,有个缓存什么的,这些一定是 flask 方便,但是你要说我从大局来看,其实还是 Serverless 更加方便一些。

    还有,最后最后,要和你说的一件事是:Serverless.com 虽然叫 Serverless Framework 但是实际上他并不是 Serverless 架构,它实际上是多云的 Serverless 开发者工具(至少你暂时可以这样认为),他只是名字叫了 Serverless。就像中国电信和电信一样,可能只是一个名字相同,中国电信是中国电信行业的一个运行商。Serverless Framework 是多云的 Serverless Framework 开发者工具。

    当然既然是做开发者工具,自然就不能太寒酸。Serverless Framework ( serverless.com )目前已经集成了多云,并且对 AWS,Tencent 的函数服务,提供了更加强大的支持,例如部署 Flask 框架,部署 Express,部署 Koa........
    scf10cent
        11
    scf10cent  
    OP
       2020-02-14 11:57:44 +08:00
    @rayhy 是的,总会有一些麻烦的地方,但是这些麻烦的地方也会被逐渐的消灭。
    scf10cent
        12
    scf10cent  
    OP
       2020-02-14 12:00:04 +08:00
    @whileFalse 所有业务绑定在同一个函数中是否有意义,其实这个还要根据跟人需求而定。
    据一个我的个人博客例子,我的博客,前端有 10 个函数,负责常用逻辑的处理,后台使用了一个 Flaks 框架用了 Flask-admin 来做的,就直接一股脑放在了一个函数中,这样前端性能有保证,后台自己用,对性能没太多要求,开发起来反而更加方便。
    scf10cent
        13
    scf10cent  
    OP
       2020-02-14 12:00:56 +08:00
    @whileFalse 是的,Flask 这样的框架,如果一股脑放入函数中,是可能带来启动速度慢的问题,但是各个厂商也都在优化冷启动问题,可能不久的将来,就会基本消灭冷启动。
    whileFalse
        14
    whileFalse  
       2020-02-14 13:34:01 +08:00
    @scf10cent #12
    你提到自用的后端对性能不敏感,是 Flask ;前端是独立的函数。那你前端还是几乎不依赖框架裸写咯?在生产场景下,普通的后端程序员不依赖框架的工作效率是很低的。至于对性能不敏感的管理端,部署在 lambda 中也没有太大意义。

    厂商是在优化冷启动问题,但更多的是用户代码之外的冷启动速度优化。Flask 作为用户代码,几乎没法优化冷启动效率。
    Aceyclee
        15
    Aceyclee  
       2020-04-13 16:10:02 +08:00
    写得好详细
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     997 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 18:49 PVG 02:49 LAX 11:49 JFK 14:49
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86