使用 AWS SAM 构建 Serverless 应用:从模板编写、打包到 CloudFormation 部署与内在函数实战指南
后端云原生IaC【免费下载链接】serverless-application-modelThe AWS Serverless Application Model (AWS SAM) transform is a AWS CloudFormation macro that transforms SAM templates into CloudFormation templates.项目地址https://gitcode.com/gh_mirrors/se/serverless-application-model点击查看免费下载导读本文基于 serverless-application-model 仓库的 HOWTO.md 编写系统讲解如何用 AWS Serverless Application ModelSAM编写 JSON/YAML 格式的 SAM 模板并借助aws cloudformation package/aws cloudformation deploy或对应的sam package/sam deploy完成从代码打包、S3 上传到 CloudFormation 变更集执行的全流程。同时我们将深入本仓库的 samtranslator 源码揭示 SAM 模板在被 CloudFormation 转换Transform为原生 CloudFormation 模板时CodeUri/DefinitionUri是如何被解析、内在函数Ref、Fn::Sub、Fn::GetAtt、Fn::ImportValue是如何被求解以及哪些属性不支持ImportValue等关键底层机制。读完本文你将能独立编写、打包并安全部署一个生产可用的 SAM 应用并理解每一步背后的转换原理。什么是 AWS SAM 与 SAM 模板AWS Serverless Application ModelAWS SAM是 AWS CloudFormation 之上的一个 Transform 宏macro它允许你用SAM 模板一个 JSON 或 YAML 配置文件来描述 Serverless 应用中的 Lambda 函数、API 端点以及其他资源。SAM 模板本身不是 CloudFormation 原生模板而是需要经过Transform: AWS::Serverless-2016-10-31声明的转换过程。本仓库的核心价值就在于此samtranslator包正是这个 Transform 宏的参考实现。它的入口函数位于 samtranslator/translator/transform.py该函数接收 SAM 模板片段input_fragment和参数值parameter_values完成如下流水线Parser()解析并校验模板结构将 SAM 模板交由Translator.translate()处理遍历模板中的每个 SAM 资源AWS::Serverless::Function、AWS::Serverless::Api等调用各自宏的to_cloudformation()方法生成对应的原生 CloudFormation 资源最终返回一份可以被 CloudFormation 直接执行的纯 CloudFormation 模板。你可以通过aws cloudformation或 aws-sam-cli 将 SAM 模板上传到 CloudFormationCloudFormation 会为应用中的所有资源创建一个CloudFormation Stack堆栈统一管理。之后每次更新 SAM 模板只需重新部署到同一个堆栈CloudFormation 会负责增量更新其中的各个资源无需手动逐个创建或删除。从转换源码看samtranslator/translator/translator.py 中的translate()方法是核心它会按特定顺序遍历资源先处理AWS::Serverless::Function与AWS::Serverless::StateMachine因为它们可能通过 API 事件源修改对应AWS::Serverless::Api的 Swagger 定义再处理 API最后处理其他资源和 Connector并把转换结果写入新模板副本。转换完成后还会执行插件钩子after_transform_template并删除Transform声明最终产出纯粹的 CloudFormation 模板。开始使用 SAM 模板编写 SAM 模板的第一步是了解 SAM 规范本身。仓库内的规范与参考文档包括samtranslator/schema/schema.jsonSAM 资源的 JSON Schema 定义AWS::Serverless::Function、AWS::Serverless::Api等全部资源类型及其属性docs/schema.md面向读者的规范说明samtranslator/internal/schema_source/aws_serverless_function.py 等源码文件各个 SAM 资源类型的属性模型与校验逻辑。要快速生成一个可用的 SAM 项目骨架可以借助 aws-sam-cli 的sam init命令。HOWTO 中给出的示例是$ sam init --runtime python3.14该命令会基于指定运行时此处为 Python 3.14初始化一个 SAM 工程包含模板文件与示例代码目录。需要注意的是--runtime取值应与你实际安装的 aws-sam-cli 版本所支持的运行时列表一致。打包构建产物Packing Artifacts在部署 SAM 模板之前需要先把两类本地产物上传到 S3Lambda 函数代码zip、jar 或源码目录API 的 OpenAPISwagger定义文件。上传后需要在模板中把CodeUri和DefinitionUri属性指向 S3 上的文件位置。你可以手动完成上传也可以使用aws cloudformation packageCLI 命令自动完成本地产物 → S3的上传并返回一份把本地引用替换为 S3 位置的新模板副本。模板中声明本地路径在模板中CodeUri与DefinitionUri先写成相对路径如下所示示例来自 HOWTOMyLambdaFunction: Type: AWS::Serverless::Function Properties: CodeUri: ./code ... MyApi: Type: AWS::Serverless::Api Properties: DefinitionUri: ./specs/swagger.yaml ...执行打包命令使用 AWS CLI 打包上传产物并生成可直接部署的模板$ aws cloudformation package \ --template-file /path_to_template/template.yaml \ --s3-bucket bucket-name \ --s3-prefix appname/branchname/version \ --output-template-file packaged-template.yaml使用 aws-sam-cli 的等价命令$ sam package \ --template-file /path_to_template/template.yaml \ --s3-bucket bucket-name \ --s3-prefix appname/branchname/version \ --output-template-file packaged-template.yaml各参数含义参数说明--template-file本地 SAM 模板文件的路径--s3-bucket用于存放打包产物的 S3 桶名--s3-prefix产物在桶内的前缀路径建议按appname/branchname/version组织便于版本管理--output-template-file打包后输出模板的保存路径该模板可直接交给 CloudFormation 部署。打包后的模板形态打包命令返回的模板会将本地路径替换为 S3 URI形态大致如下MyLambdaFunction: Type: AWS::Serverless::Function Properties: CodeUri: s3://mybucket/my-zipfile-path ... MyApi: Type: AWS::Serverless::Api Properties: DefinitionUri: s3://mybucket/my-openapi-file-path ...底层原理S3 URI 是如何被解析的CodeUri/DefinitionUri最终会变成 CloudFormationAWS::Lambda::Function的Code属性S3Bucket、S3Key、S3ObjectVersion这一步由 samtranslator/model/s3_utils/uri_parser.py 中的construct_s3_location_object()完成。从源码可以总结出它支持三种输入形式字典形式{ Bucket: ..., Key: ... }必须同时包含Bucket与Key否则抛出InvalidResourceException提示CodeUri requires Bucket and Key properties to be specified.S3 URI 字符串形如s3://bucket/key可带?versionIdxxx查询参数由 parse_s3_uri() 解析它使用urllib.parse.urlparse把s3://的 netloc 当作 Bucket、path 当作 Key并支持versionId查询参数转为Version字段若解析失败会抛出明确错误提示动态引用dynamic reference源码对{{resolve:ssm:...}}、{{resolve:ssm-secure:...}}、{{resolve:secretsmanager:...}}模式做了专门检测若在CodeUri字符串中发现此类引用会直接报错并建议改用FunctionCode对象格式。也就是说aws cloudformation package只是完成了本地路径 → S3 URI的替换而真正把 S3 URI 拆解为S3Bucket/S3Key并注入生成资源的逻辑位于 samtranslator 的上述源码中。部署到 AWS CloudFormationSAM 模板最终通过 CloudFormation 的变更集ChangeSet机制部署先用 SAM 模板创建变更集再执行变更集。可以把变更集理解为当前堆栈模板与新部署模板之间的差异diff。创建变更集后你有机会在执行前仔细检查这份差异避免意外改动。AWS 控制台与 AWS CLI 均提供创建和执行变更集的命令。使用 deploy 命令一键部署更便捷的方式是使用aws cloudformation deploy它在内部自动完成创建变更集 → 执行变更集 → 等待部署完成并在部署失败时打印调试提示。部署打包后的模板到名为my-new-stack的堆栈$ aws cloudformation deploy \ --template-file /path_to_template/packaged-template.yaml \ --stack-name my-new-stack \ --capabilities CAPABILITY_IAMaws-sam-cli 的等价命令$ sam deploy \ --template-file /path_to_template/packaged-template.yaml \ --stack-name my-new-stack \ --capabilities CAPABILITY_IAM其中--capabilities CAPABILITY_IAM是必需的因为 SAM 模板通常会为 Lambda 函数自动创建 IAM 角色AWS::IAM::Role而 CloudFormation 出于安全考虑只有在显式声明CAPABILITY_IAM或CAPABILITY_NAMED_IAM时才允许创建此类资源。如果你的模板还使用了命名 IAM 角色则需改为CAPABILITY_NAMED_IAM。底层原理部署时模板经历了什么从部署视角看CloudFormation 在创建变更集时会调用 SAM Transform也就是本仓库 samtranslator/translator/transform.py 中的transform()函数。整个转换链条包括samtranslator/parser/parser.pyParser.parse()先校验模板结构要求必须包含非空的Resources节、每个资源必须是对象等再触发插件的before_transform_template生命周期钩子samtranslator/translator/translator.pyTranslator.translate()安装插件含隐式 REST API/HTTP API 插件、Globals插件、策略模板插件、Serverless 应用插件等逐个把 SAM 资源翻译为 CloudFormation 资源并收集文档错误若存在任何InvalidResourceException/InvalidEventException/InvalidTemplateException最终会抛出InvalidDocumentException部署即失败——这也解释了aws cloudformation deploy打印调试提示时你看到错误来源。部署完成后后续对 SAM 模板的修改只需再次执行sam deploy指向同一--stack-nameCloudFormation 会基于新变更集自动增量更新堆栈中的资源。在 SAM 中使用内在函数Intrinsic FunctionsCloudFormation 提供了一系列可在运行时生成值的函数称为内在函数Intrinsic Functions。由于 SAM 最终通过 CloudFormation 部署因此这些内在函数同样可以在 SAM 模板中使用。HOWTO 给出了两个典型场景。场景一动态设置 Lambda 代码的 S3 位置通过Parameters定义输入参数再用!Ref在运行时取参数值Transform: AWS::Serverless-2016-10-31 # Parameters are CloudFormation features to pass input # to your template when you create a stack Parameters: BucketName: Type: String CodeKey: Type: String Resources: MyFunction: Type: AWS::Serverless::Function Properties: Handler: index.handler Runtime: nodejs24.x CodeUri: # !Ref function allows you to fetch value # of parameters and other resources at runtime Bucket: !Ref BucketName Key: !Ref CodeKey这里CodeUri使用了字典形式BucketKey两个字段均由!Ref动态填充——这正是上一节提到的字典形式的 S3 位置声明且允许引用模板参数。Runtime: nodejs24.x仅为示例运行时请以实际支持的运行时列表为准。场景二为每个堆栈生成不同的函数名利用!Sub做字符串替换把参数后缀拼进函数名Transform: AWS::Serverless-2016-10-31 # Parameters are CloudFormation features to pass input # to your template when you create a stack Parameters: FunctionNameSuffix: Type: String Resources: MyFunction: Type: AWS::Serverless::Function Properties: # !Sub performs string substitution FunctionName: !Sub mylambda-${FunctionNameSuffix} Handler: index.handler Runtime: nodejs24.x CodeUri: s3://bucket/key当你在不同环境如 dev/stage/prod各建一个堆栈、传入不同的FunctionNameSuffix时就能得到互不冲突的 Lambda 函数名。CodeUri这里直接使用了打包后的 S3 URI 字符串。底层原理内在函数是如何被求解的SAM 转换器中的内在函数求解由 samtranslator/intrinsics/resolver.py 与 samtranslator/intrinsics/actions.py 共同实现resolver.py 默认注册三类 ActionRefAction、SubAction、GetAttActionDEFAULT_SUPPORTED_INTRINSICS求解过程采用**前序遍历Pre-Order Traversal**算法见 _traverse()遇到形如{Ref: foo}这种单键字典时尝试解析解析不成功就原样保留交给 CloudFormation 处理RefAction.resolve_parameter_refs() 从参数表中查值并内联SubAction则用正则\$\{([A-Za-z0-9\.]|AWS::[A-Z][A-Za-z]*)\}匹配${...}引用并逐个替换见 _sub_all_refs()值得注意的细节actions.py 顶部_get_parameter_value()会跳过以{{IntrinsicFunction:开头的 CloudFormation 内部占位符在嵌套堆栈变更集场景下出现避免被 SAM 误解析另外!Ref/!GetAtt还支持引用派生 SAM 资源如MyFunction.Alias、MyApi.Deployment转换器通过 resource_refs.py 中的SupportedResourceReferences收集这些引用并解析为真实资源名例如{Ref: MyFunction.Alias}→{Ref: MyFunctionAliasLive}。注意事项Fn::ImportValue仅部分支持Fn::ImportValue允许一个堆栈引用另一个堆栈导出的属性值。由于 SAM 需要解析模板中的某些属性才能生成正确的下游资源因此ImportValue在绝大多数属性上都可用但以下三个属性不支持资源类型不支持的属性AWS::Serverless::FunctionRestApiIdAWS::Serverless::FunctionPoliciesAWS::Serverless::ApiStageName为什么这三个属性特殊从源码看这三处限制的根源是 SAM 转换器必须在转换期读懂这些属性的具体值RestApiIdAWS::Serverless::Function的 Api 事件源samtranslator/model/eventsources/push.py 中的resources_to_link_for_rest_api()需要顺着RestApiId找到同一模板内的AWS::Serverless::Api资源读取其StageName来构造 Lambda 的调用权限stage 后缀。源码注释明确指出customers could use !ImportValue, !Ref or other intrinsic functions which can be sometimes impossible to resolve (ie. when it has cross-stack references)——跨堆栈引用无法在转换期解析因此只能回退到通配AllStages权限。若RestApiId指向外部堆栈SAM 无法在模板内找到对应的 API 资源做此类关联处理PoliciesAWS::Serverless::Function策略需要被解析为具体的 IAM 策略文档含托管策略 ARN 展开、策略模板展开等ImportValue的取值在转换期不可知无法参与这些解析StageNameAWS::Serverless::Api阶段名会直接影响生成资源的命名与权限拼接逻辑同样需要转换期可解析的字面值。其他内在函数不受影响除上述三个属性外!Ref、!Sub、!GetAtt、!If、!FindInMap等内在函数均可正常使用。samtranslator/validator/validator.py 中维护了一份INTRINSIC_ATTR集合涵盖Fn::And、Fn::Base64、Fn::FindInMap、Fn::GetAtt、Fn::ImportValue、Fn::Join、Fn::Sub、Ref等全部标准内在函数说明整个模板校验与转换链路对这些函数是一视同仁的ImportValue的例外仅发生在上述三个需要转换期解析的属性上。实战小结一条完整的 SAM 应用生命周期结合 HOWTO 与源码一条典型的 SAM 应用从编写到上线的完整链路为编写模板用sam init --runtime runtime初始化工程或手动编写 SAM 模板YAML/JSON在Transform中声明AWS::Serverless-2016-10-31可参考仓库内 examples/2016-10-31/api_endpointconfiguration/template.yaml 这类示例模板该示例演示了AWS::Serverless::Api的EndpointConfiguration与!Ref组合用法以及 docs/faq.rst、docs/globals.rst 等补充文档打包执行sam package或aws cloudformation package把本地CodeUri/DefinitionUri产物上传到 S3得到packaged-template.yaml部署执行sam deploy --stack-name my-new-stack --capabilities CAPABILITY_IAM或aws cloudformation deployCloudFormation 内部调用本仓库的transform()完成 SAM → CloudFormation 翻译创建并执行变更集更新修改模板后重新执行部署CloudFormation 依据新变更集增量更新堆栈内资源跨栈复用在绝大多数属性上可使用Fn::ImportValue引用其他堆栈的导出值但避开RestApiId、Policies、StageName这三个不支持转换期解析的属性。至此你已经掌握了 SAM 模板编写、产物打包、变更集部署以及内在函数使用的完整方法论并了解了本仓库转换器在处理这些环节时的真实实现逻辑——这正是 SAM 声明式定义 Serverless 应用能力背后的技术底座。赞分享后端云原生IaC【免费下载链接】serverless-application-modelThe AWS Serverless Application Model (AWS SAM) transform is a AWS CloudFormation macro that transforms SAM templates into CloudFormation templates.项目地址https://gitcode.com/gh_mirrors/se/serverless-application-model点击查看免费下载相关推荐使用 Serverless Framework 部署 AWS SAM 与 CloudFormation 项目使用 Serverless Framework 部署 AWS SAM 与 CloudFormation 项目 本文基于 Serverless Framework开发工具CLI云原生后端Rufus 3步制作 Windows 11 启动盘旧电脑绕过 TPM 限制完整指南Rufus 3步制作 Windows 11 启动盘旧电脑绕过 TPM 限制完整指南 用 Rufus 把 Windows 11 的 ISO 镜像写进 U 盘你开发工具云原生运维AWS Serverless 新手第一课serverless-application-model 完全指南从 SAM 模板到 CloudFormationAWS Serverless 新手第一课serverless application model 完全指南从 SAM 模板到 CloudFormation后端云原生IaC创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考