Serverless REST API on AWS Lambda

Serverless REST API on AWS Lambda

(API-Gateway, Lambda, ExpressJs, DynamoDB)

·

6 min read

ဒီဆောင်းပါးမှာ မျှဝေချင်တာကတော့ NodeJs Serverless REST API တစ်ခုကို AWS services တွေပေါ်မှာ ဘယ်လို တည်ဆောက်မလဲ။ Local မှာ testing လုပ်ဖို့ environment ဘယ်လိုတွေဆောက်မလဲနဲ့၊ ဒီ setup ကို gitlab-ci ပေါ်ကနေ ဘယ်လို deploy လုပ်မလဲဆိုတာ တို့ပဲဖြစ်ပါတယ်။

Serverless သုံးရင် ရလာမဲ့ အားသာချက်တွေက ဘာတွေလဲ။

  • Demand ပေါ်မူတည်ပြီး auto scale ဖြစ်နေတဲ့အတွက် server down မှာ စဉ်းစားဖို့ မလိုပါဘူး။

  • အသုံးပြုတဲ့ မီလီစက္ကန့်အလိုက်ပဲ ပေးရတာဖြစ်တဲ့အတွက် traffic အမြဲ မရှိမနေတဲ့ api တွေအတွက်ဆိုရင် running cost ကို အများကြီး လျှော့ချပေးနိုင်ပါတယ်။

  • servers တွေကို maintain လုပ်ရတဲ့ workload ကို လျှော့ချပေးနိုင်ပါတယ်။

ဘယ်လိုအခြေနေတွေမှာ serverless မသုံးသင့်ဘူးလဲ?

  • ကိုယ့် application traffic က stable ဖြစ်တယ် (သို့) ကိုယ့်ရဲ့ DevOps team က traffice ပေါ်မူတည်ပြီး auto scale ကို သေချာ ကုန်ကျစရိတ် သက်သာအောင် ချိန်ဆနိုင်တယ်၊ server maintainance ကို ကောင်းကောင်း ပိုင်နိုင်တယ်။

  • ကိုယ့် application requirement က ရှုပ်ထွေးပြီး serverless သုံးဖို့ အဆင်မပြေနိုင်ဘူး။

  • ကိုယ့် application က server resource သိပ်မသုံးပဲ တစ်ခြားက api တွေရဲ့ response ကို စောင့်နေရဲ့ အချိန်တွေက များနေတယ်။

စတဲ့ အခြေတွေမှာဆိုရင်တော့ serverless မသုံးသင့်ပါဘူး။

အသုံးပြုသွားမဲ့ AWS Services တွေကတော့

  • Lambda

  • API Gateway

  • DynamoDB

  • IAM

  • S3

တို့ပဲ ဖြစ်ပါတယ်။

Local setup

ExpressJS နဲ့ Serverless Framework ကို သုံးမှာ ဖြစ်ပြီး Local မှာစမ်းဖို့ DynamoDB Local အသုံးပြုမှာဖြစ်တဲ့အတွက် Node, npm နဲ့ Java Runtime Environment (JRE) တို့ကို install ထားဖို့ လိုအပ်ပါတယ်။

❯ node -v
v18.16.0
❯ npm -v
9.5.1
❯ java --version
openjdk 11.0.19 2023-04-18

ကျွန်တော့စက်မှာတော့ node 18, npm 9 နဲ့ jre 11 တို့ထည့်ထားပါတယ်။ ဒီဆောင်းပါးကို ဖတ်နေသူတွေဟာ ဒါတွေဘယ်လိုထည့်ရမလဲ သိပြီးသားဖြစ်တယ်လို့ ယူဆတဲ့အတွက် အသေးစိတ်မပြောတော့ပါဘူး။ ကိုယ့်စက်ထဲမှာ လိုနေတာတွေ ထည့်ပြီးပြီဆိုရင် အရင်ဆုံး serverless install လုပ်ပါမယ်။

