---
# System prepended metadata

title: Serverless Framework
tags: [Serverless, AWS]

---

# Serverless Framework

###### tags: `AWS`,`Serverless`

---

## Serverless
Serverless 架構流行了一陣子了，以 function 為單位，讓開發者專注於程式開發，無需擔心維護 server 問題。這使許多雲端服務商推出 serverless 服務，如 AWS Lambda 及 Google Cloud Function。

## How to build serverless applications ?

有幾種部署方式：
* AWS console : 手動到 AWS console 設定，同環境需重複設定多次, 設定流程難以文件化。
* AWS CloudFormation : CloudFormation 可以把環境部署文件化，但 Lambda Function code 必須自行另外打包上傳到 s3，就 Lambda 部署而言，設定相較於 JavaScript library 複雜。
* JavaScript library : [Serverless Framework](https://serverless.com/framework/docs/) or [AWS Amplify](https://aws-amplify.github.io/docs/)，提供簡單指令即可部署

---

## Serveless Framework

---

### Introduction

* Serverless Inc. released the Serverless Framework open source project in 2015.
* The easy and open way to build serverless applications.
* Support AWS Lambda, Google Cloud Functions, IBM Cloud, Azure & more.

---

### Example

---

##### Install Serverless Framework

```
yarn global add serverless
```

or

```
yarn add serverless
```

---

##### Create an index.js file

建立 Hello World 的 lambda function code: 
```javascript=

module.exports.hanlder = (event, context, callback) => {
    const response = {
        statusCode: 200,
        body: JSON.stringify({
          message: 'Hello World!',
        })
    }

    callback(null, response)
}
```

---

##### Create a serverless.yml

設定 yml 檔，handler 指向 index.js, events 設定 http method
```yaml=
# serverless.yml
service: my-lambda

provider:
  name: aws
  runtime: nodejs8.10
  stage: dev
  region: us-east-1

functions:
  app:
    handler: index.handler
    events:
      - http: ANY /
      - http: 'ANY {proxy+}'

```

---

##### Deploy function
部署 lambda
```
sls deploy
```

Serverless Framework 會自動執行下列流程
```flow
st=>start: sls deploy
op=>operation: Packaging service
op2=>operation: Uploading CloudFormation template & lambda .zip file to S3
op3=>operation: Creating/Updating CloudFormation Stack
e=>end: Stack update finished

st->op->op2->op3->e
```

note:
Packaging service : 將 serverless.yml 轉換成 CloudFormation template (JSON), 並將 lambda 打包成 .zip 檔, 相關檔案將放在 .serverless 資料夾下
Uploading CloudFormation file & service .zip file to S3 : 將 CloudFormation template 跟 .zip 檔上傳到 S3, 所以 serverless 會自動建立專屬放 serverless 檔案的 S3
Creating/Updating CloudFormation Stack : 建立 CloudFormation stack, 並執行 CloudFormation template 設定的內容

---

### Serveless Framework deployment result

完成部署後會給 Lambda 的 end point，發 request 時會先到 API Gateway ，再去呼叫 Lambda Function

```flow
st=>start: xxxx.execute-api.us-east-1.amazonaws.com/dev
op=>operation: API Gateway
op2=>operation: Lambda Function
st->op->op2
```

---

## Serverless Framework YAML

serverless.yml 是 Serveless Framework 核心，裡頭會設定環境佈署方式。舉一些常見的用法及設定。

- Variables
- Environment
- Intrinsic Function
- Lambda Function
- DynamoDB
- S3 Bucket
- AppSync

---

### Variables

Variable sources
- Environment variables : ``` ${env:STAGE} ```
- CLI options : ${opt:stage} 
    ```
    sls deploy --stage dev
    ```
- External YAML/JSON files : ``` ${file(./config.dev.yml)} ```
- Referencing S3 Objects or CloudFormation Outputs

---

### Environment

Allow to pass static information into Lambda Function

```yaml=
# serverless.yml
provider:
  environment:
    STAGE: dev
    USER_TABLE: UserList
```

```javascript=
//javascript
const TABLE_NAME = process.env.USER_TABLE
```

---

### Intrinsic Function

<br>

[AWS CloudFormation Intrinsic Function](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html)

---

#### 串接數組
```yaml=
Fn::Join:
    - '/'
    -- 'table'
    -- 'aaa'
```

```
Out: table/aaa
```

---

#### 取得 Resource attributeName
```yaml=
Fn::GetAtt:[MyS3Bucket, Arn]
```

```
Out: 回傳 logical ID 為 MyS3Bucket 的 Arn 屬性值
```

---

---

### Lambda Function

基本設置可以參考官網，以下記錄一些比較特別的設定。

---

#### Schedule

可以設定 schedule event，CloudWatch 會在設定的時間發送 event 給 Lambda Function。

---

#### Cold starts in AWS Lambda

- cold start 時間 : C# > JAVA >>> nodeJS > Python
- Lambda package size 不會影響 cold start 時間
- 整體而言有 60% lambda functions stay warm 介於 45~60 min 之間, 使用越多 memory 的 lambda function, 其 container 會被 AWS 較早關閉, 故 stay warm 時間會較短
- 設定 VPC 會增加 cold start 時間

note:
cold start : AWS 進行運行 lambda function 的 container 初始化過程 (container initialization) 稱為 cold start, 此 container 存在一段時間後才會被關閉, 這段期間若有調用 lambda function, 將在既有的 container 運行, 無需再 cold start, 若 container 已被關閉, 在必須再重新初始化

---

### DynamoDB

---

#### Basic Components
* Tables
* Items
* Attributes

note:
* Tables:
類似於 RDBMS 的 Table.
DynamoDB Table 是一個儲存集合單位。
相當於 MongoDB 的 Collection
* Items:
每個 Table 可以有多個 Items，相當於 RDBMS 的 Rows。
每個 Items 可包含多個 Attributes
相當於 MongoDB 的 Document
* Attributes:
每個 Items 由一個或多個 Attributes 組成
Attribute 的資料型態有 String, Number, Binary

---

#### serverless.yml dynamodb 基本的設定 

設定權限
```yaml=
# serverless.yml
provider:
  ...
  iamRoleStatements:
  - Effect: "Allow"
    Action:
    - "dynamodb:*Item"
    - "dynamodb:Query"
    - "dynamodb:Scan"
    Resource:
    - Fn::GetAtt: [MyTable, Arn]
  ...

...
```

設定 DB
```yaml=
...  
resources:
  Resources:
    MyTable:
      Type: AWS::DynamoDB::Table
        Properties:
          AttributeDefinitions:
          - AttributeName: id
            AttributeType: N
          - AttributeName: account
            AttributeType: S
          KeySchema:
          - AttributeName: id
            KeyType: HASH
          - AttributeName: account
            KeyType: RANGE
         TableName: 'MyTable'
...
```
---

#### Primary Key
Two different kinds of primary keys
- Partition key
-- Unique
-- Hash attribute
- Partition key and sort key
-- Composite key, partition key + sort key must be unique
-- Hash attribute and range attribute

note:
* Partition key(分區 key):
DynamoDB 利用這個值透過內部的 hash function，然後依據 hash 過的值，決定資料要放在哪個實體的儲存體 (Storage)。
基本上，不會有重複的 hash value，也就是不會有重複的 partition key。
* Partition key and sort key(分區 key 與排序 key):
使用兩個 attribute 的複合鍵 (composite key): partition key + sort key, 或者稱為 hash key + range key
sort key 又叫 range attribute
如果 sort key 存在，那麼 partition key 可以重複
hash key + range key 必須是唯一
最常用的例子就是 unique key + date range 這樣的組合。

---

#### Secondary Indexes

- More flexibility when querying data.
- Global secondary index & Local secondary index
- Define up to 5 global secondary indexes and 5 local secondary indexes per table.

note:
* Secondary Indexes 有自己的 key schema, GSI 有 自己的 partition key 及 sort key, 而 LSI 的 partition key 必須跟 Table 的 partition key 一致
* 若要對 Table partition key 之外的 attributes 作 query 或是 sort key 之外的 attributes 作排序, 需設定 Secondary Indexes

---

#### Throughput Capacity & Auto Scaling

- Read capacity unit : 4KB/s
- Write capacity unit : 1KB/s
- Auto Scaling : Manages throughput capacity for tables and global secondary indexes

note:
Read capacity unit : 每秒一次 or 兩次一致的 consistent read 最多4KB
Write capacity unit : 每秒最多1KB的項目的一次寫入
Auto Scaling : 管理 tables and global secondary indexes 讀寫容量, 可以為讀取和寫入容量單位定義範圍（上限和下限）。還可以在該範圍內定義目標利用率百分比

---

#### TimeToLiveSpecification

DynamoDB 可以設定 [TTL](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-timetolivespecification.html)，時間到就會自動幫忙清除該筆資料。

---

#### serverless.yml dynamodb 進階的設定 

```yaml=
...
resources:
  Resources:
    MyTable:
      Type: AWS::DynamoDB::Table
      Properties:
        AttributeDefinitions:
        - AttributeName: id
          AttributeType: N
        - AttributeName: account
          AttributeType: S
        KeySchema:
        - AttributeName: id
          KeyType: HASH
        - AttributeName: account
          KeyType: RANGE
        ProvisionedThroughput:
          ReadCapacityUnits: 5
          WriteCapacityUnits: 5
        TimeToLiveSpecification:
          AttributeName: exp
          Enabled: true
        TableName:
          Fn::Join:
          - "-"
          - - ${self:custom.MyTableName}
            - ${self:custom.stage}
...
```
---

### S3 Bucket

- Serverless Framework Plugin : [serverless-finch](https://github.com/fernando-mc/serverless-finch)

```yaml=
custom:
  client:
    bucketName: MyBucketName
    distributionFolder: web
    
plugins:
- serverless-finch    
```

```
sls client deploy
```

---

### AppSync

- Serverless Framework Plugin : [serverless-appsync-plugin](https://github.com/sid88in/serverless-appsync-plugin)

```yaml=
name: AppSync name
authenticationType: 驗證方式, API Key, cognito... 等等
logConfig: AppSync log 設定
mappingTemplatesLocation: mapping template vtl 檔存放位置
mappingTemplates:
  - dataSource: dataSource 名稱
    type: Mutation or Query
    field: field name
    request: request vtl 檔名
    response: response vtl 檔名
schema: schema.graphql 檔案位置
dataSources:
  - type: AMAZON_DYNAMODB or 其他
    name: dataSource 名稱

```

---

