LambdaでRDSに任意のSQLをクエリして結果をSNSで送信するServerless Frameworkのテンプレート


![Serverless Framework](https://s3-us-west-2.amazonaws.com/assets.site.serverless.com/images/home_framework_v1_1.png)

# はじめに
* 最近ハマっているServerlessFrameworkでテンプレートシリーズです。
	* [LambdaでEC2をスケジュール起動/停止するServerless Frameworkのテンプレート](https://qiita.com/ukitiyan/items/234a6dda8d6b102feb08)
* 主旨
	* 整合性チェックなり、レコード登録数の結果等をSQLして日次で取得したいニーズは時として浮上します。
	* バッチこさえるのもアレですし、既存アプリのデータソースと別のDBへの接続とかだと萎えてしまうので、こんな時はLambdaでサクッと対応します。
	* 尚、日次でコンテナが消滅するまでの間のコネクション1本という前提であれば、Lambda+RDSの問題には当たらないでしょう。
* ソースは[Github](https://github.com/ukitiyan/notification-query)で公開してます。
* `PrivateSubunet` や `NatGateway` についても触れます。
* 別件ですが、こないだ[JavaプログラマのためのAWSサーバーレスハンズオン-ServerlessFrameworkで画像処理アプリケーションを構築編-](https://qiita.com/tanamimi/items/a7b5f049dd05a3945f6c)といったハンズオンしたんですが、また同様のやりたいなと思ってます。

# 概要

![overview.png](https://qiita-image-store.s3.amazonaws.com/0/39596/8eae33b1-096b-a40e-1a71-86728a8b07d4.png "overview.png")


# 事前準備

* VPC内Lambdaから、VPC外に通信（今回はSNS）する場合、NATがバイパスされているPrivateSubnetに、Lambdaを配置する必要がありますので、そこら辺の事前準備をします。
* 今回はdefault VPCに対してPrivateSubnetを追加したりします。

## PrivateSubnetの作成（ある場合はスキップ）

### ルートテーブルの作成
* VPC -> ルートテーブル からルートテーブルを作成します。
* VPCはdefault VPCを選択します。

![2018-02-06_21h15_52.png](https://qiita-image-store.s3.amazonaws.com/0/39596/5a402f74-6f67-09b1-6559-9a583bbcbd4f.png "2018-02-06_21h15_52.png")


### サブネットの作成
* VPC -> サブネット からサブネットを作成します。

![2018-02-06_21h33_50.png](https://qiita-image-store.s3.amazonaws.com/0/39596/48b37659-d926-3d7f-fd3e-21ac46ed317f.png "2018-02-06_21h33_50.png")

* ルートテーブルタブにて先程作成したルートテーブルに変更します。

![2018-02-06_21h38_39.png](https://qiita-image-store.s3.amazonaws.com/0/39596/8d7ea181-5af7-27f0-fc97-f514f7a21395.png "2018-02-06_21h38_39.png")


## NatGatewayの関連付け（ある場合はスキップ）

### NatGatewayの作成
* VPC -> NATゲートウェイからNATゲートウェイを作成します。
* サブネットはインターネットゲートウェイが紐付いているPublicSubnetを指定します。（先程作成したPrivateSubnetじゃない）。
* Elastic IPは、新しいEIPの作成からで良いです。

![2018-02-06_21h56_50.png](https://qiita-image-store.s3.amazonaws.com/0/39596/c0f9f010-4782-1622-7fad-3e751ccb8528.png "2018-02-06_21h56_50.png")

### NatGatewayの関連付け
* 先程作成したPrivateSubnetに、NATゲートウェイを紐付けます

![2018-02-06_21h59_39.png](https://qiita-image-store.s3.amazonaws.com/0/39596/a08d5816-d376-c926-5167-a285147ea676.png "2018-02-06_21h59_39.png")


## Lambdaに関連付けるRDSアクセス用のSecurity Groupを設定（ある場合はスキップ）

### Security Groupの作成
* Lambdaに設定するSecurityGroupを作成します。
![2018-02-06_22h11_41.png](https://qiita-image-store.s3.amazonaws.com/0/39596/ca05d968-9e78-7369-8156-41dd9560a2c8.png "2018-02-06_22h11_41.png")

### RDSのSecurityGroupの変更
* RDSに設定しているSecurityGroupのインバウンドに先程作成したSecurityGroupを設定します。

![EC2_Management_Console.png](https://qiita-image-store.s3.amazonaws.com/0/39596/63304f85-7ec4-c78c-d298-9a7b8665520f.png "EC2_Management_Console.png")

## SNS Topicを作成（ある場合はスキップ）

### SNS Topicの作成
* SNSメニューから新しいトピックを作成します。

![2018-02-06_22h22_36.png](https://qiita-image-store.s3.amazonaws.com/0/39596/135af091-870f-851c-b647-e84d860e8b3a.png "2018-02-06_22h22_36.png")

* 作成したトピックに任意のサブスクリプションを設定します。

![4E082C5C-BA3A-44A0-B9A7-AE7AA8AC2EF1.png](https://qiita-image-store.s3.amazonaws.com/0/39596/32c4bf75-16cb-97e9-7c11-3f7302858eca.png "4E082C5C-BA3A-44A0-B9A7-AE7AA8AC2EF1.png")


# Serverless FrameworkでDeploy

## 環境のセットアップ
* [以前の記事](https://qiita.com/ukitiyan/items/945d84282726d3fe2c2f)を参考にしてください。
https://qiita.com/ukitiyan/items/945d84282726d3fe2c2f


## Github上のソースからプロジェクト作成

* 以下のGithubからソースをインポートします。

```
$ sls create --template-url https://github.com/ukitiyan/notification-query --path notification-query
Serverless: Generating boilerplate...
Serverless: Downloading and installing "notification-query"...
Serverless: Successfully installed "undefined"
```

* バージョンによっては、 template-url が対応していないので、エラーの場合は以下を実行します:

```
$ sls install --url https://github.com/ukitiyan/notification-query --name notification-query
```

## serverless.yml の修正

* serverless.ymlのL44 周辺のCron設定を適宜修正します。
  * functions.notify.events.schedule.rate: [AWS Lambda - Scheduled EventのCron書式
](https://qiita.com/pyr_revs/items/abd29d9472f4cba9032c)を参考に `UTC` で記載

```yaml:serverless.yml
events:
- schedule:
rate: cron(0 16 * * ? *)
```

* serverless.ymlのL48 周辺の設定を適宜修正します。
  * functions.notify.vpc.securityGroupIds: 事前準備で作成したRDSにアクセスするためのSercurityGroupIdを設定
  * functions.notify.vpc.subnetIds: 事前準備で作成したLambdaが稼働するPrivate Subnetを設定（複数設定推奨）
  * functions.notify.environment.RDS_ENDPOINT: RDSのエンドポイントを設定
  * functions.notify.environment.RDS_DATABESNAME: RDSの接続DB名を設定
  * functions.notify.environment.RDS_USERNAME: RDSの接続ユーザ名を設定
  * functions.notify.environment.RDS_PASSWORD: RDSの接続パスワードを設定
  * functions.notify.environment.SNS_TOPIC: SNSトピック名を設定
  * functions.notify.environment.SNS_SUBJECT: SNSトピック送信時のSubjectを設定

```yaml:serverless.yml
vpc:
securityGroupIds:
- sg-xxxxxxxx
subnetIds:
- subnet-xxxxxxxx
environment:
REGION: ${opt:region}
RDS_ENDPOINT: xxxx.rds.amazonaws.com
RDS_DATABESNAME: xxxx
RDS_USERNAME: xxxx
RDS_PASSWORD: xxxx
SNS_TOPIC: notification-query-xxxx
SNS_SUBJECT: "Notification Query Results"
```

## src/main/resources/query.sql の修正

* 実行したいSQLに変更します。
* SNSのメッセージの上限は 256 KB なので、それ以下の結果になるよう気をつけてください。

```sql:query.sql
SELECT *
FROM customer
WHERE create_date > DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY);
```

## Build+Deploy

* まずはmvnコマンドでビルドします。

```
$ cd notification-query
$ mvn clean package
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building notification-query 1.0.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.6.1:clean (default-clean) @ notification-query ---
～～～～～～
[INFO] Attaching shaded artifact.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 11.371 s
[INFO] Finished at: 2018-02-01T13:56:08+09:00
[INFO] Final Memory: 43M/432M
[INFO] ------------------------------------------------------------------------
```

* 続いてSLSでデプロイします。対象のregionを必ず指定してください。

```
$sls deploy --region ap-northeast-1
Serverless: Packaging service...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
.........
Serverless: Stack update finished...
Service Information
service: notification-query
stage: dev
region: ap-northeast-1
stack: notification-query-dev
api keys:
None
endpoints:
None
functions:
notify: notification-query-dev-notify
```

* SLSでinvokeしてみて、SNS経由でメッセージが配信されるか確認します。
* もしくは、Cron設定時間になるまで待って、問題ないか気にしておきましょう。

```
$sls invoke -f notify --region ap-northeast-1
```

# まとめ
* インスタンスにCronとか書きたくないじゃないですか、ソースをGitで管理して簡単にデプロイしたいじゃないですか、本当SLS素敵ですわと思います。
* 内外からこの手のニーズは良く聞いていたので、本内容でサクッと対応していけたら良いなと思います。