npm install -g serverless

serverless ထည့်ပြီးပြီဆိုရင်တော့ serverless ကိုသုံးပြီး ကျွန်တော်တို့ project create လုပ်လို့ရပါပြီ။ serverless project တစ်ခု ဆောက်ဖို့ ရိုက်ရတဲ့ command က တစ်ခုထဲလိုပါတယ်။ project ထားမဲ့ parent dir ထဲမှာ serverless (သို့) sls လို့ ရိုက်လိုက်ရုံပါပဲ။

❯ sls

Creating a new serverless project
? What do you want to make?
  AWS - Node.js - Starter
  AWS - Node.js - HTTP API
  AWS - Node.js - Scheduled Task
  AWS - Node.js - SQS Worker
  AWS - Node.js - Express API
❯ AWS - Node.js - Express API with DynamoDB
  AWS - Python - Starter
  AWS - Python - HTTP API
  AWS - Python - Scheduled Task
  AWS - Python - SQS Worker
  AWS - Python - Flask API
  AWS - Python - Flask API with DynamoDB
  Other

ပေါ်လာထဲ့ prompt ထဲကနေ AWS - Node.js - Express API with DynamoDB ကိုရွေးပြီး enter နှိပ်ပါ။ project name ပေးပါ။ ကျွန်တော်ကတော့ project name ကို kalaung လို့ ပေးလိုက်ပါတယ်။ Do you want to deploy now? လို့ မေးလာရင် no ပါ။

❯ sls

Creating a new serverless project

? What do you want to make? AWS - Node.js - Express API with DynamoDB
? What do you want to call this project? my-api

✔ Project successfully created in kalaung folder

? Do you want to deploy now? No

What next?
Run these commands in the project directory:

serverless deploy    Deploy changes
serverless info      View deployed endpoints and resources
serverless invoke    Invoke deployed functions
serverless --help    Discover more commands

project create လုပ်ပြီးပြီဆိုရင် ကျွန်တော်တို့ project dir ထဲ ဝင်ပြီး local မှာ offline စမ်းလို့ရအောင် လိုအပ်တဲ့ plugin တွေ ထည့်ပါမယ်။ ဘာတွေထည့်ရမလဲဆိုတော့ အောက်က command အတိုင်းသာ ကြည့်ရိုက်သွားလိုက်ပါ။

cd kalaung
sls plugin install -n serverless-dynamodb-local
sls plugin install -n serverless-offline
npm i @aws-sdk/client-dynamodb --save-dev
npm i @aws-sdk/lib-dynamodb --save-dev
sls dynamodb install

ပုံမှန်ဆိုရင်တော့ အကုန်လုံး အဆင်ပြေပြေပြီးသွားသင့်ပေမယ့် နောက်ဆုံး sls dynamodb install မှာ Error: Error getting DynamoDb local latest tar.gz location undefined: 403 ဆိုပြီး error တက်ပါလိမ့်မယ်။ AWS ဘက်က DynamoDb local url ကို http ကနေ https ချိန်းလိုက်လို့ ဖြစ်နေတဲ့ error ပါ။ သူ့ package က အလုပ်မလုပ်ရင် ကျွန်တော်တို့ manual လုပ်ကြတာပေါ့ ခက်ခက်ခဲခဲတော့ မဟုတ်ပါဘူး အောက်က command လေးကို run ပေးလိုက်ပါ လိုအပ်တဲ့ file ကို ဒေါင်းပြီး extract လုပ်ပေးသွားပါလိမ့်မယ်။

mkdir -p .dynamodb && curl https://s3-us-west-2.amazonaws.com/dynamodb-local/dynamodb_local_latest.tar.gz | tar -xz -C .dynamodb

ဒါဆိုရင်တော့ လိုအပ်တဲ့ package တွေ စုံသွားပါပြီ ကျွန်တော်တို့ code စရေးကြရအောင်။

