Lambda 托管实例的最佳实践
容量提供程序配置
按信任级别分离容量提供程序。为具有不同安全要求的工作负载创建不同的容量提供程序。分配给同一容量提供程序的所有功能必须相互信任,因为容量提供程序充当着安全边界的角色。
采用描述性名称。对容量提供程序进行命名,以清楚地表明其预期用途和信任级别(例如 production-trusted、dev-sandbox)。这有助于团队了解每个容量提供程序的目的和安全状况。
使用多个可用区。创建容量提供程序时,指定跨多个可用区的子网。Lambda 默认为可用区启动三个实例以增强韧性,从而确保您的函数具备高可用性。
实例类型选择
让 Lambda 选择实例类型。默认情况下,Lambda 会为您的工作负载选择最佳实例类型。我们建议让 Lambda Managed Instances 为您选择实例类型,因为限制可能的实例类型数量可能会导致可用性降低。
根据特定要求指定实例类型。如果您有特定的硬件要求,请将允许的实例类型设置为兼容的实例列表。例如:
-
对于需要高网络带宽的应用程序,请选择多种 n 系列实例类型
-
对于有成本约束的测试或开发环境,请选择较小的实例类型,例如 m7a.large
函数配置
选择适当的内存和 vCPU 设置。选择能够支持您的函数多并发执行的内存和 vCPU 配置。支持的最小函数大小为 2GB 和 1 个 vCPU。
-
对于 Python 应用程序,由于 Python 处理多并发的方式,选择较高的内存与 vCPU 比(例如 4:1 或 8:1)
-
对于 CPU 密集型操作或执行少量 IO 的函数,请选择多个 vCPU
-
对于 IO 密集型应用程序(如 Web 服务或批处理作业),多并发可提供最大的益处
适当配置最大并发。Lambda 会选择合理的默认值以实现最大的并发性,从而在资源消耗和吞吐量之间达到平衡。根据函数的资源使用情况调整此设置:
-
如果您的函数调用所占用的 CPU 资源极少,可提高最大并发数(最高每个 vCPU 64 个)
-
如果您的应用程序占用大量内存但 CPU 使用率很低,请降低最大并发数。
请注意,并发度极低的执行环境可能会出现节流并难以实现扩展。
长时间运行的函数
对于异步调用和事件源映射调用,Amazon Lambda 托管实例上的函数在每次调用时最多可运行 90 分钟(5,400 秒),但 Amazon MQ 和 Amazon DocumentDB 事件源映射除外,这两者仍限制为 15 分钟。同步调用和函数初始化阶段也保持限制为 15 分钟。有关设置超时的更多信息,请参阅配置 Lambda 函数超时。由于函数可以较长时间运行,请针对本质上属于临时性的组件(例如网络连接和凭证)查看以下注意事项。
适配空闲连接超时。请确保下游服务(例如 Amazon RDS、Amazon ElastiCache 和外部 API)的空闲连接超时时间能够适配完整的函数持续时间。如果函数通过 NAT 网关路由流量,请发送保持连接数据包,以防止空闲连接在 NAT 网关的 350 秒空闲超时后断开。
遵守 DNS TTL 值。解析外部主机名时遵守 DNS TTL 值。Amazon SDK 会自动处理此事项,但自定义 HTTP 客户端可能会将 DNS 解析结果缓存超过其 TTL 值规定的时间。
刷新临时凭证。如果您的函数获取临时凭证或令牌,请验证它们在完整执行持续时间内保持有效,或者在后台刷新它们。
进行幂等性设计。Lambda 不保证精确一次性处理。随着函数较长时间运行,重试和重复传送的时间窗口也会随之扩大。使用 Powertools for Amazon Lambda 为付款或数据库写入等操作实现幂等性,从而确保即使这些操作多次运行,它们也会产生相同的结果。如果您使用 Lambda 持久性函数,则步骤具有至少一次的执行语义:持久执行 SDK 在重放期间会跳过已完成的步骤,但在检查点之前失败的步骤可能会再次运行。您可以使用执行名称作为幂等键。
调整事件源映射以适应较长的处理时间。对于 Amazon SQS,将队列的可见性超时设置为函数超时的至少六倍时间,以确保在函数受到限制时,Lambda 有足够的时间重试批次。Lambda 会在您创建事件源映射时验证这一点,但不会阻止导致不匹配情况的后续队列或函数更改。对于 Amazon Kinesis Data Streams 和 Amazon DynamoDB Streams,请配置最大批处理时间窗口和并行化因子以应对每个批次较长的处理时间,并且启用部分批次失败报告,从而仅重试失败的记录,而非重试整个批次。
扩展配置
设置适当的目标资源利用率。默认情况下,Lambda 会保留足够的余量,让您的流量在 5 分钟翻倍也不会受到节流。根据您的工作负载特征对此进行调整:
-
对于非常稳定的工作负载或对节流不敏感的应用程序,将目标设定得较高,以实现更高的利用率和更低的成本。
-
对于可能出现流量爆发的工作负载,请将资源目标设置为较低水平,以维持额外的余量
规划流量增长。如果您的流量在 5 分钟内增长超过一倍,您可能会在 Lambda 纵向扩展实例和执行环境时看到节流出现。请设计您的应用程序,以应对在快速纵向扩展期间可能出现的节流情况。
安全性
对 PassCapacityProvider 权限应用最低权限。仅向必要的容量提供程序授予 lambda:PassCapacityProvider 权限。使用资源级权限来限制用户能够为函数分配哪些容量提供程序。
监控容量提供程序的使用情况。使用 Amazon CloudTrail 监控容量提供程序的分配和访问模式。这有助于识别未经授权的访问行为,并确保符合安全策略的要求。
隔离不受信任的工作负载。不要依靠容器来实现不受信任的工作负载之间的安全隔离。使用不同的容量提供程序来隔离相互不信任的工作负载。
成本优化
使用 EC2 定价选项。利用 EC2 节省计划和预留实例来降低成本。这些定价选项适用于底层 EC2 计算(15% 的管理费不享受折扣)。
针对稳态工作负载进行优化。Lambda 托管实例最适合具有稳态特性且流量可预测的大流量函数。对于突发流量模式,Lambda(默认)可能更具成本效益。
监控资源利用率。跟踪 CloudWatch 指标以了解 CPU 和内存利用率。根据实际使用模式调整函数内存分配和实例类型选择,以优化成本。
监控和可观测性
监控容量提供程序指标。跟踪容量提供程序级别的指标(包括 CPUUtilization、MemoryUtilization、vCPUAvailable 和 MemoryAvailable),以验证是否有足够的资源可用于工作负载。
监控执行环境指标。跟踪执行环境级别的指标,包括 ExecutionEnvironmentConcurrency 和 ExecutionEnvironmentConcurrencyLimit,以了解扩展行为并识别潜在的节流。
设置 CloudWatch 警报。为关键指标创建 CloudWatch 警报,以主动识别问题:
-
CPU 或内存利用率高
-
可用容量低
-
接近并发限制
特定语言注意事项
遵循特定语言的最佳实践。每种编程语言处理多并发的方式不同。请查看特定语言指南,了解详细建议:
-
Java:对特定于请求的状态使用线程安全的集合
AtomicInteger和ThreadLocal -
Node.js:对所有特定于请求的状态使用 InvokeStore,并避免使用全局变量
-
Python:在
/tmp中使用包含请求 ID 的唯一文件名,并考虑采用基于进程的内存隔离 -
Rust:使用
run_concurrent代替run,启用concurrency-tokio功能。处理程序必须是Clone+Send。
测试线程安全和并发问题。在将函数部署到生产环境之前,对您的函数进行全面测试,以检查是否存在线程安全问题、争用条件以及在并发负载下是否能正确实现状态隔离。
后续步骤
-
使用 CloudWatch 指标监控 Lambda 托管实例