1. 概述
在之前的文章中,我们已经基于 AWS 实现了一个完整的无服务器(serverless)全栈应用:使用 API Gateway 暴露 REST 接口,Lambda 函数处理业务逻辑,DynamoDB 作为数据存储。
但当时整个部署过程依赖大量手动操作,随着项目复杂度提升或环境增多(如测试、预发、生产),这种方式很快就会变得难以维护。
本文将介绍如何使用 AWS Serverless Application Model(SAM) 来解决这个问题——它支持通过模板定义无服务器应用,并实现自动化部署。
我们将重点讲解以下内容:
✅ SAM 的基本概念及其底层依赖 CloudFormation
✅ 使用 SAM 模板语法定义无服务器应用
✅ 借助 CloudFormation CLI 实现自动化部署
2. 基础知识
正如前文所述,AWS 提供了 API Gateway、Lambda 和 DynamoDB 的组合,可以构建完全无服务器的应用架构。这种模式在性能、成本和可扩展性方面优势明显。
但痛点也很清晰:需要在控制台中手动创建函数、上传代码、配置 DynamoDB 表、设置 IAM 角色、设计 API 路由等,步骤繁琐且容易出错。
当应用变复杂,或者需要管理多个环境时,这种“手敲式”部署就成了效率瓶颈。
这时候就需要 CloudFormation(通用基础设施即代码工具)和专门针对无服务器场景优化的 Serverless Application Model(SAM) 登场了。
2.1 AWS CloudFormation
CloudFormation 是 AWS 提供的基础设施即代码(IaC)服务。你可以通过一个模板(template)声明所需的所有资源,AWS 会自动完成创建和配置。
理解 CloudFormation 和 SAM 的核心概念如下:
- 模板(Template):描述应用运行时结构的蓝图,支持 JSON 或 YAML 格式。你可以在其中定义资源及其配置。
- 资源(Resource):CloudFormation 的基本构建单元。它可以是任何 AWS 资源,比如 RestApi、DynamoDB 表、EC2 实例、IAM 角色等。目前官方支持约 300 种资源类型。
- 栈(Stack):模板的一次实例化。CloudFormation 会根据模板创建并管理这个栈中的所有资源。
⚠️ 注意:CloudFormation 是通用基础设施管理工具,不仅限于无服务器场景。
2.2 Serverless Application Model (SAM)
虽然 CloudFormation 功能强大,但其模板语法相对复杂,写起来不够简洁。
于是 AWS 推出了 SAM —— 一个专为无服务器应用设计的简化层。它的目标很明确:提供更干净、更直观的语法来定义 Lambda、API Gateway 和 DynamoDB 等常见无服务器组件。
目前 SAM 主要支持三种资源类型:
AWS::Serverless::Function
:Lambda 函数AWS::Serverless::Api
:API Gateway 接口AWS::Serverless::SimpleTable
:简化的 DynamoDB 表
SAM 本质上是对 CloudFormation 的扩展,你写的 SAM 模板最终会被转换成标准的 CloudFormation 模板执行。
📌 更多细节可参考:
3. 准备工作
要完成本教程,你需要:
- 一个 AWS 账号(免费套餐即可)
- 安装 AWS CLI
- 在当前区域创建一个 S3 存储桶(用于存放部署包)
创建 S3 桶命令如下:
aws s3 mb s3://baeldung-sam-bucket
🔔 注意:S3 桶名全局唯一,请替换
baeldung-sam-bucket
为你自己的名称。
示例代码基于 《使用 AWS Lambda 与 API Gateway》 一文中的 Java 项目。
4. 编写 SAM 模板
本节将逐步构建 SAM 模板,先看整体结构,再填充具体资源。
4.1 模板结构
SAM 模板通常由头部和资源体组成:
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: Baeldung Serverless Application Model example
Resources:
PersonTable:
Type: AWS::Serverless::SimpleTable
Properties:
# 表属性
StorePersonFunction:
Type: AWS::Serverless::Function
Properties:
# 函数属性
GetPersonByHTTPParamFunction:
Type: AWS::Serverless::Function
Properties:
# 函数属性
MyApi:
Type: AWS::Serverless::Api
Properties:
# API 属性
关键字段说明:
AWSTemplateFormatVersion
:CloudFormation 模板版本Transform
:指定使用 SAM 转换器,这是启用 SAM 语法的关键Description
:模板描述Resources
:定义所有资源,每个资源包含名称、类型和属性
SAM 当前支持的核心资源类型只有三个:
AWS::Serverless::Api
AWS::Serverless::Function
AWS::Serverless::SimpleTable
我们接下来将依次定义这些资源。
4.2 DynamoDB 表定义
我们使用 AWS::Serverless::SimpleTable
快速定义一张表:
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: Baeldung Serverless Application Model example
Resources:
PersonTable:
Type: AWS::Serverless::SimpleTable
Properties:
PrimaryKey:
Name: id
Type: Number
TableName: Person
只需指定主键(id
,类型为 Number
)和表名即可。
✅ 适用场景:仅通过主键访问数据
❌ 复杂需求(如二级索引)建议改用原生 AWS::DynamoDB::Table
详细属性参考:SAM 官方文档 - SimpleTable
4.3 Lambda 函数定义
定义两个函数:StorePersonFunction
和 GetPersonByHTTPParamFunction
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: Baeldung Serverless Application Model example
Resources:
StorePersonFunction:
Type: AWS::Serverless::Function
Properties:
Handler: com.baeldung.lambda.apigateway.APIDemoHandler::handleRequest
Runtime: java8
Timeout: 15
MemorySize: 512
CodeUri: ../target/aws-lambda-0.1.0-SNAPSHOT.jar
Policies: DynamoDBCrudPolicy
Environment:
Variables:
TABLE_NAME: !Ref PersonTable
Events:
StoreApi:
Type: Api
Properties:
Path: /persons
Method: PUT
RestApiId:
Ref: MyApi
GetPersonByHTTPParamFunction:
Type: AWS::Serverless::Function
Properties:
Handler: com.baeldung.lambda.apigateway.APIDemoHandler::handleGetByParam
Runtime: java8
Timeout: 15
MemorySize: 512
CodeUri: ../target/aws-lambda-0.1.0-SNAPSHOT.jar
Policies: DynamoDBReadPolicy
Environment:
Variables:
TABLE_NAME: !Ref PersonTable
Events:
GetByPathApi:
Type: Api
Properties:
Path: /persons/{id}
Method: GET
RestApiId:
Ref: MyApi
GetByQueryApi:
Type: Api
Properties:
Path: /persons
Method: GET
RestApiId:
Ref: MyApi
各属性详解:
属性 | 说明 |
---|---|
Handler |
Java 类全路径 + 方法名,如 包名.类名::方法名 |
Runtime |
运行环境,这里是 java8 |
Timeout |
最大执行时间(秒) |
MemorySize |
分配内存(MB),AWS 会按比例分配 CPU |
CodeUri |
本地代码路径,部署时会自动上传到 S3 |
Policies |
IAM 权限策略,SAM 提供了快捷模板(如 DynamoDBCrudPolicy ) |
Environment |
环境变量,用 !Ref 引用其他资源 |
Events |
触发事件,这里绑定 API Gateway 的路径和方法 |
📌 踩坑提醒:MemorySize
不仅影响内存,还影响 CPU 性能!CPU 密集型任务可通过调大内存提升性能。
完整属性列表见:SAM Function 文档
4.4 使用 Swagger 定义 API(显式方式)
除了在函数中隐式定义路由,也可以直接用 Swagger 定义整个 API:
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: Baeldung Serverless Application Model example
Resources:
MyApi:
Type: AWS::Serverless::Api
Properties:
StageName: test
EndpointConfiguration: REGIONAL
DefinitionBody:
swagger: "2.0"
info:
title: "TestAPI"
paths:
/persons:
get:
parameters:
- name: "id"
in: "query"
required: true
type: "string"
x-amazon-apigateway-request-validator: "Validate query string parameters and headers"
x-amazon-apigateway-integration:
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetPersonByHTTPParamFunction.Arn}/invocations
responses: {}
httpMethod: "POST"
type: "aws_proxy"
put:
x-amazon-apigateway-integration:
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${StorePersonFunction.Arn}/invocations
responses: {}
httpMethod: "POST"
type: "aws_proxy"
/persons/{id}:
get:
parameters:
- name: "id"
in: "path"
required: true
type: "string"
responses: {}
x-amazon-apigateway-integration:
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetPersonByHTTPParamFunction.Arn}/invocations
responses: {}
httpMethod: "POST"
type: "aws_proxy"
x-amazon-apigateway-request-validators:
Validate query string parameters and headers:
validateRequestParameters: true
validateRequestBody: false
关键点:
StageName
:API 阶段名(如 test、prod)EndpointConfiguration
:接口类型(REGIONAL 或 EDGE)DefinitionBody
:内联 Swagger 定义
⚠️ 最关键的是 x-amazon-apigateway-integration
扩展字段:
uri
:指定调用的 Lambda ARNtype: aws_proxy
:使用代理集成模式httpMethod: POST
:Lambda 期望的调用方式
这种方式适合复杂 API 结构,但可读性较差,容易出错。
4.5 隐式 API 定义(推荐)
更简洁的方式是:不在 Resources
中定义 AWS::Serverless::Api
,而是在函数的 Events
中自动触发 API 创建。
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: Baeldung Serverless Application Model Example with Implicit API Definition
Globals:
Api:
EndpointConfiguration: REGIONAL
Name: "TestAPI"
Resources:
StorePersonFunction:
Type: AWS::Serverless::Function
Properties:
Handler: com.baeldung.lambda.apigateway.APIDemoHandler::handleRequest
Runtime: java8
Timeout: 15
MemorySize: 512
CodeUri: ../target/aws-lambda-0.1.0-SNAPSHOT.jar
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref PersonTable
Environment:
Variables:
TABLE_NAME: !Ref PersonTable
Events:
StoreApi:
Type: Api
Properties:
Path: /persons
Method: PUT
GetPersonByHTTPParamFunction:
Type: AWS::Serverless::Function
Properties:
Handler: com.baeldung.lambda.apigateway.APIDemoHandler::handleGetByParam
Runtime: java8
Timeout: 15
MemorySize: 512
CodeUri: ../target/aws-lambda-0.1.0-SNAPSHOT.jar
Policies:
- DynamoDBReadPolicy:
TableName: !Ref PersonTable
Environment:
Variables:
TABLE_NAME: !Ref PersonTable
Events:
GetByPathApi:
Type: Api
Properties:
Path: /persons/{id}
Method: GET
GetByQueryApi:
Type: Api
Properties:
Path: /persons
Method: GET
亮点:
- 使用
Globals
统一设置 API 全局配置(如区域、名称) - 删除了冗余的
MyApi
资源 - 每个函数通过
Events
自动绑定路由
✅ 优点:简洁、易维护
⚠️ 缺点:无法自定义 Stage 名称,AWS 会默认创建名为 Prod
的阶段
💡 建议:中小型项目优先使用隐式定义,复杂项目再考虑显式 Swagger。
5. 部署与测试
完成模板编写后,进入部署阶段。
流程如下:
- 将代码打包上传至 S3
- 使用 CloudFormation 部署栈
- 测试接口
- 清理资源
5.1 上传代码到 S3
使用 cloudformation package
命令自动上传代码并生成新模板:
aws cloudformation package \
--template-file ./sam-templates/template.yml \
--s3-bucket baeldung-sam-bucket \
--output-template-file ./sam-templates/packaged-template.yml
执行后输出类似:
Uploading to 4b445c195c24d05d8a9eee4cd07f34d0 92702076 / 92702076.0 (100.00%)
Successfully packaged artifacts and wrote output template to file packaged-template.yml.
Execute the following command to deploy the packaged template
aws cloudformation deploy --template-file c:\zz_workspace\tutorials\aws-lambda\sam-templates\packaged-template.yml --stack-name <YOUR STACK NAME>
新生成的 packaged-template.yml
中,CodeUri
已更新为 S3 地址。
5.2 执行部署
使用 deploy
命令创建或更新栈:
aws cloudformation deploy \
--template-file ./sam-templates/packaged-template.yml \
--stack-name baeldung-sam-stack \
--capabilities CAPABILITY_IAM
📌 注意:由于涉及 IAM 角色创建,必须添加 --capabilities CAPABILITY_IAM
显式授权。
成功输出:
Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - baeldung-sam-stack
5.3 查看部署资源
确认部署结果:
aws cloudformation describe-stack-resources --stack-name baeldung-sam-stack
该命令会列出栈内所有资源及其状态。
5.4 接口测试
使用 curl
测试三个接口:
存储人员信息(PUT)
curl -X PUT 'https://0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test/persons' \
-H 'content-type: application/json' \
-d '{"id": 1, "name": "John Doe"}'
通过路径参数查询(GET /persons/{id})
curl -X GET 'https://0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test/persons/1' \
-H 'content-type: application/json'
通过查询参数查询(GET /persons?id=1)
curl -X GET 'https://0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test/persons?id=1' \
-H 'content-type: application/json'
🔁 替换 URL 中的 API ID 和区域为你实际生成的值。
5.5 清理资源
测试完毕后,一键删除整个栈:
aws cloudformation delete-stack --stack-name baeldung-sam-stack
S3 桶可手动删除或保留复用。
6. 总结
本文介绍了 **AWS Serverless Application Model (SAM)**,它通过模板化方式实现了无服务器应用的自动化部署,显著提升了开发效率和可维护性。
核心要点回顾:
✅ SAM 是 CloudFormation 的简化层,专为无服务器场景设计
✅ 支持三种核心资源:Function、Api、SimpleTable
✅ 推荐使用隐式 API 定义 + Globals 提升可读性
✅ 部署流程:package
→ deploy
→ test
→ delete
所有示例代码已上传至 GitHub: 👉 https://github.com/eugenp/tutorials/tree/master/aws-modules/aws-lambda-modules