Serverless Frameworkでお手軽サーバーレス（基本編）

Lambdaでお手軽サーバーレス入門の次の記事[（とりあえず作ってみる編）](https://qiita.com/kakken1988/items/29ec13228f1398f40fec)、[（API Gateway設定編）](https://qiita.com/kakken1988/items/bcf052116b2148818930)、[（API Gatewayデプロイ編）](https://qiita.com/kakken1988/items/c864e8eecccdf877c612)、[（CloudWatch Events編）](https://qiita.com/kakken1988/items/12c1af589ade9f780222)で、軽くLambda、API Gateway、CloudWatch Eventsについて紹介しました。

# 問題

* 関数そのものを作るより設定のほうが手間
* 設定を手動でするなんてナンセンス

# 解決策

[Serverless Framework](https://serverless.com/framework)を使おう！

## できること

たくさんありますが、以下のようなことが実現できます。

* Lambda関数のデプロイ
* トリガーの設定（API Gateway, CloudWatch Events, SNSなど…）
* プラグインの使用

# 前提

Serverless FrameworkはNodeで動くので、以下のツールをインストールしておいてください。

* Node.js (4.0以上)
* AWS CLI

# インストール・セットアップ

```bash
npm install -g serverless
```

私のところではこのような感じです。
（`sls`は`serverless`のエイリアスです。）

```bash
$ node -v
v8.11.4

$ npm -v
5.6.0

$ sls --version
1.27.3

$ aws --version
aws-cli/1.16.1 Python/3.7.0 Darwin/18.0.0 botocore/1.11.1
```

インストールが終われば、AWSのアクセスキーを設定します。
（既に設定済みであればパスして問題ありません）

```bash
sls config credentials --provider aws --key AWSACCESSKEY --secret AWSSECRETACCESSKEY
```

# 定義ファイル

Serverless Frameworkでは、デプロイしたいサービスごとに、定義ファイル `serverless.yml` を作成する必要があります。

## 簡単なサンプル

以下のファイルを、[Lambdaでお手軽サーバーレス入門（Lambdaのイベント from API Gateway編）](https://qiita.com/kakken1988/items/e4d92395f17c95a31e24)で作ったディレクトリに置きます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/143240/9340ea48-5cd6-4523-8a29-2384e509416e.png)


```yaml:serverless.yml
service: qiita-lambda-apigateway

provider:
  name: aws
  profile: ${opt:profile, 'default'}
  stage: prod
  region: ap-northeast-1
  runtime: python3.6
  role: arn:aws:iam::ACCOUNT_ID:role/service-role/YOUR-ROLE-NAME  # 適切なロール名に置き換えてください！
  timeout: 30
  memorySize: 128
  versionFunctions: false

resources:
  Resources:
    ApiGatewayRestApi:
      Type: AWS::ApiGateway::RestApi
      Properties:
        Name: ${self:service}-${self:provider.stage}

package:
  individually: true

custom:
  prefix: ${self:service}-${self:provider.stage}

functions:
  lambda_function:
    name: ${self:custom.prefix}-lambda_function
    handler: lambda_function.lambda_handler
    events:
      - http:
          integration: lambda-proxy
          path: hello-world
          method: GET
          cors: true
      - http:
          integration: lambda-proxy
          path: hello-world/{id}
          method: GET
          cors: true
      - http:
          integration: lambda-proxy
          path: hello-world
          method: POST
          cors: true
```

## 何をするか簡単に解説

* サービス名は `qiita-lambda-apigateway`
* リージョンは東京
* ランタイムは Python3.6
* タイムアウトは30秒
* メモリは128MB確保
* 関数 `lambda_function` を定義
    * ハンドラーは `lambda_function.lambda_handler`
    * イベント定義は3つ
        * HTTP (API Gateway)
            * GET /hello-world
            * GET /hello-world/{id}
            * POST /hello-world
            * CORSをすべて有効にする

# 早速デプロイする

ターミナルを開き、serverless.ymlのあるディレクトリで、以下のコマンドを実行します。

```bash
serverless deploy
# serverless deploy --verbose  # 詳細を見たいとき
```

このような感じでログが出力されます。

```
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
CloudFormation - CREATE_IN_PROGRESS - AWS::CloudFormation::Stack - qiita-lambda-apigateway-prod
CloudFormation - CREATE_IN_PROGRESS - AWS::S3::Bucket - ServerlessDeploymentBucket
CloudFormation - CREATE_IN_PROGRESS - AWS::S3::Bucket - ServerlessDeploymentBucket
CloudFormation - CREATE_COMPLETE - AWS::S3::Bucket - ServerlessDeploymentBucket
CloudFormation - CREATE_COMPLETE - AWS::CloudFormation::Stack - qiita-lambda-apigateway-prod
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (6.73 KB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
CloudFormation - UPDATE_IN_PROGRESS - AWS::CloudFormation::Stack - qiita-lambda-apigateway-prod
CloudFormation - CREATE_IN_PROGRESS - AWS::Logs::LogGroup - LambdaUnderscorefunctionLogGroup
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::RestApi - ApiGatewayRestApi
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::RestApi - ApiGatewayRestApi
CloudFormation - CREATE_IN_PROGRESS - AWS::Logs::LogGroup - LambdaUnderscorefunctionLogGroup
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::RestApi - ApiGatewayRestApi
CloudFormation - CREATE_COMPLETE - AWS::Logs::LogGroup - LambdaUnderscorefunctionLogGroup
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Resource - ApiGatewayResourceHelloDashworld
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - LambdaUnderscorefunctionLambdaFunction
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Resource - ApiGatewayResourceHelloDashworld
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Resource - ApiGatewayResourceHelloDashworld
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - LambdaUnderscorefunctionLambdaFunction
CloudFormation - CREATE_COMPLETE - AWS::Lambda::Function - LambdaUnderscorefunctionLambdaFunction
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodHelloDashworldOptions
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Resource - ApiGatewayResourceHelloDashworldIdVar
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodHelloDashworldOptions
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Resource - ApiGatewayResourceHelloDashworldIdVar
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Resource - ApiGatewayResourceHelloDashworldIdVar
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Permission - LambdaUnderscorefunctionLambdaPermissionApiGateway
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodHelloDashworldGet
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodHelloDashworldPost
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Method - ApiGatewayMethodHelloDashworldOptions
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Permission - LambdaUnderscorefunctionLambdaPermissionApiGateway
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodHelloDashworldGet
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodHelloDashworldPost
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Method - ApiGatewayMethodHelloDashworldGet
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Method - ApiGatewayMethodHelloDashworldPost
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodHelloDashworldIdVarGet
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodHelloDashworldIdVarGet
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodHelloDashworldIdVarOptions
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Method - ApiGatewayMethodHelloDashworldIdVarGet
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodHelloDashworldIdVarOptions
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Method - ApiGatewayMethodHelloDashworldIdVarOptions
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Deployment - ApiGatewayDeployment1539166300519
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Deployment - ApiGatewayDeployment1539166300519
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Deployment - ApiGatewayDeployment1539166300519
CloudFormation - CREATE_COMPLETE - AWS::Lambda::Permission - LambdaUnderscorefunctionLambdaPermissionApiGateway
CloudFormation - UPDATE_COMPLETE_CLEANUP_IN_PROGRESS - AWS::CloudFormation::Stack - qiita-lambda-apigateway-prod
CloudFormation - UPDATE_COMPLETE - AWS::CloudFormation::Stack - qiita-lambda-apigateway-prod
Serverless: Stack update finished...
Service Information
service: qiita-lambda-apigateway
stage: prod
region: ap-northeast-1
stack: qiita-lambda-apigateway-prod
api keys:
  None
endpoints:
  GET - https://**********.execute-api.ap-northeast-1.amazonaws.com/prod/hello-world
  GET - https://**********.execute-api.ap-northeast-1.amazonaws.com/prod/hello-world/{id}
  POST - https://**********.execute-api.ap-northeast-1.amazonaws.com/prod/hello-world
functions:
  lambda_function: qiita-lambda-apigateway-prod-lambda_function

Stack Outputs
ServiceEndpoint: https://**********.execute-api.ap-northeast-1.amazonaws.com/prod
ServerlessDeploymentBucketName: qiita-lambda-apigateway-serverlessdeploymentbuck-**********
```

よく見ると、CloudFormationが動いているようですね。

…はい、Serverless Frameworkは、あの鬼のようにややこしいCloudFormationのテンプレートを作ってくれて、実行してくれるツールです。

# 確認

それぞれ、コンソールを覗いてみましょう。

## Lambda

![sls01.png](https://qiita-image-store.s3.amazonaws.com/0/143240/1d8a38ad-3a15-6a29-c5c6-db836fc19a0c.png)

赤枠で囲ったところに、CloudFormationのスタックに属していることが表示されていますね。

## API Gateway

![sls02.png](https://qiita-image-store.s3.amazonaws.com/0/143240/41ce8060-68c1-36d1-ba36-4c76145067d8.png)

3つ設定したエンドポイントと、CORS用のOPTIONS、計5つが登録されています。
また、このエンドポイントは、↑のLambdaを呼び出すように設定されています。

## CloudWatch Logs

![sls03.png](https://qiita-image-store.s3.amazonaws.com/0/143240/4d76daf3-db2f-5629-03bb-fc33ac255846.png)

ロググループも自動で作られます。
何も実行していないので、ストリームは空です。

# まとめ

手動設定とはさよならしましょう！

前職の新人時代、製品のデプロイを任されたときは、手動デプロイしか方法がなかったのですが（今ならもう少し考えます…）、よく設定を忘れたり、ファイルを置き忘れたりして、市場問題と言われて叱責されたことが多々…。

そんなところで神経すり減らすより、使える自動化ツールはどんどん活用しちゃいましょう！