အရင်ဆုံး .gitignore file ကို ပြင်ပါမယ်။ .gitignore file ရဲ့ အောက်ဆုံးမှာ .dynamodb ဆိုပြီး တစ်ကြောင်း ထပ်ထည့်ပေးပါ။

ပြီးရင် serverless.yml ကိုပြင်ပါမယ်။

custom:
  tableName: 'users-table-${sls:stage}'

အပေါ်က custom: > tableName: ဆိုတဲ့ code နေရာမှာ အောက်က code တွေနဲ့ အစားထိုးလိုက်ပါ။

custom:
  tableName: '${self:service}-${sls:stage}-users-table'
  dynamodb:
    start:
      migrate: true
    stages:
      - dev

package:
    excludeDevDependencies: true
    patterns:
      - '!.dynamodb/**'
      - '!README.md'

tableName ကို သူပေးတဲ့ ပုံစံအတိုင်းမထားပဲ service name နဲ့ stage ကို prefix ပေးလိုက်တာက IAM Permission တွေပေးတဲ့နေရာမှာ resource filter ပိုင်း ပိုအဆင်ပြေအောင်လို့ပါ။ dynamodb: ဆိုတဲ့ block ကတော့ DynamoDB ကို offline မှာသုံးဖို့နဲ့ package build တဲ့အခါမှာ မလိုတဲ့ ဖိုင်တွေ ချန်ခဲ့ဖို့ ဖြစ်ပါတယ်။

ပြီးရင် index.js file ကိုပြင်ပါမယ်။

const client = new DynamoDBClient();

အပေါ်က code နေရာမှာ အောက်က code တွေနဲ့ အစားထိုးပါမယ်။ offline စမ်းတဲ့အခါမှာ AWS ရဲ့ DynamoDB service ကို သွား connect မလုပ်ပဲ localhost မှာ port 8000 နဲ့ serve မဲ့ DynamoDB local ပဲသုံးဖို့အတွက်ပါ။

const dynamoDbClientParams = {};
if (process.env.IS_OFFLINE) {
  dynamoDbClientParams.region = 'localhost'
  dynamoDbClientParams.endpoint = 'http://localhost:8000'
}
const client = new DynamoDBClient(dynamoDbClientParams);

