ကျွန်တော်တို့ SSR (Server-Side Rendering) ပါ တဲ့ application တွေ ကို AWS ပေါ်မှာ serverless run ချင်တဲ့အခါမှာ Lambda ကိုသုံးလို့ရပါတယ်။ ပုံမှန်ဆိုရင်တော့ serverless framework ကိုသုံးပြီး deploy လိုက်တာက မြန်မြန်ဆန်ဆန်နဲ့ အလုပ်ရှုပ်သက်သာပါတယ်။ Serverless v4 က စပြီး ဝယ်သုံးဖို့လိုလာပါပြီ free က လည်း 2 credits ပဲ ပေးသုံးတာဖြစ်တဲ့အတွက် uat + dev မှာတင် လမ်းဆုံးပြီး production အတွက်က ဝယ်သုံးရမလိုဖြစ်နေပါပြီ။ Serverless v3 လည်း 2024 ကုန်ရင် security support end တော့မှာဆိုတော့ ရေရှည်မှာ အဆင်မပြေတဲ့အတွက် ကျွန်တော်ကတော့ SST နဲ့ AWS SAM တို့ထဲက တခုခုကို ပြောင်းသုံးဖို့ စဉ်းစားထားပါတယ်။ ရုံးမှာ ရှိနေပြီးသား NUXT နဲ့ရေးထားတဲ့ Frontend app တစ်ခုကို SAM နဲ့ deploy လုပ်ခဲ့တဲ့ အဆင့်လေးတွေကို ပြန်ပြီး မျှဝေပေးချင်ပါတယ်။ SST အကြောင်းကိုတော့ နောက်ကြုံမှ ထပ်ရေးပေးပါအုံးမယ်။
Build nuxt app with aws-lambda preset
အရင်ဆုံး build လိုက်တဲ့ output က Lambda ပေါ်မှာ တင် run လို့ရအောင် ပြင်ဖို့လိုပါတယ်။
nuxt build လိုက်တဲ့အခါ်မှာ nitro ရဲ့ default set က node-server
ဖြစ်တဲ့အတွက် lambda ပေါ်ဒီအတိုင်း တင် run လို့ မရပါဘူး။ အဲ့တော့ ကျွန်တော်တို့ NITRO_PRESET ကို ပြောင်းပြီး build ကြည့် ပါမယ်။
NITRO_PRESET=aws-lambda npm run build
ဒါဆိုရင်တော့ ကျွန်တော်တို့ build လိုက်တဲ့ output က lambda ပေါ်မှာ run လို့ရတဲ့ output လေး ထွက်လာပါပြီ။ nitro preset မှာ aws-lambda လို့ ပြနေပါတယ်။
nuxt build လိုက်တဲ့ output တွေက default အတိုင်းဆို .output
dir ထဲမှာ သိမ်းပါတယ်။ .output
ထဲမှာ ကြည့်လိုက်ရင် folder နှစ်ခု တွေ့ရမှာပါမယ်။ public
folder က public asset တွေသိမ်းတာ ဖြစ်ပြီး server
folder ကတော့ lambda ပေါ်တင် run ရမဲ့ build ပြီးသား script ဖိုင်တွေဖြစ်ပါတယ်။ nuxt.config.ts
ဖိုင် ထဲမှာ ပြင်ပြီး public
နဲ့ server
ကို folder တစ်ခုတည်း ပေါင်းထုတ်ခိုင်းလို့လည်းရပါတယ်။ အဲ့တာဆိုရင်တော့ cloudfront ကနေ အသေးစိတ် handle လုပ်စရာမလိုပဲနဲ့ အကုန်လုံးကို lambda ပေါ်ကို ပေါင်းပြီးတင်လိုက်လို့ရတဲ့အတွက် ပိုရှင်းလင်းပြီး လွယ်ကူပါတယ်။ ဒါပေမဲ့ ဒီနည်းလမ်းက production မှာ မသုံးသင့်ပါဘူး။ static file တွေကို request လုပ်တိုင်း lambda function ထ run ရမယ်ဆိုရင် လုံးဝ မတန်ပါဘူး။ ဒါ့ကြောင့် public assess တွေကို s3 ပေါ်တင်သုံးတာက ပို သင့်တော်ပါတယ်။
Creating Basic SAM Template
ကျွန်တော်တို့ SAM template file စရေးကြည့်ပါမယ်။ အရင်ဆုံး ထုံစံအတိုင်း env variables တွေ နဲ့ parameters တွေကို ရေးလိုက်ပါမယ်။
#template.yaml
---
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: SAM template for nuxt-lambda
Globals:
Function:
Runtime: nodejs18.x
Timeout: 15
Environment:
Variables:
NODE_ENV: !Ref NodeEnv
NUXT_PUBLIC_ROOT_API: !Ref NuxtPublicRootApi
NUXT_APP_URL: !Ref NuxtAppUrl
Parameters:
Stage:
Type: String
Default: dev
S3Bucket:
Type: String
NodeEnv:
Type: String
Default: production
NuxtPublicRootApi:
Type: String
NuxtAppUrl:
Type: String
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: "Application Configuration"
Parameters:
- Stage
- NodeEnv
- NuxtPublicRootApi
- NuxtAppUrl
ParameterLabels:
Stage:
default: "Deployment Stage"
NodeEnv:
default: "Node Environment"
NuxtPublicRootApi:
default: "Nuxt Public Root API"
NuxtAppUrl:
default: "Nuxt App URL"
Resources:
Outputs:
Resources
နောက်တဆင့်မှာ Resource တွေထည့်ပါမယ်။ ကျွန်တော်တို့မှာ cloudFront distribution
, S3 Bucket
, API Gateway
, နဲ့ Lambda Function
ဆိုပြီး basic resource လေးခုထည့်ဖို့လိုပါမယ်။ နောက်ထပ် additional resources တွေအနေနဲ့ cloudfront ကနေ s3 ကို access ရဖို့အတွက် s3 bucket policy
နဲ့ CloudFront OAC
တို့ကို ထည့်ပေးဖို့လိုပါမယ်။ နောက်တဆင့်ကြရင်တော့ resource တစ်ခုခြင်းဆီအတွက် detail လေးတွေ ဆက်ရေးကြပါမယ်။
Resources:
# HttpApi to trigger the Lambda function
HttpApi:
# Lambda function to serve the Nuxt.js application
LambdaFunction:
# S3 bucket to store the Nuxt.js app's static assets
StaticBucket:
# S3 bucket policy to allow CloudFront to access the static assets
StaticBucketPolicy:
# CloudFront OAC to restrict access to the S3 bucket
CloudFrontOriginAccessControl:
# CloudFront distribution to serve the Nuxt.js app
CloudFrontDistribution:
HttpApi:
API GateWay အတွက် ကတော့ ဒီလိုလေးပဲ create လုပ်ပေးလိုက်ရင် လုံလောက်ပါတယ်။
# HttpApi to trigger the Lambda function
HttpApi:
Type: AWS::Serverless::HttpApi
LambdaFunction:
Lambda function မှာတော့ လိုအပ်တဲ့ properties တွေနည်းနည်းများပါမယ်။ ဒီအထဲက အချို့ကိုရှင်းပြရရင်တော့
BuildProperties ကတော့ lambda ပေါ်ကိုတင်တဲ့အခါမှာ မလိုအပ်တဲ့ ဖိုင်တွေပါမသွားပဲ
.output/server/
ထဲက ဖိုင်တွေပဲ ပါသွားဖို့အတွက်ဖြစ်ပါတယ်။Event > ProxyResource ကတော့ အပေါ်မှာ create လုပ်ခဲ့တဲ့ HttpApi နဲ့ integrate လုပ်ဖို့ဖြစ်ပါတယ်။
# Lambda function to serve the Nuxt.js application
LambdaFunction:
Type: AWS::Serverless::Function
Metadata:
BuildMethod: nodejs18.x
BuildProperties:
Exclude:
- '**/*'
Include:
- '.output/server/**'
Properties:
Handler: index.handler
CodeUri: .output/server
MemorySize: 256
Policies:
- AWSLambdaBasicExecutionRole
Events:
ProxyResource:
Type: HttpApi
Properties:
ApiId: !Ref HttpApi
Path: $default
Method: ANY
StaticBucket:
static file တွေ သိမ်းဖို့အတွက် s3 bucket လေး create လုပ်ပါမယ်။
# S3 bucket to store the Nuxt.js app's static assets
StaticBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
BucketName: !Ref S3Bucket
OwnershipControls:
Rules:
- ObjectOwnership: BucketOwnerPreferred
StaticBucketPolicy:
CloudFront နေဝင်လာတဲ့ request တွေကို access လုပ်ဖို့အတွက် s3 bucket မှာ policy လေးထည့်ပေးဖို့လိုပါတယ်။
# S3 bucket policy to allow CloudFront to access the static assets
StaticBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref StaticBucket
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: "cloudfront.amazonaws.com"
Action: "s3:GetObject"
Resource: !Sub "${StaticBucket.Arn}/*"
Condition:
StringEquals:
"AWS:SourceArn": !Sub "arn:aws:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution}"
CloudFrontOriginAccessControl:
CloudFront ကနေ s3 ကို OAC နဲ့ လှမ်းခေါ်လို့ရဖို့ OAC တစ်ခု ကြိုပြီး create လုပ်ပါမယ်။
# CloudFront OAC to restrict access to the S3 bucket
CloudFrontOriginAccessControl:
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Name: !Sub "${AWS::StackName}-OAC"
SigningProtocol: sigv4
SigningBehavior: always
OriginAccessControlOriginType: s3
CloudFrontDistribution:
နောက်ဆုံးအနေနဲ့ CloudFront distribution create လုပ်ပါမယ်။ ကျွန်တော်တို့ရဲ့ distribution ထဲမှာ origin နှစ်ခုလိုပါမယ်။ တစ်ခုက S3 ဖြစ်ပြီး နောက်တစ်ခုကတော့ HttpApi ပါ။ Default Cache Behavior ထဲမှာတော့ Target Origin အဖြစ်နဲ့ HttpApi ကိုသုံးပါမယ်။ static file တွေကို request လုပ်တဲ့အခါမှာ S3 ကို forward လုပ်ပေးဖို့အတွက် Cache Behaviors တွေ ထပ်ထည့်ရပါမယ်။ public folder ထဲမှာ _nuxt
, images
folder နှစ်ခု နဲ့ favicon.ico
ဖိုင် တခုရှိတဲ့အတွက် Cache behaviors စုစုပေါင်း သုံးခုလိုအပ်ပါတယ်။ ဒါကတော့ ကိုယ့် app ရဲ့ public folder ထဲမှာ ဘယ်နှစ်ခုရှိိလဲဆိုတာပေါ် မူတည်ပြီး ရေးဖို့လိုပါတယ်။
Aliases (Alternate domain name) နဲ့ ViewerCertificate certificate ကိုတော့ domain setup မလုပ်ချင်တော့လို့ comment ပိတ်ခဲ့ပါမယ်။ သုံးချင်ရင် comment ဖွင့်ပြီး နည်းနည်းပြင်သုံးလို့ရပါတယ်။
# CloudFront distribution to serve the Nuxt.js app
CloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Enabled: true
# Aliases:
# - !Ref CloudFrontDomain
# ViewerCertificate:
# AcmCertificateArn: !Ref CloudFrontCertificate
# SslSupportMethod: sni-only
Comment: !Sub "${AWS::StackName} ${Stage} Nuxt App"
CustomErrorResponses:
- ErrorCachingMinTTL: 0
ErrorCode: 500
- ErrorCachingMinTTL: 0
ErrorCode: 504
Origins:
- Id: SsrOrigin
# DomainName: !Select [2, !Split ["/", !Ref LambdaFunctionUrl]]
DomainName: !Sub "${HttpApi}.execute-api.${AWS::Region}.amazonaws.com"
OriginPath: ""
CustomOriginConfig:
HTTPSPort: 443
OriginProtocolPolicy: https-only
OriginSSLProtocols:
- TLSv1.2
- Id: S3Origin
# DomainName: !GetAtt StaticBucket.DomainName
DomainName: !Sub "${StaticBucket}.s3.${AWS::Region}.amazonaws.com"
OriginAccessControlId: !Ref CloudFrontOriginAccessControl
S3OriginConfig: {}
DefaultCacheBehavior:
TargetOriginId: SsrOrigin
ViewerProtocolPolicy: redirect-to-https
AllowedMethods:
- GET
- HEAD
- OPTIONS
- PUT
- PATCH
- POST
- DELETE
CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # CachingDisabled
OriginRequestPolicyId: b689b0a8-53d0-40ab-baf2-68738e2966ac # AllViewerExceptHostHeader
Compress: true
CacheBehaviors:
- PathPattern: "/_nuxt/*"
TargetOriginId: S3Origin
ViewerProtocolPolicy: redirect-to-https
AllowedMethods:
- GET
- HEAD
- OPTIONS
Compress: true
CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6 # CachingOptimized
OriginRequestPolicyId: 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf # CORS-S3Origin
- PathPattern: "/images/*"
TargetOriginId: S3Origin
ViewerProtocolPolicy: redirect-to-https
AllowedMethods:
- GET
- HEAD
- OPTIONS
Compress: true
CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6 # CachingOptimized
OriginRequestPolicyId: 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf # CORS-S3Origin
- PathPattern: "/favicon.ico"
TargetOriginId: S3Origin
ViewerProtocolPolicy: redirect-to-https
AllowedMethods:
- GET
- HEAD
- OPTIONS
Compress: true
CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6 # CachingOptimized
OriginRequestPolicyId: 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf # CORS-S3Origin
Outputs:
Resource တွေအကုန်ထည့်ပြီးတဲ့အခါမှာတော့ Output ထုတ်ပါမယ်။ ကျန်တာကိုတော့ အထွေအထူး မထုတ်တော့ပဲ cloudfront distribution domain တစ်ခုပဲ ထုတ်ကြည့်လိုက်ပါမယ်။
Outputs:
CloudFrontDistributionDomainName:
Value: !GetAtt CloudFrontDistribution.DomainName
Description: Domain name of the CloudFront distribution
Deploy commands
ကျွန်တော်တို့ template file ရေးပြီးပြီဆိုရင်တော့ အရင်ဆုံး validate လုပ်ကြည့်ပါမယ်
sam validate --lint
validate လုပ်ကြည့်လို့ အတင်ပြေတယ်ဆိုရင်တော့ deploy လို့ရပါပြီ။ deploy မလုပ်ခင် build ထားပြီးသားဖြစ်ဖို့တော့ လိုပါမယ်နော်။ deploy တဲ့အခါမှာ guided mode နဲ့ deploy ဖို့အတွက် sam deploy -g
command ကို သုံးပါမယ်။
NUXT_NITRO_PRESET=aws-lambda npm run build
sam deploy -g
တစ်ခုသတိထားရမှာက sam deploy မလုပ်ခင် aws configure လုပ်ပြီး credentials တွေ ထည့်ထားဖို့ လိုပါလိမ့်မယ်။
prompt တွေအကုန်လုံးကို ဒီလို ရွေးပြီးသွားတဲ့အခါ်မှာတော့ aws ပေါ်မှာ deploy လုပ်ဖို့ ပြင်ဆင်ပြီးတဲ့အခါမှာ အောက်ကလိုမျိုး ပေါ်လာတဲ့အခါမှာ Y ကို ရွေးပြီး ဆက်သွားလိုက်ပါ။ resource တွေကို AWS ပေါ်မှာ deploy လုပ်ပေးပါလိမ့်မယ်။
Deployment ပြီးတဲ့အချိန်မှာတော့ output ကို ဒီလိုမြင်ရပါမယ်
SAM deployment ပြီးရင် ကျွန်တော်တို့ တစ်ခုတော့ လုပ်ဖို့ကျန်ပါသေးတယ်။ .output/public ထဲက ဖိုင်တွေကို S3 ပေါ်တင်ပေးရပါမယ် aws s3 sync
command ကို သုံးပြီးတင်ပေးလိုက်ပါ။
ဒါတွေပြီးရင်တော့ ကျွန်တော်တို့ ရဲ့ deployment က အားလုံးပြီးသွားပါပြီ။ Result ကို sam deploy ပြီးတဲ့အခါမှာထွက်လာတဲ့ output ထဲက cloudfront domain ကို ဖွင့်ကြည့်ခြင်းအားဖြင့် မြင်တွေ့နိုင်ပါပြီ။
ဒီ deployment ကို github action သုံးပြီး ဘယ်လို deploy လုပ်မလဲဆိုတာကို နောက် article (https://kalaung.org/aws-sam-deployment-with-github-action) တခုအနေနဲ့ရေးပေးထားပါတယ်။ စိတ်ပါရင် ဆက်ပြီး အားပေးသွားပါအုံး။