谜团揭秘:揭穿围绕 Lambda 冷启动的谬论
这篇富有见地的 InfoQ 文章消除了围绕 Lambda 冷启动(Cold Start)的常见谬论,这是无服务器(serverless) 计算社区中广泛讨论的话题。随着无服务器架构的不断普及,对 Lambda 冷启动的误解激增,经常会导致混乱和错误的优化策略。
我们将深入研究什么是 Lambda 冷启动,它在什么情况下发生,以及它对应用程序性能的影响。
本文还将讨论缓解 Lambda 冷启动的策略,并解释为什么它并不总是像描述的那样具有威胁性。通过阐明这些谬见,我们旨在更清晰地理解 Lambda 冷启动,使开发人员能够更有效地利用无服务器计算。在本文的最后,我们将分享一个使用 AWS Lambda 构建低延迟控制平面应用程序的用例。
冷启动(Cold start) 是指尚未使用的应用程序需要更长时间才能启动的现象。对于 AWS Lambda,当需要引导一个新的函数实例来响应调用时,就会发生这种情况。影响冷启动时长的因素包括运行时的选择、配置设置以及 Lambda 函数是否属于 VPC 等。
关于冷启动,有必要揭穿围绕这一现象的一些普遍的谬论。冷启动通常被认为是无服务器计算的一个重大缺点,但要理解其影响,需要有一个微妙的视角。
一个普通的冷启动(视频截图)
VPC内部的冷启动 (视频截图)
AWS Lambda 中的冷启动通常会造成混乱和误解,从而导致某些谬论的传播。本文旨在揭穿这些谬论,为 Lambda 冷启动提供一个更清晰的视角。
一个常见的误解是,每次 Lambda 调用都会发生冷启动。然而,事实并非如此。AWS Lambda 会尽可能重用函数实例,因此只有在需要新实例时才会冷启动。这可能发生在函数部署后的第一个请求,该请求在一段时间内不再活动,或者在扩展以处理增加的负载时。因此,虽然冷启动确实会发生,但并不像人们通常认为的那样频繁。
另一个误解是,冷启动总是会导致显著的延迟,需要增加额外的内存 /CPU 才能克服这种影响。冷启动的持续时间取决于几个因素,包括运行时(runtime)、函数配置以及该函数是否属于 VPC。虽然有些冷启动可能需要几秒钟,但其他启动可能会快得多。因此,认为所有冷启动都会导致长时间的延迟是不正确的。
冷启动的影响常常被高估。虽然对延迟敏感的应用程序确实可能会受到冷启动的影响,但许多类型的应用程序,如后台任务或异步处理作业,在很大程度上不受影响。因此,冷启动的影响并不是普遍显著的,很大程度上取决于应用程序的性质。
一个常见的误解是,使用无操作(no-op)指令持续触发 Lambda 函数会就维护一个热实例池,以便在没有冷启动延迟的情况下处理后续请求。然而,由于不可预测的热状态管理、潜在的资源浪费、节流的风险以及更有效的替代方案的存在,如 AWS 的预置并发(Provisioned Concurrent)功能,该策略存在缺陷。因此,对于管理 Lambda 函数准备就绪状态来说,这种做法 既无效率,也不推荐使用。
关于 Lambda 冷启动的一个普遍的误解是,经历一次持续时间为“T ms”的冷启动比经历 N 次持续时间分别为“T/N th ms”的冷启动更糟糕。这一观点源于这样一种想法,即与单次持续时间更长的冷启动相比,将冷启动的延迟分布在多个调用之间可以减轻其影响。然而,这是一种误解。
让我们来考虑一个例子,其中它有两种场景:
场景 A:对于 20% 的调用,会经历每次 1 秒的冷启动惩罚。
场景 B:对于 40% 的调用,会经历每次 500 毫秒的冷启动惩罚。
问:场景 A p100 为 1100ms,而场景 B p100 为 600ms;哪一个更好?
当考虑 p100(第 100 个百分位,100%)时,方案 B 更好,因为它的响应时间为 600 ms,比场景 A 的 1100 ms 更低。然而,当我们观察较低的百分位和截尾后的平均值时,评估可能会发生变化。因此,当考虑第 70/80 个百分位数(70%/80%)和第 70/80 百分位数的截尾平均值时,场景 A 更好,因为它的两个度量值都较低。
场景 A
场景 B
通过直方图 /cdf 监测延迟可以帮助可视化 Lambda 函数延迟的异常值。
尾部延迟(异常值)表示 Lambda 函数冷启动的影响。
异常值扭曲了平均值,因此平均值并不能代表包含异常值的系统中的典型行为。
百分位数(p)和截尾均值(TrimmedMean,tm)是定义服务 API 延迟目标的好方法。
p99 表示 99% 的客户请求的最差(最高延迟)体验。
它并没有告诉我们在这 99% 的请求中,有多少百分比的请求观察到了最糟糕的体验(最高延迟)。
tm99 表示 99% 的客户请求的截尾平均值(通过去除 1% 的异常值)。
截尾平均值(tm)越高表示冷启动的频率越高,而百分比(p)越高主要表示冷启动持续时间越长。
较高的截尾平均值(tm)比较高的百分位数(p)更令人担忧,因为这意味着相当大比例的客户看到了延迟增加。
Lambda 冷启动的可观测性
Lambda 冷启动的可观测性至关重要,因为它允许开发人员衡量初始化延迟对其无服务器应用程序整体性能的影响。开发人员可以通过监控冷启动的持续时间和频率等指标来识别模式和潜在的应用程序瓶颈。这种级别的洞察力对于实现有效的优化策略至关重要,例如调整函数内存设置、调整超时配置或优化代码库以减少初始化时间。
1) Lambda 的 AWS X-Ray 跟踪:在 Lambda 冷启动期间,可以在 AWS X-Ray 中观察到“初始化”子段。此段捕获函数的初始化活动,例如依赖项的解析和全局变量的设置,这些活动发生在函数处理程序执行之前。这一步非常关键,因为它只发生一次,从而避免了每次函数调用都需要重复的问题。然而,大量依赖项的存在可能会延长这个初始化阶段。
2) Lambda 的 AWS Cloudwatch 日志:AWS Lambda 在每次调用结束时的 REPORT 消息中会包括初始化持续时间。但是,该详细信息并不能自动作为 CloudWatch 指标来提供。Lambda 还会自动捕获 Lambda 执行环境生命 周期中每个阶段的日志,并将其发送到 CloudWatch 日志中。这包括初始化(Init)阶段,Lambda 在此阶段初始化 Lambda 运行时和函数处理程序外部的静态代码;恢复(Restore)阶段,Lambda 从启用了 Lambda Snapstart 函数 的快照中恢复执行环境;以及调用(Invoke)阶段,Lambda 在该阶段执行函数处理程序中的代码。
3)用于 Lambda 实例的 AWS CW LogStream 为 AWS Lambda 函数提供了强大而详细的日志记录机制,为彻底的调试和性能分析提供了必要的见解。通过为 Lambda 函数的每个实例创建专用的日志流,开发人员可以更有效地解决特定的调试问题:
请求的实例标识:通过检查日志条目,开发人员可以确定请求“A”和“B”是由同一个 Lambda 实例处理的还是由不同的实例处理的。这对于理解负载分布和会话一致性至关重要。
负载分布分析:日志流可以监控请求如何在 Lambda 函数的不同实例之间分布。这有助于验证工作负载是否在所有可用的实例之间得到了公正的平衡。
活动实例计数:通过日志分析,开发人员可以估计当前有多少 Lambda 函数的实例是处于活动状态的,这有助于扩展决策和资源优化。
冷启动分析:日志提供每个实例冷启动持续时间的数据。这允许进行比较,以确定冷启动持续时间是否在所有实例中一致,或者是否需要解决变异。
实例启动频率:日志可以显示新实例的启动频率,这对于理解 Lambda 函数在不同负载下的行为和容量规划至关重要。
识别故障实例:如果某个特定实例始终显示错误或执行时间较长,则可以通过日志将其识别为“坏的”实例。这对于维护应用程序的整体运行状况至关重要。
版本和别名跟踪:每个日志条目都使用 Lambda 函数的版本或别名进行了标记,从而更容易跟踪每个实例所属的版本或别名。这在管理多个版本并确保正确的版本运行时尤其有用。
AWS 提供了几种策略来管理和缓解冷启动。
1) SnapStart
Java 的 Lambda snapstart 可以在不增加额外成本的情况下,将对延迟敏感的应用程序的启动性能提高 10 倍。
Snapstart 概述(视频截图)
2) Lambda 预置并发
预置并发(Provisioned Concurrency)使函数保持“热”状态,并准备好立即响应调用。它还产生了额外的费用。如果应用程序有严格的冷启动延迟要求,请使用预置并发。我们不能在同一个函数版本上同时使用 SnapStart 和预置并发。
当考虑在 AWS Lambda 中使用预置并发(Provisioned Concurrency)时,需要记住以下几个关键细节,以确保能有效的部署和管理它:
设置时间:一旦为 Lambda 函数启用了预置并发(Provisioned Concurrency),AWS 就需要时间来配置指定数量的并发执行。该设置过程可能需要几分钟时间。在此期间,我们可以在 AWS 管理控制台中监控配置状态,以查看功能何时可用。
绑定到特定版本:必须针对 Lambda 函数的特定版本配置预置并发(Provisioned Concurrency)。它不能直接应用于 $LATEST 版本,后者是一个可变版本,表示最新上传的代码。这确保了所提供实例的行为是可预测且稳定的。
别名上的配置:如果在别名上配置预置并发(Provisioned Concurrency),它会自动应用于别名所指向的版本。例如,如果我们有一个名为“canary”的别名,它指向函数的版本 10,则在“canary”别名上设置预置并发(Provisioned Concurrency)意味着版本 10 将准备好已配置的实例。
对 $LATEST 和别名重叠的限制:不能在“$LATEST”别名或指向“$LATEST”的任何别名上设置预置并发(Provisioned Concurrency)。此外,不能在指向同一版本的多个别名上设置预置并发(Provisioned Concurrency)。该限制旨在避免配置冲突和已配置容量的重复。
不可组合性:不能在别名及其基础版本上组合预置并发(Provisioned Concurrency)的配置,也不能在指向同一版本的两个别名上配置它。这可以防止可能导致意外行为或过度供应的重叠配置。
初始化阶段的选择性引导
缓解 AWS Lambda 冷启动的有效策略之一是采用一种名为选择性引导的技术。在 Lambda 函数的初始化阶段,主要执行两项任务:
引导运行时:这涉及到为函数设置执行环境,包括加载运行时、函数代码和任何依赖项。
运行函数的静态代码:这指的是在函数处理程序之外执行任何代码,这些代码只在 Lambda 函数初始化时运行一次。
对于 预置并发,Lambda 实例保持“温暖”并准备立即响应,可以执行“完全”引导。这意味着要提前运行所有初始化任务,以确保函数准备好以最小的延迟响应调用。
对于 按需调用,根据需要初始化的新 Lambda 实例,可以执行“最小”引导。这意味着只运行启动函数所需的基本初始化任务,从而减少冷启动时间。
要实现这种选择性引导,可以使用 Lambda 的环境变量 AWS_LAMBDA_INITIALIZATION_TYPE 来确定在函数代码静态部分的初始化任务期间要执行的引导类型。
在这个案例研究中,我们将探索使用 AWS Lambda 开发低延迟控制平面 API 后端。主要目标是确保快速响应时间,使冷启动的有效管理至关重要。
控制平面 API 后端是在基于云的应用程序中管理和编排资源的大脑。它要求低延迟,以快速响应 API 调用,从而确保平稳的操作和用户体验。然而,像 AWS Lambda 这样的无服务器架构经常面临冷启动的挑战,这可能会会带来延迟。
第一步是优化函数配置以确保低延迟。运行时是根据性能选择的,内存设置经过调整以平衡成本和性能。部署包的大小被最小化,以减少函数代码的下载时间,从而减少冷启动时间。
接下来,使用了 AWS Lambda 的预置并发(Provisioned Concurrency)功能。该功能使指定数量的函数实例保持“热”状态,并准备好立即响应调用。通过设置适当级别的预置并发,我们确保始终有足够的热实例可用于处理传入的 API 调用,而不会导致冷启动。溢出 lambda 调用(按需调用)仍将采用冷路径,因此监控的预置并发使用度量的百分比(%)至关重要。
切换到 AWS Java SDK v2,因为它包括三个主要的更改,可以缩短初始化时间(请参阅 AWS SDK 文档)。
立即(Eager)与延迟(Lazy)初始化,取决于容器是“按需调用”还是“预置并发”。
通过实施这些策略,控制平面 API 后端可以有效地管理冷启动。API 后端的延迟显著降低了,大多数 API 调用由热的 Lambda 实例处理。即使发生了冷启动,也通过仔细的功能配置将其持续时间降到了最低。
该案例研究表明,通过适当的配置和管理策略,AWS Lambda 可以有效地处理控制平面 API 后端的低延迟要求。关键是了解 Lambda 冷启动的行为和特征,并使用可用的功能和策略来减轻其影响。
Mohit Palriwal 是 Netflix 的高级软件工程师,也是 Netflix 可观测性团队的重要成员。Netflix Atlas 项目支持团队的成员,Netflix Atlas 项目是一个开源的维度时间序列数据库,旨在处理高规模的需求。在加入 Netflix 之前,Mohit 是 Salesforce 的首席软件工程师,在那里他与人合作构建了 AWS 上的可观测性云。Mohit 的经验也延伸到了亚马逊网络服务(AWS),他在那里花了四年多的时间开发和推出了无服务器架构上的 AWS Pinpoint。
原文链接:
https://www.infoq.com/articles/aws-lambda-cold-starts-myths/
声明:本文为 InfoQ 翻译,未经许可禁止转载。
德国再次拥抱Linux:数万系统从windows迁出,能否避开二十年前的“坑”?
系统 bug 致百人入狱,砸了 2.8 亿元仍上云失败!二十年了,这家大企业被日本软件坑惨了
章文嵩、蒋晓伟、李飞飞、张凯巅峰对谈:大模型时代的数据智能新趋势
演示文生图时出现sleep代码,华为回应造假嫌疑;微软将中国AI团队集体打包到美国;百度ECharts创始人“下海”养鱼|Q资讯
微信扫码关注该文公众号作者