Serverless Framework AWS Lambda Layers(层)配置实战指南
【免费下载链接】serverless⚡ Serverless Framework – Effortlessly build apps that auto-scale, incur zero costs when idle, and require minimal maintenance using AWS Lambda and other managed cloud services.项目地址: https://gitcode.com/GitHub_Trending/se/serverless
导读
AWS Lambda Layer 用于把运行时可复用的依赖(如第三方库、公共代码、配置文件)与函数代码分离打包与共享,让多个函数共享同一份依赖而无需重复打包。本文基于 Serverless Framework 的 AWS Provider 文档 layers.md,系统讲解如何在serverless.yml中声明层、控制其打包行为、跨账号共享层权限,以及把层挂载到单个函数或整个服务的具体方法,并深入到本仓库packages/serverless的打包编译源码与单元测试,说明每一处配置在 CloudFormation 模板生成阶段究竟被如何消费。读完本文,你将能够为服务安全地定义、打包并共享 Layer,同时理解retain、Layer 逻辑 ID、版本复用等底层机制。
一、背景:什么是 AWS 的层(Layer)
在使用 AWS Provider 时,服务中定义的所有 layer都会被映射为 AWS Lambda 的层能力。层存在的价值在于:Lambda 函数本质上包含"你的代码 + 运行平台",而层允许你把共享组件单独抽离、独立打包与版本化,再叠加到任意数量的函数上。这样同一份依赖只需上传一次,多个函数即可复用,减少重复打包体积,也让团队内部更容易共享公共库。
在 Serverless Framework 中,层的声明统一收敛在serverless.yml的layers属性下。框架负责将层目录压缩成 zip、上传到部署用的 S3 bucket,并在生成的 CloudFormation 模板中创建对应的AWS::Lambda::LayerVersion资源。
二、定义层:layers配置详解
所有层的配置都位于serverless.yml顶层layers属性内,每个键即为层名。最小可用示例:
# serverless.yml service: myService provider: name: aws layers: hello: path: layer-dir # 必填,层内容在磁盘上的目录路径 name: ${sls:stage}-layerName # 可选,发布到 AWS 的层名称 description: Description of what the lambda layer does # 可选,发布到 AWS 的描述 compatibleRuntimes: # 可选,该层兼容的运行时列表 - python3.11 compatibleArchitectures: # 可选,该层兼容的指令集架构列表 - x86_64 - arm64 licenseInfo: GPLv3 # 可选,license 信息字符串 # allowedAccounts: # 可选,允许访问此层的 AWS 账号 ID 列表 # - '*' # 注意:如果放开 * 通配符,将无条件把所有 AWS 用户纳入访问范围 retain: false # 可选,默认 false;为 true 时新版本生成后不删除旧层版本各配置项要点:
path(必填):指向层内容所在目录。框架会按目录内容压缩出层构件。name(可选):覆盖发布后的 Layer 名称;不填时框架使用层名本身。description/licenseInfo/compatibleRuntimes/compatibleArchitectures:这些信息在打包阶段会逐一写入 CloudFormation 的AWS::Lambda::LayerVersion属性中,用于帮助使用者判断该层是否可用于自己的运行时与架构。allowedAccounts(可选):跨账号共享的账号 ID 列表,见本文第五节。retain(可选,默认 false):控制旧层版本的保留策略,见第七节源码解析。
2.1 一个服务中声明多个层
同一服务内可以同时声明多个层(受 AWS 侧每个函数最多叠加 5 个层的约束,见下):
# serverless.yml service: myService provider: name: aws layers: layerOne: path: layerOne description: optional description for your layer layerTwo: path: layerTwo layerThree: path: layerThree文档特别提示"你可以在本属性内按需添加至多 5 个层"。从源码看,每个层会独立执行一次打包与编译流程,逻辑互不干扰(详见 compile/layers.js 中compileLayers对getAllLayers()的遍历)。
三、打包控制:全局继承与层级覆盖
层打包遵循与函数类似的package机制,存在两种写法。
3.1 继承服务级package配置
如果服务级定义了全局package.patterns,层默认会继承它:
# serverless.yml service: myService provider: name: aws package: patterns: - '!layerSourceTarball.tar.gz' layers: layerOne: path: layerOne3.2 在层级别独立指定
也可以在单个层内覆写:
# serverless.yml service: myService provider: name: aws layers: layerOne: path: layerOne package: patterns: - '!layerSourceTarball.tar.gz'需要牢记的一个关键语义:所有 patterns(即便继承自服务级配置)都是以该层的path为解析基准,而不是服务根目录。也就是说'!layerSourceTarball.tar.gz'表示排除<layer path>/layerSourceTarball.tar.gz,而不是服务根目录下的同名文件。
3.3 使用预构建的归档文件
如果依赖已经在别处打成了 zip,可直接用package.artifact指向预构建归档,此时无需再写path:
# serverless.yml service: myService provider: name: aws layers: layerOne: package: artifact: layerSource.zip从 Provider 实现看,resolveLayerArtifactName(见 provider.js)会优先采用package.artifact;只有在没有 artifact 时才回退到默认的.serverless/<layerName>.zip打包产物。也就是说,无论走目录打包还是预构建归档,最终上传 S3 的都是该路径下的 zip 文件,二者在编译阶段是统一的。
四、跨账号共享与公开访问:allowedAccounts
默认情况下,层是私有的,只有当前账号可以使用。通过allowedAccounts可以把层共享给指定账号:
# serverless.yml service: myService provider: name: aws layers: layerOne: path: layerOne allowedAccounts: - 111111111111 # 一个具体的账号 ID - 222222222222 # 另一个具体的账号 ID如果希望公开该层(所有 AWS 用户都能无条件使用),传入'*':
# serverless.yml service: myService provider: name: aws layers: layerOne: path: layerOne allowedAccounts: - '*' # 所有账号!授权语义与源码依据:从 compile/layers.js 可以看到,框架会针对allowedAccounts中的每个账号生成一条独立的AWS::Lambda::LayerVersionPermission授权资源:Principal被设置为该账号 ID,LayerVersionArn通过{ Ref: layerLogicalId }指向对应的 Layer 版本,固定动作权限为lambda:GetLayerVersion(模板见同文件cfLambdaLayerPermissionTemplate)。因此"共享"的本质是给特定 Principal 授予获取该层版本的权限;公开共享即把 Principal 设为*,文档注释也特别提醒:这样做等于无条件放行所有 AWS 用户,请按需谨慎使用。
五、在函数中挂载层:functions.<fn>.layers
用函数的layers配置键即可把层挂到函数上。支持直接写层版本 ARN:
functions: hello: handler: handler.hello layers: - arn:aws:lambda:region:XXXXXX:layer:LayerName:Y5.1 同服务内引用:CloudFormation Ref
同服务内自定义的层在 CloudFormation 模板中的逻辑 ID 规则为:层名转 Title Case(去空格)后追加LambdaLayer。例如配置了名为test的层,则函数中通过!Ref TestLambdaLayer引用:
layers: test: path: layer functions: hello: handler: handler.hello layers: - !Ref TestLambdaLayer这一点有明确的命名实现可验证:getLambdaLayerLogicalId返回${getNormalizedFunctionName(layerName)}LambdaLayer(见 lib/naming.js)。相比写死 ARN,!Ref的好处是版本号交由 CloudFormation 解析,函数在每次部署时自动跟随新层版本。
同时,函数编译阶段会识别以Ref形式引用的本地层:extractLayerConfigurationsFromFunction(见 compile/functions.js)会从 CF 模板资源中回查该层的配置(包括_serverlessLayerName标记)并读取层的 zip 构件纳入函数版本哈希,确保"层代码更新会触发函数版本轮换"这一正确行为。
5.2 服务级默认层:provider.layers
如果想让服务里所有函数都默认加载某些层,可配置在 provider 级别:
# serverless.yml service: myService provider: name: aws runtime: python3.11 layers: - arn:aws:lambda:us-east-1:xxxxxxxxxxxxx:layer:xxxxx:mylayer1 - arn:aws:lambda:us-east-1:xxxxxxxxxxxxx:layer:xxxxx:mylayer2 functions: hello1: handler: handler.hello1 hello2: handler: handler.hello2从 compile/functions.js 可以看到其实现:函数优先使用自身functionObject.layers;若函数未定义,则回退到provider.layers,并刻意用Array.from复制一份新数组,避免多个函数共享同一数组引用引发副作用。需要注意:provider 级配置通常配合 ARN 使用;要引用同服务内自定义层,仍建议在函数级用!Ref精确控制。
六、层定义的优先级总结
综合源码行为,引用顺序可归纳为:
- 函数自身的
functions.<fn>.layers具有最高优先级; - 未显式定义时,回退到
provider.layers(服务级); - 对外部共享层使用 ARN 引用,对同服务自定义层使用
!Ref <TitleCaseName>LambdaLayer。
七、源码视角:层编译与部署的底层机制
为了更自信地使用层,理解打包编译管线很有价值。核心逻辑集中在 compile/layers.js。
7.1 从layers配置到 CloudFormation 资源
AwsCompileLayers插件监听package:compileLayers钩子,在打包阶段为每个层执行compileLayer(compile/layers.js):
- 生成
AWS::Lambda::LayerVersion模板,Content.S3Bucket默认Ref: ServerlessDeploymentBucket,S3Key为部署目录 + 层 zip 文件名; LayerName取layerObject.name || layerName;description、licenseInfo、compatibleRuntimes、compatibleArchitectures仅在配置了对应属性时才写入资源;allowedAccounts展开为若干AWS::Lambda::LayerVersionPermission资源;- 每个层还会向模板
Outputs追加三个输出:当前层版本 ARN、基于层内容与配置算出的哈希、S3 Key(getLambdaLayerHashOutputLogicalId等命名见 lib/naming.js)。
7.2retain: true的真实含义
retain一旦开启(compile/layers.js),框架会:
- 用 SHA1(基于层配置与 zip 二进制内容计算)为层资源的逻辑 ID 追加哈希后缀,使内容变化必然产生新逻辑资源;
- 为该层资源以及对应权限资源都设置
DeletionPolicy: 'Retain',确保 CloudFormation 在栈更新或删除时不会物理删除旧层版本。
换句话说,默认行为(retain: false)下,新层版本发布后旧版本会随 CFN 生命周期被清理;而需要长期保留历史版本(例如版本回滚或多环境复用)时,应开启retain。这一行为在单元测试 layers.test.js 中有完整断言(资源与权限资源的DeletionPolicy均为Retain,且逻辑 ID 含哈希)。
7.3 层复用优化:内容未变则不重复上传
compareWithLastLayer(compile/layers.js)会在编译后调用 CloudFormationdescribeStacks,将上一次部署输出的层哈希与本次对比。若哈希一致且历史层仍可定位,框架会直接复用上次的 S3 Key、跳过重复上传并标记artifactAlreadyUploaded,日志输出Layer <name> is already uploaded.——这正是仓库中check-for-changes-reused-layer专项测试(check-for-changes-reused-layer.test.js)所覆盖的场景:未变更的层不会白白刷出版本号。
八、实战建议与常见注意点
- 路径基准要分清:打包 patterns 永远相对层的
path解析,别把服务根目录路径误写进去。 - 本地层用
!Ref,外部层用 ARN:同服务内引用务必使用TitleCase层名 + LambdaLayer的 Ref 形式,让版本自动跟随;跨账号或跨服务才维护 ARN。 - 控制公开范围:
allowedAccounts中的'*'会把层暴露给所有 AWS 账号,仅在确实需要公共依赖时使用。 - 按需开启
retain:涉及生产共享层时应评估是否需要保留历史版本;同时理解启用后逻辑资源会带哈希后缀,部署产物更"多",但换来的是旧版本不被删除的安全边界。 - 每函数 5 层上限:文档明确至多 5 个层,规划公共依赖时优先合并可复用的依赖为更少、更聚合的层。
- 配置参考:层相关的示例配置集中在 docs/sf/providers/aws/guide/layers.md,更多 AWS Guide 类配置(函数、事件、IAM 等)可继续参考同目录下的 guide/README,整体使用脉络见 getting-started.md。
综上所述,Serverless Framework 把 AWS Lambda 层从"手动上传 zip、手工填 ARN"的繁琐流程,收敛为serverless.yml中一段声明式配置,并在打包阶段自动完成 zip 生成、S3 上传、CloudFormation 资源与权限展开、内容哈希版本复用等一系列工作。理解上述参数与编译管线,即可在团队内安全、高效地构建和维护共享依赖体系。
【免费下载链接】serverless⚡ Serverless Framework – Effortlessly build apps that auto-scale, incur zero costs when idle, and require minimal maintenance using AWS Lambda and other managed cloud services.项目地址: https://gitcode.com/GitHub_Trending/se/serverless
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考