Chương 36: AWS SAM — Serverless Application Model
Trọng tâm DVA-C02: SAM xuất hiện đều đặn ở domain Deployment (24%) và Development (32%). Đề hay hỏi: phần
Transformbắt buộc trong template, ý nghĩa các resource rút gọn (AWS::Serverless::Function,Api,SimpleTable), khác biệt giữasam build/package/deploy, cáchsam local invokemô phỏng Lambda bằng Docker, policy templates (DynamoDBCrudPolicy...) thay cho viết IAM policy tay, và đặc biệt là canary/linear deployment cho Lambda alias quaDeploymentPreference+ CodeDeploy với hook PreTraffic/PostTraffic và rollback theo CloudWatch alarm. Câu hỏi thường ở dạng "deploy Lambda mới mà giảm rủi ro, tự rollback khi lỗi tăng — chọn gì?".
Mục tiêu chương
- Hiểu SAM là CloudFormation extension (macro
AWS::Serverless-2016-10-31) chứ không phải một dịch vụ riêng, và vì sao nó rút ngắn template serverless tới 80%. - Viết được SAM template với
Globals,Function,Api/HttpApi,SimpleTable,StateMachine; gắn event sources (API, S3, SQS, Schedule). - Thành thạo vòng đời
sam build → package → deploy --guidedvà test cục bộ vớisam local invoke / start-api / start-lambda / generate-event. - Dùng policy templates thay vì viết IAM JSON tay, và biết khi nào phải fallback về policy thường.
- Cấu hình safe deployment cho Lambda:
AutoPublishAlias+DeploymentPreference(Canary/Linear) + CodeDeploy hooks + alarm rollback. - Hiểu SAM Accelerate (
sam sync), SAR (Serverless Application Repository), và quyết định khi nào dùng SAM vs CloudFormation thuần vs CDK (chi tiết CDK ở Chương 37).
36.1 SAM là gì — một macro CloudFormation, không phải dịch vụ riêng
AWS SAM (Serverless Application Model) gồm hai thứ tách biệt mà người mới hay nhầm:
- SAM template specification — một tập cú pháp YAML/JSON mở rộng CloudFormation, khai báo bằng dòng
Transform: AWS::Serverless-2016-10-31. Đây là một CloudFormation macro chạy phía AWS: khi bạn submit stack, CloudFormation gọi transform để "nở" (expand) các resourceAWS::Serverless::*thành tài nguyên CloudFormation thật (AWS::Lambda::Function,AWS::IAM::Role,AWS::ApiGateway::RestApi,AWS::Lambda::Permission...). - SAM CLI — công cụ dòng lệnh (
sam) chạy trên máy bạn để build, test cục bộ bằng Docker, đóng gói và deploy. SAM CLI không bắt buộc — bạn có thể deploy SAM template bằngaws cloudformation deploythuần, nhưng mất các tính năng build/local.
Điểm cốt lõi cho đề thi: SAM template CHÍNH LÀ CloudFormation template. Bạn được phép trộn resource SAM rút gọn với resource CloudFormation đầy đủ trong cùng một file. Mọi intrinsic function (Ref, Fn::GetAtt, Fn::Sub, !ImportValue...) đều dùng được (chi tiết CloudFormation ở Chương 19–20).
Ví dụ kinh điển — một function HTTP đầy đủ chỉ vài dòng:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31 # BẮT BUỘC — biến file thành SAM template
Description: Hello API bằng SAM
Resources:
HelloFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/ # thư mục chứa code
Handler: app.lambdaHandler # file app.js, export lambdaHandler
Runtime: nodejs20.x
Events:
GetHello:
Type: Api # tạo luôn API Gateway + permission
Properties:
Path: /hello
Method: get
Khi transform nở ra, AWS tự sinh: Lambda function, IAM execution role (kèm AWSLambdaBasicExecutionRole), REST API, deployment, stage, và AWS::Lambda::Permission cho API Gateway gọi function. Viết tay CloudFormation cho từng cái này tốn khoảng 80–100 dòng. Đó là toàn bộ giá trị của SAM.
💡 Exam Tip: Nếu thiếu dòng
Transform: AWS::Serverless-2016-10-31, CloudFormation sẽ báo lỗi "Invalid template resource property 'Type'... Unrecognized resource type AWS::Serverless::Function". DòngTransformlà dấu hiệu nhận diện một SAM template trong câu hỏi.
36.2 Globals và các resource type cốt lõi
Section Globals — DRY cho thuộc tính lặp lại
Globals là section chỉ có ở SAM (không phải CloudFormation chuẩn), cho phép đặt thuộc tính dùng chung cho mọi resource cùng loại, tránh lặp lại. Hỗ trợ cho Function, Api, HttpApi, SimpleTable, StateMachine.
Globals:
Function:
Runtime: nodejs20.x
Timeout: 10 # giây — mọi function mặc định 10s nếu không override
MemorySize: 256
Tracing: Active # bật X-Ray active tracing cho TẤT CẢ function
Environment:
Variables:
TABLE_NAME: !Ref OrdersTable
Api:
Cors:
AllowMethods: "'GET,POST,OPTIONS'"
AllowOrigin: "'*'"
Quy tắc merge: thuộc tính ở resource override Globals; với map (như Environment.Variables) thì merge theo key; với danh sách thì resource thay thế hoàn toàn, không nối thêm.
Các resource type SAM cần nhớ
| Resource SAM | Nở ra (CloudFormation) | Dùng khi |
|---|---|---|
AWS::Serverless::Function |
Lambda Function + IAM Role + EventSourceMapping + Permission | Mọi Lambda |
AWS::Serverless::Api |
API Gateway REST API + Deployment + Stage | REST API có VTL, usage plan, WAF |
AWS::Serverless::HttpApi |
API Gateway HTTP API (v2) | API nhẹ, rẻ, latency thấp, JWT auth |
AWS::Serverless::SimpleTable |
DynamoDB table (chỉ primary key, on-demand mặc định) | Table đơn giản, không GSI/LSI |
AWS::Serverless::StateMachine |
Step Functions state machine + IAM role | Workflow (chi tiết Chương 43) |
AWS::Serverless::LayerVersion |
Lambda LayerVersion | Chia sẻ thư viện chung |
AWS::Serverless::Application |
Nested stack từ SAR/file | Tái dùng app từ SAR |
💡 Exam Tip:
SimpleTablechỉ tạo được table với primary key đơn giản (hash + optional range), mặc định billing mode on-demand (PAY_PER_REQUEST). Cần GSI, LSI, stream, hay provisioned capacity tinh chỉnh → dùngAWS::DynamoDB::Tablethường (vẫn hợp lệ trong SAM template).
Api vs HttpApi là điểm dễ bị gài: HTTP API rẻ hơn ~70%, latency thấp hơn, hỗ trợ JWT authorizer native; nhưng REST API mới có request validation, API keys/usage plans, VTL mapping templates, edge-optimized endpoint (so sánh chi tiết ở Chương 35).
36.3 Events — gắn trigger cho Function
Events trong AWS::Serverless::Function là nơi SAM tỏa sáng: khai báo event source, SAM tự tạo cả trigger lẫn IAM permission/resource cần thiết.
Resources:
ProcessorFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/processor/
Handler: index.handler
Events:
# 1. API Gateway
ApiEvent:
Type: Api
Properties: { Path: /orders, Method: post }
# 2. S3 — SAM tự cấu hình bucket notification + permission
S3Upload:
Type: S3
Properties:
Bucket: !Ref UploadBucket
Events: s3:ObjectCreated:*
# 3. SQS — tạo EventSourceMapping, role poll queue
QueueConsumer:
Type: SQS
Properties:
Queue: !GetAtt JobQueue.Arn
BatchSize: 10
# 4. Schedule — EventBridge rule cron/rate
DailyJob:
Type: Schedule
Properties:
Schedule: rate(1 day)
Mỗi Type (Api, HttpApi, S3, SQS, SNS, Kinesis, DynamoDB, Schedule, EventBridgeRule, Cognito...) sinh tài nguyên tương ứng. Với S3, SAM còn xử lý vòng phụ thuộc tuần hoàn (bucket cần biết function, function cần permission từ bucket) — một bài toán khó nếu viết CloudFormation tay.
💡 Exam Tip: Với event
Type: S3, bucket phải được khai báo trong cùng template (SAM cần sửa NotificationConfiguration của bucket). Không thể trỏ event S3 tới bucket đã tồn tại bên ngoài stack — đó là bẫy hay gặp; trường hợp bucket có sẵn, dùng EventBridge hoặc cấu hình notification riêng.
36.4 Policy templates — IAM gọn gàng không cần viết JSON
Mặc định mỗi AWS::Serverless::Function được cấp một execution role với AWSLambdaBasicExecutionRole (ghi log CloudWatch). Để cấp thêm quyền, thay vì viết IAM policy JSON, SAM cung cấp policy templates — các khuôn quyền có sẵn, nhận tham số (thường là tên/ARN resource) và sinh ra least-privilege policy.
OrderFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/
Handler: app.handler
Policies:
# Policy template — chỉ cần tên table
- DynamoDBCrudPolicy:
TableName: !Ref OrdersTable
- SQSPollerPolicy:
QueueName: !GetAtt JobQueue.QueueName
- S3ReadPolicy:
BucketName: !Ref UploadBucket
# Trộn được policy JSON thường khi template không có sẵn
- Statement:
- Effect: Allow
Action: kms:Decrypt
Resource: !GetAtt MyKey.Arn
# Hoặc gắn managed policy ARN
- arn:aws:iam::aws:policy/AmazonSESFullAccess
Các policy template hay gặp trong đề: DynamoDBCrudPolicy, DynamoDBReadPolicy, SQSPollerPolicy, SQSSendMessagePolicy, S3ReadPolicy, S3CrudPolicy, SNSPublishMessagePolicy, KMSDecryptPolicy, SSMParameterReadPolicy, LambdaInvokePolicy, VPCAccessPolicy. Mỗi template ánh xạ sang một bộ action least-privilege — ví dụ DynamoDBCrudPolicy cấp GetItem, PutItem, UpdateItem, DeleteItem, Query, Scan, BatchGet/Write... chỉ trên đúng table ARN (kèm cả index ARN table/*/index/*).
💡 Exam Tip:
Policieslà property của Function. Bạn có thể trộn 3 dạng trong cùng một list: policy template (map có tham số), IAM statement thô (Statement:), và managed policy ARN (chuỗi). Nếu đề hỏi "cách least-privilege gọn nhất để function đọc/ghi DynamoDB", đáp án là DynamoDBCrudPolicy với TableName, không phảiAmazonDynamoDBFullAccess.
36.5 Vòng đời SAM CLI: init → build → package → deploy
sam init
sam init tạo scaffold project từ template mẫu (Hello World, theo runtime). Sinh template.yaml, code mẫu, và file cấu hình.
sam build — biên dịch & gom dependency
sam build # build mọi function, output ra .aws-sam/build/
sam build --use-container # build TRONG Docker giống môi trường Lambda
sam build đọc template.yaml, với mỗi function: cài dependency (npm install, pip install...), copy code vào .aws-sam/build/<FunctionName>/, và sinh một template.yaml mới trong .aws-sam/build/ đã trỏ CodeUri tới thư mục build. --use-container dùng image public.ecr.aws/sam/build-<runtime> để build trong môi trường giống Lambda — bắt buộc khi có native dependency (ví dụ thư viện compile C như bcrypt, sharp) để tránh lỗi "works on my machine".
sam package — upload artifact lên S3
sam package \
--output-template-packaged.yaml packaged.yaml \
--s3-bucket my-sam-artifacts
package zip code, upload lên S3, rồi thay CodeUri: src/ (đường dẫn cục bộ) bằng CodeUri: s3://bucket/key. CloudFormation không đọc được code cục bộ — phải nằm trên S3 (hoặc ECR với container image). Bước này tương đương aws cloudformation package.
sam deploy — tạo/cập nhật stack
sam deploy --guided # lần đầu — hỏi tương tác, lưu vào samconfig.toml
sam deploy # các lần sau dùng lại samconfig.toml
sam deploy thực chất gọi CloudFormation tạo ChangeSet rồi execute. --guided hỏi: stack name, region, có cho phép tạo IAM role không (CAPABILITY_IAM), confirm changeset, lưu config. Kết quả ghi vào samconfig.toml để lần sau chạy sam deploy gọn.
Điểm hay quên: SAM template tạo IAM role nên deploy cần capabilities. SAM CLI tự thêm CAPABILITY_IAM; nếu function đặt tên role rõ ràng cần CAPABILITY_NAMED_IAM; nếu dùng macro/nested cần CAPABILITY_AUTO_EXPAND.
| Lệnh | Làm gì | Tương đương CloudFormation |
|---|---|---|
sam build |
Cài dependency, gom code vào .aws-sam/build |
(không có) |
sam package |
Zip + upload S3, rewrite CodeUri | aws cloudformation package |
sam deploy |
ChangeSet + execute | aws cloudformation deploy |
💡 Exam Tip:
sam deploy(không--guided) tự động chạy package bên trong nếu phát hiện CodeUri cục bộ, nên thực tế chỉ cầnsam build && sam deploy. Nhưng đề có thể tách rõ 3 bước; nhớ thứ tự build → package → deploy và rằng deploy = tạo và execute ChangeSet qua CloudFormation, không phải gọi thẳng Lambda API.
36.6 Test cục bộ: sam local & generate-event
SAM CLI mô phỏng Lambda bằng Docker trên máy bạn — đây là khác biệt lớn so với CloudFormation thuần. Yêu cầu: Docker phải chạy.
# Invoke 1 lần với event giả lập
sam local invoke ProcessorFunction --event events/sqs.json
# Sinh sample event đúng schema của service
sam local generate-event sqs receive-message > events/sqs.json
sam local generate-event s3 put > events/s3.json
sam local generate-event apigateway aws-proxy > events/api.json
# Chạy local API Gateway tại http://127.0.0.1:3000
sam local start-api
# Giả lập Lambda service endpoint để SDK gọi Invoke
sam local start-lambda
sam local invoke: chạy function một lần trong container, đọc event từ--event(hoặc stdin), in kết quả + log. Hữu ích cho event-driven function (SQS/S3/Kinesis).sam local generate-event <service> <event>: in ra JSON mẫu đúng cấu trúc payload thật của service — tránh phải bịa tay event S3/SQS/API.sam local start-api: dựng HTTP server cục bộ map cácEvents: Type: Api/HttpApithành route thật, mỗi request khởi một container.sam local start-lambda: dựng một endpoint giả lập Lambda Invoke API; bạn trỏ AWS SDK tớihttp://127.0.0.1:3001(quaendpoint) để test code gọi Lambda, hoặc dùng cho integration test.
Hạn chế quan trọng cho đề: local emulation không mô phỏng IAM/permission, network VPC, hay quota chính xác; cold start và timing khác production. Nó test logic + tích hợp, không thay thế test trên cloud.
// Gọi local Lambda từ test bằng SDK v3 — trỏ endpoint về start-lambda
import { LambdaClient, InvokeCommand } from "@aws-sdk/client-lambda";
const client = new LambdaClient({
endpoint: "http://127.0.0.1:3001", // sam local start-lambda
region: "us-east-1",
});
const res = await client.send(new InvokeCommand({
FunctionName: "ProcessorFunction",
Payload: Buffer.from(JSON.stringify({ key: "value" })),
}));
console.log(Buffer.from(res.Payload).toString());
💡 Exam Tip:
sam local *cần Docker. Nếu câu hỏi nói "test Lambda cục bộ trước khi deploy, mô phỏng API Gateway endpoint" →sam local start-api. "Sinh event mẫu để test" →sam local generate-event.
36.7 Safe deployment cho Lambda: AutoPublishAlias + DeploymentPreference + CodeDeploy
Đây là section nặng điểm thi nhất của chương. Bài toán: bạn deploy code Lambda mới. Nếu version mới có bug, mọi traffic dính ngay lập tức. SAM giải quyết bằng cách tích hợp CodeDeploy để dịch chuyển traffic dần dần (traffic shifting) trên một alias, kèm rollback tự động theo CloudWatch alarm.
Cơ chế bên dưới: SAM tạo Lambda version mới mỗi lần deploy (qua AutoPublishAlias), gắn alias trỏ tới version đó, rồi tạo một CodeDeploy application + deployment group dùng DeploymentConfig kiểu Canary/Linear để dời routing weight của alias từ version cũ sang version mới (chi tiết CodeDeploy ở Chương 41).
Resources:
ApiFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/
Handler: app.handler
Runtime: nodejs20.x
AutoPublishAlias: live # tạo & quản lý alias "live", publish version mới mỗi deploy
DeploymentPreference:
Type: Canary10Percent5Minutes # 10% traffic trong 5 phút, rồi 100%
Alarms: # rollback nếu alarm vào trạng thái ALARM
- !Ref ApiErrorAlarm
Hooks:
PreTraffic: !Ref PreTrafficHook # Lambda chạy TRƯỚC khi shift traffic
PostTraffic: !Ref PostTrafficHook # Lambda chạy SAU khi shift xong
Events:
Api:
Type: Api
Properties: { Path: /, Method: get }
ApiErrorAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
MetricName: Errors
Namespace: AWS/Lambda
Dimensions:
- Name: Resource
Value: ApiFunction:live # giám sát đúng alias
Statistic: Sum
Period: 60
EvaluationPeriods: 2
Threshold: 1
ComparisonOperator: GreaterThanOrEqualToThreshold
Các kiểu DeploymentPreference.Type
| Type | Hành vi |
|---|---|
Canary10Percent5Minutes |
10% trong 5 phút → 100% |
Canary10Percent30Minutes |
10% trong 30 phút → 100% |
Linear10PercentEvery1Minute |
+10% mỗi 1 phút (10 bước) |
Linear10PercentEvery10Minutes |
+10% mỗi 10 phút |
AllAtOnce |
Dời 100% ngay (không có canary) |
Canary = hai bước nhảy (một phần nhỏ rồi toàn bộ). Linear = tăng đều theo từng nấc. AllAtOnce = không an toàn, dùng cho dev.
Hooks PreTraffic / PostTraffic
Hooks là Lambda function của riêng bạn mà CodeDeploy gọi trong lifecycle deployment:
- PreTraffic: chạy trước khi bất kỳ traffic nào tới version mới — dùng để smoke test version mới (gọi thử, kiểm tra kết quả). Hook phải báo lại kết quả cho CodeDeploy qua
codedeploy.putLifecycleEventHookExecutionStatus(Succeeded/Failed). Failed → hủy deployment, không shift. - PostTraffic: chạy sau khi đã shift 100% — dùng kiểm thử end-to-end, integration test, cleanup.
// PreTraffic hook — bắt buộc gọi putLifecycleEventHookExecutionStatus
import { CodeDeployClient, PutLifecycleEventHookExecutionStatusCommand }
from "@aws-sdk/client-codedeploy";
const cd = new CodeDeployClient({});
export const handler = async (event) => {
const { DeploymentId, LifecycleEventHookExecutionId } = event;
let status = "Succeeded";
try {
// ... smoke test version mới ở đây (invoke, kiểm tra response) ...
} catch (e) {
status = "Failed"; // Failed => CodeDeploy rollback
}
await cd.send(new PutLifecycleEventHookExecutionStatusCommand({
deploymentId: DeploymentId,
lifecycleEventHookExecutionId: LifecycleEventHookExecutionId,
status, // Succeeded | Failed
}));
return status;
};
Rollback qua Alarms
Trong khi traffic đang được shift, nếu bất kỳ alarm nào trong Alarms chuyển sang ALARM, CodeDeploy tự động rollback: dời 100% traffic về version cũ. Đây chính là pattern an toàn mà đề mô tả: "deploy Lambda giảm rủi ro, tự rollback khi error rate tăng".
💡 Exam Tip: Khi câu hỏi nêu "shift traffic gradually to new Lambda version, automatically roll back if errors spike" — đáp án là SAM
DeploymentPreferencevới Canary/Linear + CodeDeploy + CloudWatch Alarms.AutoPublishAliaslà điều kiện bắt buộc đi kèm (phải có alias để CodeDeploy dịch traffic). PreTraffic hook = validate trước khi cho traffic vào; PostTraffic = sau khi đã shift.
36.8 SAM Accelerate (sam sync), SAR và so sánh với CloudFormation thuần
sam sync — vòng lặp dev nhanh (SAM Accelerate)
sam build && sam deploy qua ChangeSet mất hàng phút — quá chậm khi đang code. sam sync (SAM Accelerate) rút ngắn vòng lặp bằng cách bỏ qua ChangeSet và cập nhật trực tiếp khi có thể.
sam sync --watch --stack-name dev-app
--watch: theo dõi file thay đổi, tự đồng bộ.- Nếu chỉ code Lambda đổi →
sam syncgọi thẳng Lambda UpdateFunctionCode API, bỏ qua CloudFormation hoàn toàn (vài giây). - Nếu infrastructure (template) đổi → fallback về CloudFormation deploy.
--codeđể chỉ đồng bộ code, không động tới hạ tầng.
💡 Exam Tip:
sam sync --watchlà công cụ inner-loop development — cập nhật cực nhanh nhưng làm stack drift so với CloudFormation (vì cập nhật trực tiếp Lambda). Không dùng cho production; production deploy bằngsam deploy(qua ChangeSet) trong pipeline.
SAR — Serverless Application Repository
SAR là "kho ứng dụng serverless" công khai/riêng tư. Bạn publish một SAM application (template + code) lên SAR; người khác deploy nó vào account của họ chỉ bằng vài click hoặc tham chiếu trong template qua AWS::Serverless::Application.
Resources:
MyNestedApp:
Type: AWS::Serverless::Application
Properties:
Location:
ApplicationId: arn:aws:serverlessrepo:us-east-1:123456789012:applications/my-app
SemanticVersion: 1.2.0
Parameters:
TableName: orders
SAR application có thể public (ai cũng deploy được) hoặc private/shared trong org. AWS::Serverless::Application thực chất tạo một nested stack từ template của app đó. Đây là cách tái sử dụng kiến trúc serverless đã đóng gói (ví dụ: một app gửi alert Slack, một auth layer).
So sánh SAM vs CloudFormation thuần vs CDK
| Tiêu chí | SAM | CloudFormation thuần | CDK (Chương 37) |
|---|---|---|---|
| Ngôn ngữ | YAML/JSON + macro | YAML/JSON | TypeScript/Python/Java... |
| Độ dài cho serverless | Rất ngắn (transform nở ra) | Dài, lặp | Ngắn, lập trình được |
| Local testing | sam local (Docker) tốt nhất |
Không | Dùng được SAM CLI qua cdk synth |
| Traffic shifting Lambda | DeploymentPreference built-in |
Phải tự ghép CodeDeploy | Construct hoặc dùng SAM |
| Phạm vi | Tập trung serverless | Mọi resource AWS | Mọi resource AWS |
| Phía sau | Là CloudFormation | — | Synth ra CloudFormation |
Kết luận chọn lựa: dùng SAM khi stack chủ yếu là Lambda/API Gateway/DynamoDB/Step Functions và muốn local test + safe deploy nhanh gọn. Dùng CloudFormation thuần khi không phải serverless hoặc cần kiểm soát mọi resource thô. Dùng CDK khi muốn viết hạ tầng bằng ngôn ngữ lập trình thật, có vòng lặp/điều kiện phức tạp, tái dùng construct (CDK vẫn synth ra CloudFormation và có thể test local qua SAM CLI).
💡 Exam Tip: Nhớ ba điều dễ bị gài: (1) SAM template là CloudFormation — trộn resource thường được; (2) SAM không phải dịch vụ deploy riêng, nó dùng CloudFormation bên dưới; (3) thế mạnh độc nhất của SAM so với CloudFormation thuần là local testing (
sam local) và gradual deployment built-in (DeploymentPreference).
Một template hoàn chỉnh gói gọn cả chương
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Runtime: nodejs20.x
Timeout: 15
Tracing: Active
Environment:
Variables: { TABLE: !Ref OrdersTable }
Resources:
OrdersTable:
Type: AWS::Serverless::SimpleTable # DynamoDB on-demand, PK đơn giản
Properties:
PrimaryKey: { Name: orderId, Type: String }
ApiFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/
Handler: app.handler
AutoPublishAlias: live # bật alias cho safe deploy
DeploymentPreference:
Type: Linear10PercentEvery1Minute
Alarms: [ !Ref ErrAlarm ]
Policies:
- DynamoDBCrudPolicy: { TableName: !Ref OrdersTable }
Events:
Post: { Type: Api, Properties: { Path: /orders, Method: post } }
ErrAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
MetricName: Errors
Namespace: AWS/Lambda
Dimensions: [ { Name: Resource, Value: ApiFunction:live } ]
Statistic: Sum
Period: 60
EvaluationPeriods: 1
Threshold: 1
ComparisonOperator: GreaterThanOrEqualToThreshold
Outputs:
ApiUrl:
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/orders"
Template này gói: SimpleTable, Function với least-privilege qua policy template, X-Ray tracing qua Globals, REST API tự sinh, và linear safe deployment có rollback theo alarm — toàn bộ trong ~35 dòng mà CloudFormation thuần cần hàng trăm. Đó là lý do SAM là lựa chọn mặc định cho serverless trên AWS.
Hands-on Lab: Build, deploy và canary một serverless API bằng AWS SAM
Mục tiêu lab: Đi trọn vòng đời một ứng dụng serverless bằng AWS SAM CLI: viết template.yaml dùng Globals, AWS::Serverless::Function, AWS::Serverless::Api và AWS::Serverless::SimpleTable; gắn quyền bằng policy templates (DynamoDBCrudPolicy) thay vì viết IAM thủ công; test cục bộ với sam local invoke và sam local start-api (dùng Docker); deploy guided với sam deploy --guided; bật AutoPublishAlias + DeploymentPreference để CodeDeploy shift traffic kiểu canary kèm PreTraffic hook và CloudWatch alarm rollback; cuối cùng thử sam sync (SAM Accelerate) cho vòng lặp dev nhanh. So sánh chi tiết SAM vs CloudFormation thuần, và CodeDeploy lifecycle hooks chi tiết thuộc Chương 41 — ở đây chỉ dùng ở mức SAM tự sinh.
Chuẩn bị:
- AWS CLI v2 + AWS SAM CLI đã cài (
sam --version), Docker đang chạy (bắt buộc chosam localvàsam build --use-container). - IAM principal có quyền tạo CloudFormation stack, Lambda, API Gateway, DynamoDB, IAM role, S3 (bucket staging), CodeDeploy. Region lab:
us-east-1.
export AWS_REGION=us-east-1
export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
sam --version && docker info >/dev/null && echo "SAM + Docker OK"
Bước 1: Khởi tạo project và đọc cấu trúc
sam init --name lab36-api --runtime nodejs20.x --architecture x86_64 \
--app-template hello-world --package-type Zip
cd lab36-api
sam init sinh khung: template.yaml, thư mục code hello-world/, và samconfig.toml (sẽ tạo khi deploy guided). Ta sẽ thay template.yaml bằng bản đầy đủ hơn ở bước sau.
Bước 2: Viết template SAM hoàn chỉnh
Tạo template.yaml. Lưu ý dòng Transform: AWS::Serverless-2016-10-09 — đây là thứ biến file này thành SAM template: CloudFormation chạy macro Transform để nở (expand) các resource AWS::Serverless::* thành resource CloudFormation gốc (Lambda function, IAM role, API Gateway, DynamoDB table...) trước khi tạo stack.
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-09
Description: lab36 - serverless API voi canary deploy
Globals: # ap dung cho MOI Serverless::Function ben duoi
Function:
Runtime: nodejs20.x
Timeout: 10
MemorySize: 256
Environment:
Variables:
TABLE_NAME: !Ref ItemsTable
Resources:
ItemsTable:
Type: AWS::Serverless::SimpleTable # tao DynamoDB table PK 'id', PAY_PER_REQUEST
Properties:
PrimaryKey:
Name: id
Type: String
PutItemFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello-world/
Handler: app.handler
AutoPublishAlias: live # tao version moi + alias 'live' moi lan deploy
DeploymentPreference:
Type: Canary10Percent5Minutes # 10% traffic 5 phut, roi 100%
Hooks:
PreTraffic: !Ref PreHookFunction
Alarms:
- !Ref ErrorsAlarm # alarm bao dong -> CodeDeploy rollback
Policies:
- DynamoDBCrudPolicy: # POLICY TEMPLATE - khong viet IAM tay
TableName: !Ref ItemsTable
Events:
Api:
Type: Api
Properties:
Path: /items
Method: post
PreHookFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hooks/
Handler: pretraffic.handler
Policies:
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action: codedeploy:PutLifecycleEventHookExecutionStatus
Resource: '*'
ErrorsAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: lab36-put-errors
Namespace: AWS/Lambda
MetricName: Errors
Dimensions:
- Name: Resource
Value: !Sub "${PutItemFunction}:live"
- Name: FunctionName
Value: !Ref PutItemFunction
Statistic: Sum
Period: 60
EvaluationPeriods: 1
Threshold: 1
ComparisonOperator: GreaterThanOrEqualToThreshold
TreatMissingData: notBreaching
Outputs:
ApiUrl:
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/items"
So với CloudFormation thuần: chỉ resource
PutItemFunctionnày khi nở ra sẽ thành ~8 resource gốc (function, version, alias, execution role, API, deployment, stage, CodeDeploy DeploymentGroup, permission). SAM viết ~25 dòng thay cho ~200 dòng CFN — đó là giá trị cốt lõi của Transform.
Bước 3: Viết code handler và PreTraffic hook
mkdir -p hooks
cat > hello-world/app.js <<'EOF'
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const { DynamoDBDocumentClient, PutCommand } = require("@aws-sdk/lib-dynamodb");
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
exports.handler = async (event) => {
const body = JSON.parse(event.body || "{}");
const id = body.id || Date.now().toString();
await ddb.send(new PutCommand({ TableName: process.env.TABLE_NAME, Item: { id, ...body } }));
return { statusCode: 200, body: JSON.stringify({ saved: id }) };
};
EOF
cat > hooks/pretraffic.js <<'EOF'
// PreTraffic hook: chay TRUOC khi shift traffic. Phai bao status ve CodeDeploy.
const { CodeDeployClient, PutLifecycleEventHookExecutionStatusCommand } =
require("@aws-sdk/client-codedeploy");
const cd = new CodeDeployClient({});
exports.handler = async (event) => {
// ... o day ban chay smoke test goi version moi truoc khi mo traffic ...
const status = "Succeeded"; // hoac "Failed" de chan deploy
await cd.send(new PutLifecycleEventHookExecutionStatusCommand({
deploymentId: event.DeploymentId,
lifecycleEventHookExecutionId: event.LifecycleEventHookExecutionId,
status,
}));
return status;
};
EOF
Bẫy kinh điển: nếu PreTraffic/PostTraffic hook không gọi
PutLifecycleEventHookExecutionStatus, CodeDeploy treo cho tới khi timeout rồi coi như Failed và rollback. Đây là lý do số 1 khiến canary deploy "kẹt" trong thực tế.
Bước 4: Build
sam build --use-container
sam build đọc template.yaml, với mỗi function chạy build theo runtime (Node.js: copy code + npm install dependencies), đặt artifact vào .aws-sam/build/, và sinh template.yaml đã chuẩn hoá trong đó. --use-container build trong Docker image giống môi trường Lambda (quan trọng khi có native dependency). Output mong đợi kết thúc bằng Build Succeeded kèm gợi ý sam local invoke / sam deploy --guided.
Bước 5: Test cục bộ với sam local
generate-event tạo payload mẫu cho nhiều nguồn sự kiện; local invoke chạy function trong Docker, không cần deploy:
sam local generate-event apigateway aws-proxy --body '{"id":"a1","name":"test"}' \
> event.json
sam local invoke PutItemFunction --event event.json
Chạy cả API cục bộ (mô phỏng API Gateway tại http://127.0.0.1:3000):
sam local start-api & # chay nen
sleep 5
curl -s -XPOST http://127.0.0.1:3000/items -d '{"id":"local-1"}'
kill %1
sam localmô phỏng Lambda + API Gateway nhưng gọi tới AWS thật cho các service khác (DynamoDB...) bằng credentials hiện hành. Muốn DynamoDB cũng cục bộ thì trỏ endpoint tới DynamoDB Local (Chương 33).sam local start-lambdadựng một endpoint giả lập Lambda service để SDK test gọi vào — khác vớistart-api.
Bước 6: Deploy guided
sam deploy --guided
Trả lời prompt: Stack name lab36-api, Region us-east-1, Confirm changes before deploy = Y, Allow SAM CLI IAM role creation = Y (SAM cần CAPABILITY_IAM vì nó tạo IAM role), lưu vào samconfig.toml. SAM sẽ: đóng gói code, upload lên S3 bucket staging (aws-sam-cli-managed-default-... tự tạo), tạo ChangeSet, hiển thị diff, rồi thực thi. Lần deploy đầu, DeploymentPreference chưa shift gì (chưa có version cũ) — alias live trỏ thẳng version 1.
API_URL=$(aws cloudformation describe-stacks --stack-name lab36-api \
--query "Stacks[0].Outputs[?OutputKey=='ApiUrl'].OutputValue" --output text)
curl -s -XPOST "$API_URL" -d '{"id":"prod-1","name":"hi"}'
# mong doi: {"saved":"prod-1"}
Bước 7: Trigger một canary deploy và quan sát rollback
Sửa code (ví dụ đổi message trả về) rồi deploy lại không guided:
sed -i.bak 's/saved/stored/' hello-world/app.js
sam build && sam deploy
Lần này có version cũ → CodeDeploy tạo deployment kiểu Canary10Percent5Minutes: alias live cho 10% traffic sang version mới trong 5 phút, chạy PreTraffic hook trước. Theo dõi:
aws deploy list-deployments --query 'deployments[0]' --output text
# Lay deployment-id roi:
aws deploy get-deployment --deployment-id <id> \
--query 'deploymentInfo.status' # InProgress -> Succeeded
Nếu trong 5 phút canary mà ErrorsAlarm chuyển ALARM (có ≥1 lỗi), CodeDeploy tự rollback alias live về version cũ và đánh dấu deployment Stopped/Failed. Đây chính là điểm thi hay hỏi: SAM + CodeDeploy + CloudWatch alarm = safe deployment tự động.
Bước 8 (tuỳ chọn): sam sync — SAM Accelerate
Cho vòng lặp dev nhanh, sam sync --watch theo dõi file và đẩy thẳng thay đổi code (qua UpdateFunctionCode) không qua CloudFormation ChangeSet, chỉ rơi về deploy hạ tầng khi template.yaml đổi:
sam sync --stack-name lab36-api --watch
# Sua hello-world/app.js -> SAM tu update code trong ~giay, khong tao changeset
# Ctrl+C de thoat
sam syncbỏ qua an toàn của ChangeSet/rollback để đổi lấy tốc độ → chỉ dùng cho dev/test stack, KHÔNG dùng production. Đề có thể hỏi "công cụ nào tăng tốc inner-loop development cho serverless" →sam sync/ SAM Accelerate.
Dọn dẹp tài nguyên
sam delete --stack-name lab36-api --no-prompts
# sam delete xoa stack + artifact tren S3 staging do SAM quan ly
rm -f event.json hello-world/app.js.bak
sam deletegọicloudformation delete-stackrồi dọn artifact S3. DynamoDB table tạo qua SimpleTable bị xoá theo stack (mặc định không Retain) — đảm bảo không còn data quan trọng. CloudWatch alarm và CodeDeploy application/deployment group cũng do stack quản nên xoá theo.
💡 Exam Tips chương 36
Transform: AWS::Serverless-2016-10-09là dòng BẮT BUỘC nhận diện SAM template. Không có nó, CloudFormation không hiểuAWS::Serverless::*. SAM là superset của CloudFormation — mọi resource CFN gốc đều dùng được trong SAM template.AWS::Serverless::Functionnở ra: Lambda function + execution role + (nếu cóEvents) trigger/permission + (nếu cóAutoPublishAlias) version, alias, CodeDeploy. Biết nó nở ra cái gì là điểm thi.- Policy templates (
DynamoDBCrudPolicy,SQSPollerPolicy,S3ReadPolicy,SNSPublishMessagePolicy...) sinh IAM policy least-privilege gắn vào execution role — tránh viết IAM tay. Hay hỏi "cách đơn giản nhất cấp quyền least-privilege cho Lambda trong SAM". sam buildđóng gói code + dependencies vào.aws-sam/build/.sam deploy --guidedlần đầu tạo S3 bucket staging +samconfig.toml; nó luôn upload code lên S3 trước khi tạo stack (zip > 50 MB không upload trực tiếp được — Chương 28).sam local invoke/start-api/start-lambdacần Docker.start-apimô phỏng API Gateway,start-lambdadựng endpoint giả Lambda service.sam local generate-event <service>tạo payload mẫu (s3, sqs, apigateway...).AutoPublishAlias: live= mỗi deploy publish version mới và trỏ alias; là TIỀN ĐỀ đểDeploymentPreference(CodeDeploy traffic shifting) hoạt động. Không có alias thì không canary được.DeploymentPreference.Type:Canary10Percent5Minutes(10% rồi 100% sau 5 phút),Linear10PercentEvery1Minute,AllAtOnce. KèmAlarmsđể auto-rollback vàHooks(PreTraffic/PostTraffic Lambda) để smoke test.- Hook bắt buộc gọi
codedeploy:PutLifecycleEventHookExecutionStatusbáoSucceeded/Failed; quên gọi → deploy treo tới timeout rồi rollback. sam sync/ SAM Accelerate: tăng tốc inner-loop dev bằng cách đẩy thẳng code (bỏ ChangeSet). Chỉ dùng dev/test, không production.sam deploycần capabilities: SAM tự tạo IAM role nên cầnCAPABILITY_IAM(hoặcCAPABILITY_NAMED_IAMnếu đặt tên role); nếu template có macro/nested →CAPABILITY_AUTO_EXPAND.- SAR — Serverless Application Repository: nơi publish/discover ứng dụng serverless đóng gói SAM; nhúng app công khai bằng
AWS::Serverless::Application. Khác với việc tự deploy stack. AWS::Serverless::SimpleTablechỉ tạo DynamoDB table khoá đơn giản (PK, tuỳ chọn SK qua... không có — chỉ PK); cần GSI/LSI/stream phải dùngAWS::DynamoDB::Tableđầy đủ.
Quiz chương 36 (10 câu)
Câu 1. Dòng nào BẮT BUỘC để CloudFormation hiểu các resource AWS::Serverless::Function?
- A.
AWSTemplateFormatVersion: '2010-09-09' - B.
Transform: AWS::Serverless-2016-10-09 - C.
Type: AWS::Lambda::Function - D.
Globals:block
Câu 2. Một developer cần cấp cho Lambda quyền read/write một DynamoDB table với least-privilege, ít công nhất trong SAM. Cách nào?
- A. Viết IAM policy JSON đầy đủ trong
Policies - B. Dùng policy template
DynamoDBCrudPolicyvớiTableName - C. Gắn managed policy
AmazonDynamoDBFullAccess - D. Tạo IAM user và embed access key vào env var
Câu 3. Lệnh sam local start-api báo lỗi không kết nối được. Nguyên nhân thường gặp nhất?
- A. Chưa chạy
sam deploy - B. Docker không chạy
- C. Region chưa cấu hình
- D. Thiếu
samconfig.toml
Câu 4. Developer muốn deploy Lambda dịch chuyển 10% traffic sang version mới trong 5 phút, tự rollback nếu lỗi. Cấu hình SAM nào?
- A.
DeploymentPreference.Type: AllAtOnce - B.
AutoPublishAlias+DeploymentPreference.Type: Canary10Percent5Minutes+Alarms - C. Chỉ cần
AutoPublishAlias: live - D. Đặt
ReservedConcurrentExecutions
Câu 5. Một canary deployment bằng SAM/CodeDeploy bị "kẹt" ở trạng thái InProgress rồi rollback dù code đúng. Có PreTraffic hook. Nguyên nhân khả dĩ nhất?
- A. Alarm chưa cấu hình
- B. Hook function không gọi
PutLifecycleEventHookExecutionStatus - C. Memory function quá thấp
- D. Thiếu
AutoPublishAlias
Câu 6. sam deploy trả lỗi Requires capabilities: [CAPABILITY_IAM]. Vì sao?
- A. Template sai cú pháp
- B. SAM tạo IAM role nên phải xác nhận capability
- C. Region không hỗ trợ
- D. Thiếu S3 bucket
Câu 7. Đội dev muốn rút ngắn vòng lặp sửa code → thấy thay đổi trên AWS, không chờ ChangeSet mỗi lần. Công cụ SAM nào?
- A.
sam build --use-container - B.
sam sync --watch(SAM Accelerate) - C.
sam local invoke - D.
sam package
Câu 8. sam build thực hiện việc gì?
- A. Tạo CloudFormation stack
- B. Đóng gói code + dependencies vào
.aws-sam/build/và chuẩn hoá template - C. Shift traffic theo canary
- D. Xoá stack cũ
Câu 9. Developer cần một DynamoDB table có GSI và stream trong template SAM. Resource nào phù hợp?
- A.
AWS::Serverless::SimpleTable - B.
AWS::DynamoDB::Table - C.
AWS::Serverless::Function - D.
AWS::Serverless::Api
Câu 10. Tổ chức muốn publish một ứng dụng serverless đóng gói SAM để các team khác discover và deploy lại. Dịch vụ nào?
- A. AWS CodeArtifact
- B. AWS Serverless Application Repository (SAR)
- C. Amazon ECR
- D. AWS Service Catalog Connector
Đáp án & giải thích
Câu 1 — Đáp án B. Transform: AWS::Serverless-2016-10-09 kích hoạt macro SAM để nở AWS::Serverless::* thành resource CFN gốc. A là phiên bản format của mọi template CFN, không đặc trưng SAM. C là resource Lambda thô (không phải dòng kích hoạt). D là tuỳ chọn, không bắt buộc.
Câu 2 — Đáp án B. Policy template DynamoDBCrudPolicy sinh đúng các action CRUD trên đúng table ARN — least-privilege, một dòng. A đúng kỹ thuật nhưng nhiều công nhất, dễ sai ARN. C vi phạm least-privilege (full access mọi table). D là anti-pattern bảo mật, không bao giờ embed key.
Câu 3 — Đáp án B. sam local * chạy function trong container nên Docker bắt buộc; Docker không chạy là lỗi phổ biến nhất. A sai: local không cần deploy. C/D không gây lỗi kết nối local API (region/samconfig liên quan deploy).
Câu 4 — Đáp án B. Canary cần AutoPublishAlias (tạo version+alias) làm nền, DeploymentPreference.Type: Canary10Percent5Minutes để CodeDeploy shift, và Alarms để auto-rollback. A shift toàn bộ ngay, không canary. C mới chỉ publish alias, chưa shift theo % hay rollback. D không liên quan traffic shifting.
Câu 5 — Đáp án B. Hook PHẢI gọi PutLifecycleEventHookExecutionStatus báo Succeeded; nếu không, CodeDeploy chờ tới timeout, coi như Failed và rollback — đúng triệu chứng. A sai: đề nói có hook, và thiếu alarm không gây kẹt. C không gây treo deployment. D thì sẽ không có canary ngay từ đầu chứ không "kẹt".
Câu 6 — Đáp án B. SAM tạo IAM execution role nên CloudFormation yêu cầu xác nhận CAPABILITY_IAM (hoặc CAPABILITY_NAMED_IAM). A sai: lỗi cú pháp báo khác. C/D không phải nguyên nhân của thông báo capabilities này.
Câu 7 — Đáp án B. sam sync --watch (SAM Accelerate) đẩy thẳng code qua UpdateFunctionCode, bỏ ChangeSet, rút ngắn inner-loop dev. A chỉ build. C test cục bộ một lần, không sync lên AWS liên tục. D chỉ đóng gói/upload, không deploy nhanh.
Câu 8 — Đáp án B. sam build cài dependencies và đóng gói artifact vào .aws-sam/build/, sinh template chuẩn hoá để deploy. A là sam deploy. C là việc của CodeDeploy khi deploy. D là sam delete.
Câu 9 — Đáp án B. SimpleTable chỉ tạo table khoá đơn giản, không hỗ trợ GSI/LSI/stream → phải dùng AWS::DynamoDB::Table đầy đủ (SAM cho phép trộn resource CFN gốc). A không đủ. C/D là function/api, không phải table.
Câu 10 — Đáp án B. SAR là kho publish/discover ứng dụng serverless đóng gói bằng SAM; nhúng lại qua AWS::Serverless::Application. A quản lý package npm/pip (Chương 42). C cho container image. D không phải dịch vụ publish app serverless.
Tóm tắt chương
- SAM là framework IaC chuyên serverless, là superset của CloudFormation; dòng
Transform: AWS::Serverless-2016-10-09biến template thành SAM và nở các resource ngắn gọn thành CFN gốc khi deploy. - Resource cốt lõi:
AWS::Serverless::Function,::Api,::HttpApi,::SimpleTable,::StateMachine;Globalsđặt thuộc tính dùng chung (Runtime, Timeout, Memory, Env) cho mọi function. Events(Api/HttpApi, S3, SQS, SNS, Schedule...) khai báo trigger ngay trong function; SAM tự tạo permission/integration tương ứng.- Policy templates (
DynamoDBCrudPolicy,SQSPollerPolicy,S3ReadPolicy...) cấp IAM least-privilege gọn gàng — cách ưu tiên để cấp quyền cho function. - Vòng đời CLI:
sam build(đóng gói vào.aws-sam/build/) →sam deploy --guided(upload S3 staging, tạo ChangeSet, cầnCAPABILITY_IAM) →sam delete. sam local invoke/start-api/start-lambdatest cục bộ qua Docker;sam local generate-eventsinh payload mẫu cho nhiều nguồn sự kiện.- Safe deployment:
AutoPublishAliastạo version+alias;DeploymentPreference(Canary/Linear/AllAtOnce) để CodeDeploy shift traffic;Hooks(PreTraffic/PostTraffic) smoke test;Alarmsauto-rollback. - Lifecycle hook PHẢI gọi
codedeploy:PutLifecycleEventHookExecutionStatus; quên gọi khiến deploy treo rồi rollback — bẫy thực tế hàng đầu. sam sync(SAM Accelerate) đẩy thẳng code bỏ ChangeSet, tăng tốc inner-loop dev — chỉ cho dev/test, không production.- SAR (Serverless Application Repository) để publish/discover và tái dùng ứng dụng SAM qua
AWS::Serverless::Application. - So với CloudFormation thuần: SAM viết ngắn hơn nhiều lần cho serverless và tích hợp sẵn traffic shifting; vẫn dùng được mọi resource CFN gốc khi cần (ví dụ DynamoDB có GSI/stream dùng
AWS::DynamoDB::Table). - CodeDeploy lifecycle hooks và deployment config chi tiết ở Chương 41; CDK (so sánh với SAM) ở Chương 37.