AWS Lambdaデプロイ方法を求めて：Serverlessフレームワーク


このドキュメントレベル：初めて学ぶ人向け

Lambdaの良いデプロイフローはないかと思って調べた記録です。
Lambdaのデプロイにはいくつか種類があるようです。

- Serveless
- Apex
- Lamvery
- LambCI
- CodeShip

今回は「Serverless」について、どんなものかを触りだけ調べています。

# Serverless

サーバーレスなアーキテクチャを容易に作成、管理できるフレームワークです
AWS Lambda, Apache OpenWhisk, Microsoft Azureなどをサポートしているようです。

## デプロイイメージをつかむ

- このnode.jsのサンプルを実際にやってみると感じがよくわかります。事前のAWSのCredentialsの設定をやっておきましょう

[Hello World Node\.js Example](https://serverless.com/framework/docs/providers/aws/examples/hello-world/node/)


[Serverless Framework \- AWS Lambda Guide \- Credentials](https://serverless.com/framework/docs/providers/aws/guide/credentials/)

## 豊富なサンプルコード

gutHubにサンプルコードがアップされているので、これを実行するだけでも感じがつかめます

[serverless/examples: Serverless Examples – A collection of boilerplates and examples of serverless architectures built with the Serverless Framework and AWS Lambda](https://github.com/serverless/examples)


## 例）aws-node-rest-api-with-dynamodb/

### cloneしてきて、deployしてみます

```
14:45:35 aws-node-rest-api-with-dynamodb/  $ ls
README.md	package.json	serverless.yml	todos


14:46:01 aws-node-rest-api-with-dynamodb/  $ npm install
aws-rest-with-dynamodb@1.0.0 /Users/bohebohechan/devel/src/gitlab.com/FirstFourNotes/serverless/aws-node-rest-api-with-dynamodb
└── uuid@2.0.3

npm WARN aws-rest-with-dynamodb@1.0.0 No repository field.

11:23:44 aws-node-rest-api-with-dynamodb/  $ sls deploy
Serverless: Packaging service...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
.....
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (22.45 KB)...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
......................................................................................................
Serverless: Stack update finished...
Service Information
service: serverless-rest-api-with-dynamodb
stage: dev
region: us-east-1
api keys:
  None
endpoints:
  POST - https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/todos
  GET - https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/todos
  GET - https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/todos/{id}
  PUT - https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/todos/{id}
  DELETE - https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/todos/{id}
functions:
  create: serverless-rest-api-with-dynamodb-dev-create
  list: serverless-rest-api-with-dynamodb-dev-list
  get: serverless-rest-api-with-dynamodb-dev-get
  update: serverless-rest-api-with-dynamodb-dev-update
  delete: serverless-rest-api-with-dynamodb-dev-delete
11:25:19 aws-node-rest-api-with-dynamodb/  $

```

RestAPIができてしまったようです。

### AWSコンソールで確認

実際にコンソールで確認してみましょう

#### > Lambda

<img width="932" alt="lambda.png" src="https://qiita-image-store.s3.amazonaws.com/0/32215/93048390-2a8b-8422-c325-8eea16766f9c.png">

#### > DynamoDB

キャプチャ撮り忘れたので割愛・・・

#### > API Gateway
<img width="1512" alt="apigateway.png" src="https://qiita-image-store.s3.amazonaws.com/0/32215/9a8802ac-7dbe-7dc7-926b-644c13073fe3.png">

#### > S3

<img width="1443" alt="s3.png" src="https://qiita-image-store.s3.amazonaws.com/0/32215/b92e4c5e-4715-ae36-68f0-1f778c87a33f.png">


#### > CloudFormation

<img width="1145" alt="cloudformation.png" src="https://qiita-image-store.s3.amazonaws.com/0/32215/56898df2-de0e-cebe-4dbf-f207fad56d41.png">

#### > CloudWatch Logs

<img width="1180" alt="cloudwatchlogs.png" src="https://qiita-image-store.s3.amazonaws.com/0/32215/ee80e16b-2222-490f-66fb-c8e0b783a526.png">

### PostmanでAPIを実行してみます

#### > Post

<img width="845" alt="postman-post.png" src="https://qiita-image-store.s3.amazonaws.com/0/32215/b2b2ac02-31ed-df7e-51ea-ae97f5d62cc6.png">


#### > Get

<img width="932" alt="postman-list.png" src="https://qiita-image-store.s3.amazonaws.com/0/32215/aab81907-ae30-e2e4-a825-907e77d23e01.png">


### 後片付け

消しておきましょう

```
11:44:47 aws-node-rest-api-with-dynamodb/  $ sls remove
Serverless: Getting all objects in S3 bucket...
Serverless: Removing objects in S3 bucket...
Serverless: Removing Stack...
Serverless: Checking Stack removal progress...
..............................................................
Serverless: Stack removal finished...
```

このようなサンプルをカスタマイズしていくことで、自分の欲しい機能を簡単に作っていけそうです。

## serverless.yml 

先ほどの例）aws-node-rest-api-with-dynamodb/を参考にして、serverless.ymlの中身を紐解いてみましょう
解説はざっくりなので、詳細は公式ページで確認のことです。

**AWSをプロバイダーにセットしたときに有効となるプロパティ一覧が載っています**
[Serverless Framework \- AWS Lambda Guide \- Serverless\.yml Reference](https://serverless.com/framework/docs/providers/aws/guide/serverless.yml/)


- service

プロジェクト名

- frameworkVersion

フレームワークの対応バージョン

```
service: serverless-rest-api-with-dynamodb

frameworkVersion: ">=1.1.0 <2.0.0"
```

- provider

AWS CloudFormation stack
サービスがデプロイされる対象について書きます。ここでは、AWSですよね　
　
- iamRoleStatements
[How it works iamRoleStatements configuration section? \- Serverless Framework \- Serverless Forums](http://forum.serverless.com/t/how-it-works-iamrolestatements-configuration-section/467)

Lambda Functionで、AWSのリソースにアクションする際の許可をAWS IAM Roleで記述します。 
「provider.iamRoleStatements」のプロパティに必要となる許可ステートメントを設定します。 
今回は、Lambdaからdynamodbへの許可が必要ですね。 

```
provider:
  name: aws
  runtime: nodejs6.10
  environment:
    DYNAMODB_TABLE: ${self:service}-${opt:stage, self:provider.stage}
  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}"
```

-Functions

Lambdaに作成するFunctionの定義を書きます

- Events

Lambda Function の起動するトリガーを書きます
S３バケットへのアップロードや、SNSトピック受信や、HTTPのエンドポイントですね

**サポートしているイベントの一覧はこちら**
[Serverless \- AWS Lambda \- Events](https://serverless.com/framework/docs/providers/aws/events/)


```
functions:
  create:
    handler: todos/create.create
    events:
      - http:
          path: todos
          method: post
          cors: true

  list:
    handler: todos/list.list
    events:
      - http:
          path: todos
          method: get
          cors: true

  get:
    handler: todos/get.get
    events:
      - http:
          path: todos/{id}
          method: get
          cors: true

  update:
    handler: todos/update.update
    events:
      - http:
          path: todos/{id}
          method: put
          cors: true

  delete:
    handler: todos/delete.delete
    events:
      - http:
          path: todos/{id}
          method: delete
          cors: true
```

- Resources

AWS CloudFormation stackに追加することができます。
以下では、TodosDynamoDbTableを追加しています。
特定のCloudFormationのリソースに対して、値を上書きすることもできます

```
resources:
  Resources:
    TodosDynamoDbTable:
      Type: 'AWS::DynamoDB::Table'
      DeletionPolicy: Retain
      Properties:
        AttributeDefinitions:
          -
            AttributeName: id
            AttributeType: S
        KeySchema:
          -
            AttributeName: id
```


## ワークフロー

[Serverless Framework Guide \- AWS Lambda \- Workflow](https://serverless.com/framework/docs/providers/aws/guide/workflow/)

Development Workflowとして以下の手順で回しましょう、といったことが書かれています。


> 1. Write your functions
> 2. Use serverless deploy only when you've made changes to serverless.yml and in CI/CD systems.
> 3. Use serverless deploy function -f myFunction to rapidly deploy changes when you are working on a specific AWS Lambda Function.
> 4. Use serverless invoke -f myFunction -l to test your AWS Lambda Functions on AWS.
> 5. Open up a separate tab in your console and stream logs in there via serverless logs -f myFunction -t.
> 6. Write tests to run locally.

## 参考になるドキュメント

- [Building a REST API in Node\.js with AWS Lambda, API Gateway, DynamoDB, and Serverless Framework](https://serverless.com/blog/node-rest-api-with-serverless-lambda-and-dynamodb/)

- [CICD for Serverless Part 1 \- Mocha Endpoint Testing](https://serverless.com/blog/cicd-for-serverless-part-1/)
- [serverless/examples: Serverless Examples – A collection of boilerplates and examples of serverless architectures built with the Serverless Framework and AWS Lambda](https://github.com/serverless/examples)