app.use(express.json()); ရဲ့ အောက်မှာ app.get("/". ...... ဆိုပြီး အောက်က code တွေ ထပ်ထည့်ပါမယ်။

app.use(express.json());

app.get("/", function (req, res) {
  res.json({hello: "KalaungTech"});
});

အကုန်ပြင်ပြီးရင်တော့ local မှာ စမ်းကြည့်ဖို့အတွက် sls offline start ဆိုပြီး run ကြည့်လိုက်လို့ရပါပြီ။ DynamoDB local က port 8000 နဲ့ run သွားပြီး api server ကတော့ port 3000 နဲ့ run သွားပါလိမ့်မယ်။

ကျွန်တော်တို့ api ကို curl နဲ့ စမ်းကြည့်လို့ရပါပြီ။

❯ curl --request GET 'http://localhost:3000/'
{"hello":"KalaungTech"}%
❯ curl --request POST 'http://localhost:3000/users' --header 'Content-Type: application/json' --data-raw '{"name": "John", "userId": "1"}'
{"userId":"1","name":"John"}%
❯ curl --request GET http://localhost:3000/users/1
{"userId":"1","name":"John"}%
❯

Request တစ်ခုခြင်းဆီအတွက် ကျသင့်တဲ့ Billed Duration ကိုပါပြပေးတဲ့အတွက် ကုန်ကျစရိတ်ကို ချိန်ဆဖို့အတွက်နဲ့ code ကို optimize လုပ်ဖို့လည်း တော်တော် အသုံးဝင်ပါတယ်။

ကျွန်တော်တို့ စမ်းလို့ အဆင်ပြေပြီဆိုရင် aws ပေါ်ကို deploy ပါမယ်။

Creating AWS IAM User

ကျွန်တော်တို့ deploy လုပ်ဖို့ AWS user create မလုပ်ခင် Permission အတွက် IAM Policy တစ်ခု အရင်ရေးပါမယ်။ အောက်က policy ကို ကော်ပီကူးပြီး IAM policy အသစ် တစ်ခု ပြုလုပ်ပါ။ Policy Name ကို ကျွန်တော်ကတော့ ServerlessDeployPermission လို့ ပေးလိုက်ပါတယ်။ ဒီ policy မှာ တစ်ခု သတိထားရမှာက Resource တွေထဲမှာ kalaung-prod ဆိုတဲ့ prefix ကို သုံးထားပါတယ်။ kalaung က service name ကို ယူထားတာဖြစ်ပြီး prod ကတော့ deploy လုပ်တဲ့ stage ပါ။ ကိုယ်သုံးမဲ့ service name နဲ့ stage ကို ပြင်ပြီးတော့ ဒီ policy ကို အသုံးပြုရမှာ ဖြစ်ပါတယ်။ ဥပမာ ကိုယ့် serverless.yml ထဲက service name က myapi လို့ ပေးထားပြီး dev stage နဲ့ deploy မှာဆိုရင် kalaung-prod ဆိုတဲ့ နေရာတွေမှာ myapi-dev နဲ့ ပြောင်းပြီးမှ policy ကို save ရမှာ ဖြစ်ပါတယ်။

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "ServerlessUpdatePolicies",
            "Effect": "Allow",
            "Action": [
                "cloudformation:List*",
                "cloudformation:Describe*",
                "cloudformation:UpdateStack",
                "cloudformation:CreateChangeSet",
                "cloudformation:DeleteChangeSet",
                "cloudformation:ExecuteChangeSet",
                "lambda:*",
                "logs:Describe*",
                "logs:TagResource",
                "logs:CreateLogGroup",
                "logs:DeleteLogGroup",
                "iam:GetRole",
                "iam:TagRole",
                "iam:PassRole",
                "iam:CreateRole",
                "iam:PutRolePolicy",
                "iam:getRolePolicy",
                "iam:DeleteRolePolicy",
                "s3:PutObject",
                "s3:GetObject",
                "s3:ListBucket",
                "s3:DeleteObject",
                "s3:PutBucketPolicy",
                "s3:GetBucketPolicy",
                "s3:GetBucketPolicyStatus",
                "s3:DeleteBucketPolicy",
                "s3:GetEncryptionConfiguration",
                "s3:PutEncryptionConfiguration",
                "dynamodb:CreateTable",
                "dynamodb:DescribeTable",
                "dynamodb:DeleteTable"
            ],
            "Resource": [
                "arn:aws:s3:::kalaung-prod-*",
                "arn:aws:iam::*:role/kalaung-prod-*",
                "arn:aws:dynamodb:*:*:table/kalaung-prod-*",
                "arn:aws:lambda:*:*:function:kalaung-prod-*",
                "arn:aws:cloudformation:*:*:stack/kalaung-prod",
                "arn:aws:cloudformation:*:*:stack/kalaung-prod/*",
                "arn:aws:logs:*:*:log-group:/aws/lambda/kalaung-prod-*"
            ]
        },
        {
            "Sid": "ServerlessExtraPolicies",
            "Effect": "Allow",
            "Action": [
                "s3:CreateBucket",
                "lambda:GetFunction",
                "cloudformation:GetTemplate",
                "cloudformation:ValidateTemplate"
            ],
            "Resource": "*"
        },
        {
            "Sid": "ServerlessApiPolicies",
            "Effect": "Allow",
            "Action": [
                "apigateway:*"
            ],
            "Resource": [
                "arn:aws:apigateway:*::/apis",
                "arn:aws:apigateway:*::/apis/*",
                "arn:aws:apigateway:*::/apikeys/*",
                "arn:aws:apigateway:*::/tags/*"
            ]
        }
    ]
}

