AWS LambdaにServerless Frameworkを使ってみる

# はじめに

私はこれまでWebコンソールからポチポチと弄っていたのですが、設定を間違えて動かなくなることがあったので、コマンドラインでサクッとできたらいいなぁと思っておりました。
これを解決すべく、素晴らしいフレームワークが公開されておりますので、今回はこれを使ってみます。

執筆にあたり、以下の記事を参考にさせていただきました。
どうもありがとうございます😄

[Serverless Frameworkならすぐ出来るよね AWSでサーバーレス（API+Lambda[java]を簡単セットアップ）](https://qiita.com/ukitiyan/items/945d84282726d3fe2c2f#aws-cli%E3%81%AE%E3%82%BB%E3%83%83%E3%83%88%E3%82%A2%E3%83%83%E3%83%97)

# AWS Lambdaとは

AWS Lambda を使用すれば、サーバーのプロビジョニングや管理なしでコードを実行できます。
課金は実際に使用したコンピューティング時間に対してのみ発生し、コードが実行されていないときには料金も発生しません。

魔法のような言葉が羅列していますが、実際にWebコンソールから使ってみると、ソースコード、ビルド、デプロイがバラバラになり、使い勝手が良くありません。  
そこで、そこら辺のソリューションをフレームワークとして提供してくれるのが[Serverless Framework](https://github.com/serverless/serverless)です。
Serverless Frameworkでは、LambdaのTemplateとして、以下が用意されています。
今回は、aws-java-gradleを採用します。

* aws-nodejs
* aws-python
* aws-java-maven
* aws-java-gradle
* aws-scala-sbt
* aws-csharp

# 事前準備

設定済みの方は不要です。
私の開発環境は、MacOSXとなっております。  
あまり詳しいことは書いておりませんので、ご了承ください。

# 環境のセットアップ
![Serverless Framework](https://s3-us-west-2.amazonaws.com/assets.site.serverless.com/images/home_framework_v1_1.png)


## Serverless Frameworkのインストール

npmコマンドを使って、serverlsss Frameworkをインストールします。

```
$ node -v
v7.5.0
$ npm install -g serverless
$ serverless —version
1.27.2
```

nodeやnpmが使えない方は、[Homebrew](https://brew.sh/index_ja)からインストール可能です。
スクリプトをターミナルに貼り付け実行して下さい。

```
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
```


## AWS CLIのインストール

コマンド内部で、AWS CLIを叩くのでインストールします。
（インストール済みの場合は、読み飛ばしてください）

```
$ pip install awscli
```

必要な権限のあるIAMユーザを用意し、設定をします。
（詳細は調べていません）

```
$ aws configure
AWS Access Key ID [None]:(Access Key)
AWS Secret Access Key [None]:(Secret Key)
Default region name [None]: ap-northeast-1
Default output format [None]:json
```

# アプリケーションの作成

hello world的なものを作成します。

## Serviceの作成

まずはテンプレートを作成します。
プロジェクト名は、`myService`としました。適宜変更してください。

```
$ serverless create --template aws-java-gradle --path myService
Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "/Users/hogehoge/Documents/myService"
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  \___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v1.27.2
 -------'

Serverless: Successfully generated boilerplate for template: "aws-java-gradle"
```

## Eclipseにインポート
Eclipseを起動して、Project Explorer -> 右クリック -> Gradle -> Existing Gradle Projectsで先程作成した「myService」フォルダを選択します。


## serverless.ymlの修正とBuild

serverless.ymlの20行目あたりにある設定を変更します。

```yaml:serverless.yml
provider:
  name: aws
  runtime: java8
  stage: prod
  region: ap-northeast-1
```

せっかくなのでコマンドラインから、ビルド出来るか試してみます。
Eclipseから実行する場合は、Gradleタスクの実行からbuildを選択してください。

```
$ gradle build
Starting a Gradle Daemon (subsequent builds will be faster)

BUILD SUCCESSFUL in 8s
4 actionable tasks: 4 executed

$ ls -l build/libs
total 16
-rw-r--r--  1 hogehoge  staff  4907  5 15 10:04 myService.jar
```


## LambdaのみDeployしてみる

Serveress Frameworkでは、Lambdaのセットアップ、JarのアップロードをCloudFormationを使って実行します。
これで Lambda関数に ```myService-prod-hello``` が出来ていればOKです。
WebコンソールでLambdaを設定して、Jarアップロードして...面倒な作業が不要となります。

```
$ cd myService
$ serverless deploy -v
Serverless: Packaging service...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
CloudFormation - CREATE_IN_PROGRESS - AWS::CloudFormation::Stack - myService-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 - myService-prod
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (1.81 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
CloudFormation - UPDATE_IN_PROGRESS - AWS::CloudFormation::Stack - myService-prod
CloudFormation - CREATE_IN_PROGRESS - AWS::Logs::LogGroup - HelloLogGroup
CloudFormation - CREATE_IN_PROGRESS - AWS::IAM::Role - IamRoleLambdaExecution
CloudFormation - CREATE_IN_PROGRESS - AWS::Logs::LogGroup - HelloLogGroup
CloudFormation - CREATE_IN_PROGRESS - AWS::IAM::Role - IamRoleLambdaExecution
CloudFormation - CREATE_COMPLETE - AWS::Logs::LogGroup - HelloLogGroup
CloudFormation - CREATE_COMPLETE - AWS::IAM::Role - IamRoleLambdaExecution
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - HelloLambdaFunction
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - HelloLambdaFunction
CloudFormation - CREATE_COMPLETE - AWS::Lambda::Function - HelloLambdaFunction
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Version - HelloLambdaVersionAMFqcDHAse48pHCLO4T8bZ0tcLP0S5hwaqiXFZhekc
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Version - HelloLambdaVersionAMFqcDHAse48pHCLO4T8bZ0tcLP0S5hwaqiXFZhekc
CloudFormation - CREATE_COMPLETE - AWS::Lambda::Version - HelloLambdaVersionAMFqcDHAse48pHCLO4T8bZ0tcLP0S5hwaqiXFZhekc
CloudFormation - UPDATE_COMPLETE_CLEANUP_IN_PROGRESS - AWS::CloudFormation::Stack - myService-prod
CloudFormation - UPDATE_COMPLETE - AWS::CloudFormation::Stack - myService-prod
Serverless: Stack update finished...
Service Information
service: myService
stage: prod
region: ap-northeast-1
stack: myService-prod
api keys:
  None
endpoints:
  None
functions:
  hello: myService-prod-hello

Stack Outputs
HelloLambdaFunctionQualifiedArn: arn:aws:lambda:ap-northeast-1:************:function:myService-prod-hello:1
ServerlessDeploymentBucketName: myservice-prod-serverlessdeploymentbucket-n1n0p0oovbe7
```

### テスト

早速動くかテストしてみましょう。
テストケースを作成してから実行します。

```
echo '{"key1":"value1","key2":"value2","key3":"value3"}' > event.json
$ serverless invoke --function hello -p event.json
{
    "statusCode": 200,
    "body": "{\"message\":\"Go Serverless v1.x! Your function executed successfully!\",\"input\":{\"key1\":\"value1\",\"key2\":\"value2\",\"key3\":\"value3\"}}",
    "headers": {
        "X-Powered-By": "AWS Lambda & serverless"
    },
    "isBase64Encoded": false
}
```


## API Gateway+Lambdaの構成でデプロイ

上記手順では、endpoints がありません。
API Gateway+Lambdaとなるよう、設定を変更後にDeployします。
serverless.ymlの51行目周辺のfunction句を書き換えます。

```yaml:serverless.yml
functions:
  hello:
    handler: com.serverless.Handler
    events:
      - http:
          path: users/create
          method: get
```

Deployしてみます。
Amazon API Gatewayに```prod-myService```が作成されます。

```
$ serverless deploy -v
Serverless: Packaging service...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (1.81 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
CloudFormation - UPDATE_IN_PROGRESS - AWS::CloudFormation::Stack - myService-prod
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::RestApi - ApiGatewayRestApi
CloudFormation - UPDATE_IN_PROGRESS - AWS::Lambda::Function - HelloLambdaFunction
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::RestApi - ApiGatewayRestApi
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::RestApi - ApiGatewayRestApi
CloudFormation - UPDATE_COMPLETE - AWS::Lambda::Function - HelloLambdaFunction
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Resource - ApiGatewayResourceUsers
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Resource - ApiGatewayResourceUsers
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Permission - HelloLambdaPermissionApiGateway
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Resource - ApiGatewayResourceUsers
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Permission - HelloLambdaPermissionApiGateway
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Resource - ApiGatewayResourceUsersCreate
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Resource - ApiGatewayResourceUsersCreate
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Resource - ApiGatewayResourceUsersCreate
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodUsersCreateGet
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodUsersCreateGet
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Method - ApiGatewayMethodUsersCreateGet
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Deployment - ApiGatewayDeployment1526347394711
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Deployment - ApiGatewayDeployment1526347394711
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Deployment - ApiGatewayDeployment1526347394711
CloudFormation - CREATE_COMPLETE - AWS::Lambda::Permission - HelloLambdaPermissionApiGateway
CloudFormation - UPDATE_COMPLETE_CLEANUP_IN_PROGRESS - AWS::CloudFormation::Stack - myService-prod
CloudFormation - UPDATE_COMPLETE - AWS::CloudFormation::Stack - myService-prod
Serverless: Stack update finished...
Service Information
service: myService
stage: prod
region: ap-northeast-1
stack: myService-prod
api keys:
  None
endpoints:
  GET - https://hogehoge.execute-api.ap-northeast-1.amazonaws.com/prod/users/create
functions:
  hello: myService-prod-hello

Stack Outputs
HelloLambdaFunctionQualifiedArn: arn:aws:lambda:ap-northeast-1:************:function:myService-prod-hello:1
ServiceEndpoint: https://hogehoge.execute-api.ap-northeast-1.amazonaws.com/prod
ServerlessDeploymentBucketName: myservice-prod-serverlessdeploymentbucket-n1n0p0oovbe7
```

### テスト

endpointに向けてリクエストして、反応するか確認します。

```
curl https://hogehoge.execute-api.ap-northeast-1.amazonaws.com/prod/users/create | jq
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                               Dload  Upload   Total   Spent    Left  Speed
100  1541  100  1541    0     0   7820      0 --:--:-- --:--:-- --:--:--  7822
{
"message": "Go Serverless v1.x! Your function executed successfully!",
"input": {
  "resource": "/users/create",
  "path": "/users/create",
  "httpMethod": "GET",
  "headers": {
    "Accept": "*/*",
    "CloudFront-Forwarded-Proto": "https",
    "CloudFront-Is-Desktop-Viewer": "true",
    "CloudFront-Is-Mobile-Viewer": "false",
    "CloudFront-Is-SmartTV-Viewer": "false",
    "CloudFront-Is-Tablet-Viewer": "false",
    "CloudFront-Viewer-Country": "JP",
    "Host": "**********.execute-api.ap-northeast-1.amazonaws.com",
    "User-Agent": "curl/7.54.0",
    "Via": "2.0 *********.cloudfront.net (CloudFront)",
    "X-Amz-Cf-Id": "************",
    "X-Amzn-Trace-Id": "Root=********",
    "X-Forwarded-For": "XXX.XXX.XXX.XXX, XXX.XXX.XXX.XXX",
    "X-Forwarded-Port": "443",
    "X-Forwarded-Proto": "https"
  },
  "queryStringParameters": null,
  "pathParameters": null,
  "stageVariables": null,
  "requestContext": {
    "resourceId": "***",
    "resourcePath": "/users/create",
    "httpMethod": "GET",
    "extendedRequestId": "******************",
    "requestTime": "15/May/2018:01:27:55 +0000",
    "path": "/prod/users/create",
    "accountId": "************",
    "protocol": "HTTP/1.1",
    "stage": "prod",
    "requestTimeEpoch": *****,
    "requestId": "********-****-****-****-************",
    "identity": {
      "cognitoIdentityPoolId": null,
      "accountId": null,
      "cognitoIdentityId": null,
      "caller": null,
      "sourceIp": "XXX.XXX.XXX.XXX",
      "accessKey": null,
      "cognitoAuthenticationType": null,
      "cognitoAuthenticationProvider": null,
      "userArn": null,
      "userAgent": "curl/7.54.0",
      "user": null
    },
    "apiId": "hogehoge"
  },
  "body": null,
  "isBase64Encoded": false
  }
}
```


## Stackの削除

上記で作成したCloudFormationのStackごと削除できます。

```
$ serverless 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 Frameworkならすぐ出来るよね AWSでサーバーレス（API+Lambda[java]を簡単セットアップ）](https://qiita.com/ukitiyan/items/945d84282726d3fe2c2f#aws-cli%E3%81%AE%E3%82%BB%E3%83%83%E3%83%88%E3%82%A2%E3%83%83%E3%83%97)