利用 Serverless Framework 建置和部署專案
02 Dec 2022本文說明如何利用 Serverless Framework 建立一個經由 RESTful API 讀取 DynamoDB 資料庫的專案。
行前準備
在動手實作前,需要準備的行前工作有 (1) 安裝 Serverless Framework;(2) 取得部署的通關密碼。
安裝 Serverless Framework
Serverless 是一種不用自行管理伺服器,而能讓開發者建置和執行應用程式的一種雲端模型,這樣開發者就能專心在功能開發,而不用花時間在建構底層的架構和設備上,像是 CPU、記憶體、硬碟、作業系統、網路設定等,只要經由設定就能完成這些繁瑣的工作。
若要自己從頭到尾打底建專案就太累了,在這裡會用 Serverless Framework 來幫忙處理。Serverless Framework 是一套協助建立雲端服務的介面工具,在很簡單的撰寫 YAML 設定檔和應用程式的程式碼後,即可利用 CloudFormation 部署整組 stack 到雲端服務上。
安裝 Serverless Framework,稍等會用 Serverless Framework 幫我們建立專案。
yarn add serverless --global
或
npm install serverless -g
取得部署的通關密碼
由於是透過 Serverless Framework 來做部署,因此會用到 Access key ID 和 Secret access key,這在 AWS console 上是可以取得的。
從 AWS console 取得 deploy 的通關密碼,步驟如下:
- Step 1:進入 IAM 建立 user 並設定適當的權限,由於在這裡只是做簡單範例,因此權限全開。
- Step 2:經由 IAM > Access management > Users > Security credentials > Create access key 建立 access key,這裡會得到 Access key ID 和 Secret access key,記得複製貼到筆記本上,稍等會用這組 key-value 讓這個 user 來做 deploy。
- Step 3:在 terminal export 這組 access key。
export AWS_ACCESS_KEY_ID=
export AWS_SECRET_ACCESS_KEY=
建立專案
在這裡我想建立一個甜點店家清單,利用 Serverless Framework 建立專案,在這裡使用 aws-nodejs 樣版,來建立名為 snack-list 的專案。
serverless create --template aws-nodejs --path snack-list
或是打指令 serverless
後選擇樣版「AWS - Node.js - HTTP API」,接著就會幫我們建立名為 aws-node-http-api-project 的專案也是可以的,之後專案裡面的內容都可以再調整。
建立完成!顯示成功訊息。
$ serverless create --template aws-nodejs --path snack-list
✔ Project successfully created in "snack-list" from "aws-nodejs" template (9s)
建立完成後,檢視資料夾,主要是新增 serverless.yml
和 handler.js
這兩支檔案,serverless.yml
是 AWS 服務的設定檔,像是稍等會用到的 Lambda function、API Gateway 和 DynamoDB 都會在這支檔案做設定;而 handler.js
則是來實作 Lambda function,像是怎麼讀取資料表、新增一筆資料和刪除指定資料。
設定檔
在實作 serverless.yml
設定檔方面,由於會用到 Lambda、API Gateway 和 DynamoDB,接下來逐步設定。
API Gateway
在 serverless.yml
定義 API Gateway 要長什麼樣子,在這裡定義的 RESTful API 會對應稍後用到的 Lambda function 的 create、list 和 delete,其中 delete 會帶入要刪除的項目的 id。
functions:
createSnack:
handler: handler.create
events:
- http:
path: snacks
method: POST
getSnacks:
handler: handler.list
events:
- http:
path: snacks
method: GET
deleteSnack:
handler: handler.delete
events:
- http:
path: snacks/{id}
method: DELETE
request:
parameters:
paths:
id: true
DynamoDB
在 serverless.yml
定義 DynamoDB 要長什麼樣子,例如:執行的環境和權限。
provider:
name: aws
runtime: nodejs14.x
environment:
TABLE_NAME:
Ref: snacks
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:DescribeTable
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource:
- "Fn::GetAtt": [snacks, Arn]
再來定義 primary key 的欄位與資料型別。
resources:
Resources:
snacks:
Type: AWS::DynamoDB::Table
Properties:
TableName: snacks
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
以上定義好之後,就完成簡易的設定了。
Lambda Function
在 handler.js
實作 Lambda function。
首先設定環境,像是引入 aws-sdk
和稍等會用到的 table。
const { DynamoDB } = require('aws-sdk');
const db = new DynamoDB.DocumentClient();
const TableName = process.env.TABLE_NAME;
在這支檔案裡面會 export 3 支 function - create、list 和 delete。
create 是建立一筆 snack,欄位有 id (primary key)、name (甜點店家名稱) 和 photoUrl (圖片網址)。
module.exports.create = async (event) => {
const { id, name, photoUrl } = JSON.parse(event.body);
const newSnack = { id, name, photoUrl };
await db
.put({
TableName,
Item: newSnack,
})
.promise();
return {
statusCode: 200,
body: JSON.stringify(newSnack),
};
};
list 是取出所有的 snack,在這裡會用 scan 來實作,關於 scan 可參考這裡。
module.exports.list = async (event) => {
const snacks = await db
.scan({
TableName,
})
.promise();
return {
statusCode: 200,
body: JSON.stringify(snacks),
};
};
delete 是經由帶入的 id 移除指定的 snack。
module.exports.delete = async (event) => {
const snackToBeRemovedId = event.pathParameters.id;
await db
.delete({
TableName,
Key: {
id: snackToBeRemovedId,
},
})
.promise();
return {
statusCode: 200,
};
};
在測試時,可從 CloudWatch 看 log 來做 debug。
部署
完成 serverless.yml
和 handler.js
的實作,接下來就可以做部署。
serverless deploy
部署完成!顯示成功訊息。
$ serverless deploy
Deploying serverless-framework-example to stage dev (us-east-1)
✔ Service deployed to stack serverless-framework-example-dev (109s)
endpoints:
POST - https://sample.execute-api.us-east-1.amazonaws.com/dev/snacks
GET - https://sample.execute-api.us-east-1.amazonaws.com/dev/snacks
DELETE - https://sample.execute-api.us-east-1.amazonaws.com/dev/snacks/:id
functions:
createSnack: serverless-framework-example-dev-createSnack (1.4 kB)
getSnacks: serverless-framework-example-dev-getSnacks (1.4 kB)
deleteSnack: serverless-framework-example-dev-deleteSnack (1.4 kB)
接下來用 Postman 測試看看部署好的 endpoints。
GET
先到 AWS console 裡面新增一筆 record,這樣在 get 的時候就會拿到資料。
DELETE
刪除剛才新增的資料,注意要將 id 帶入網址,格式是 https://sample.com/snacks/{id}
,例如:https://sample.com/snacks/1669537453864
。
檢查 table 裡的確沒有資料了。
POST
從 request body 帶入想要存的資料。
{
"id": "1669643175939",
"name": "通天掌糕糕",
"photoUrl": ""
}
檢查 table 出現了一筆剛剛丟進去的資料。
總結
比起純手工從無到有打造專案和流程,Serverless Framework 讓開發人員能更專注在功能的開發上,也能更有效的管理各種服務,是很便利的!
關於 Serverless Framework 和 SAM 的比較,SAM 在針對 AWS 服務上的串接更簡單 (畢竟是同一家的人),並且如果已經很熟悉 CloudFormation 的語法和操作,上手 SAM 是相對容易的,不過無法做複雜的專案,像是想要有很多 step function 就是做不到的。有興趣可參考這篇文章。