Policy ရေးပြီးသွားရင် IAM user အသစ် create လုပ်ပါ permission မှာ အခုရေးထားတဲ့ policy ServerlessDeployPermission ကို attach တွဲပေးလိုက်ပါ။ User Name ကို ကျွန်တော်ကတော့ ServerlessDeployUser လို့ ပေးလိုက်ပါတယ်။

User account ဆောက်ပြီးသွားရင် ServerlessDeployUser User account ရဲ့ Security credentials ထဲ သွားပြီးတော့ Access Key ထုတ်ပါ။

Access key ရလာပြီဆိုရင် serverless မှာ သုံးဖို့ configure လုပ်ပါမယ်။ အောက်က command မှာ ကိုယ့်ရဲ့ accessKey, secretKey အစားထိုးပြီး run လိုက်ပါ။

serverless config credentials \
  -o --provider aws \
  --key YOUR_ACCESS_KEY \
  --secret YOUR_SECRET_VALUE

credential ထည့်ပြီးပြီဆိုရင်တော့ ကျွန်တော်တို့ ရဲ့ api ကို AWS ပေါ် deploy လို့ရပါပြီ။ serverless deploy --region ap-southeast-1 --stage prod လို့ ရိုက်ပြီး deploy လိုက်ပါ။

ပုံထဲကလို ပေါ်လာရင်တော့ deploy လုပ်တာ အောင်မြင်သွားပါပြီ။ ထွက်လာတဲ့ endpoint မှာ ကိုယ့် ရဲ့ api အလုပ် လုပ်နေပါလိမ့်မယ်။

GitLab CI

ကျွန်တော်တို့ Local မှာ စမ်းလို့လည်းရပြီ deploy လို့လည်း ရပြီဆိုတော့ gitlab ပေါ်တင်ပြီး CI/CD pipeline run ဖို့အတွက် .gitlab-ci.yml ဖိုင် create လုပ်ပါမယ်။

# .gitlab-ci.yml
image: node:18-alpine

before_script:
  - npm install -g serverless

stages:
  - build
  - deploy

build-production:
    when: manual
    stage: build
    script:
        - npm install --legacy-peer-deps --progress=false 
        - sls package --region ${AWS_REGION} --stage prod  
    artifacts:
      paths:
        - .serverless
        - node_modules
    only:
        - master

deploy-production:
  when: on_success
  stage: deploy
  needs: ["build-production"]
  script:
      - sls deploy --region ${AWS_REGION} --stage prod 
  only:
      - master

GitlabCI file create လုပ်ပြီးပြီဆိုရင် gitlab မှာ repo ဆောက်ပါမယ်။ ပြီးရင် repo အသစ်ရဲ့ CI/CD setting ထဲကိုသွားပြီး Variables ထဲမှာ အောက်က variables သုံးခုကို ထည့်ပေးပါ။

Key: AWS_ACCESS_KEY_ID
Value: YOUR_ACCESS_KEY
Protected: ✓

Key: AWS_SECRET_ACCESS_KEY
Value: YOUR_SECRET_VALUE
Protected: ✓

Key: AWS_REGION
Value: ap-southeast-1

Gitlab repo ထဲမှာ variable တွေ ထည့်ပြီးပြီဆိုရင်တော့ ကျွန်တော်တို့ရဲ့ project ကို repo အသစ်ပေါ်တင်ပြီး CI/CD pipeline ကို run ကြည့်လို့ရပါပြီ။

Pipeline result လေး pass ဖြစ်သွားပြီဆိုရင်တော့ ပြီးပါပြီ။

ကိုယ့်ရဲ့ api ကို ကိုယ်ပိုင် domain နဲ့ သုံးချင်တယ်ဆိုရင်တော့ ApiGateway dashboard ထဲက Custom domain names မှာသွားပြီး manual ထည့်ပေးလို့ရပါတယ်။

RepoUrl: https://gitlab.com/thixpin/serverlessapi/