Serverless Frameworkを本番運用する際にやっておいたほうが良い事

# この記事の概要
[Serverless Framework](https://github.com/serverless/serverless) を本番環境で運用する際にやっておいたほうが良い設定を紹介させて頂きます。

# 前提条件
以下の要件で運用しているという前提でお話させて頂きます。

- AWS Lambdaを利用している事
- Node.js 6.10のランタイムを使用している事
- [Serverless Framework](https://github.com/serverless/serverless) のバージョンは `1.24.1` を利用している事（この記事を書いている 2017-11月時点での最新版）

なお、この記事を書くにあたって開発したのはデータベースにDynamoDBを利用したREST APIです。

# CloudWatch Logsを出力するように設定しておく

[Serverless Framework](https://github.com/serverless/serverless) はLambdaの実行結果を [CloudWatch Logs](http://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/WhatIsCloudWatchLogs.html) にログとして出力します。

しかし、私は開発中ログが出力されていない事がありました。（どういう状況でそうなったかは今でも不明です。）

もしログ出力がされていない場合は `serverless.yml` に以下の記述を追加しましょう。

```yaml:serverless.yml
provider:
  name: aws
  runtime: nodejs6.10
  iamRoleStatements:
    - Effect: Allow
      Action:
        - logs:CreateLogGroup
        - logs:CreateLogStream
        - logs:PutLogEvents
      Resource:
        - "*"
```

IAMロールに CloudWatch Logsへの書き込み権限を与えています。

詳しくは [CloudWatch Logs の権限リファレンス](http://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/permissions-reference-cwl.html) を参照して下さい。

先程は書きましたが通常はログ出力は行われるハズです。
この手順に関してはログ出力がされていない場合のみ行えばOKです。

以下のようにログGroupが作成されていればログ出力が行われています。

<img width="1095" alt="logs.png" src="https://qiita-image-store.s3.amazonaws.com/0/71899/537fc645-c6e0-15eb-a4bd-1cc51a2595fc.png">

ログが出力されていないと、問題が起こった際の調査が非常に困難になるので、必ずログが出力されている事を確認するようにしましょう。

# コールドスタート対策

[Serverless Frameworkで行うLambdaのコールドスタート対策](https://qiita.com/keitakn/items/fac324cd44c4374d82e6) という記事を書かせて頂きました。

[こちらの記事](https://dev.classmethod.jp/cloud/aws/lambda-provisioned-concurrency-coldstart/) に載っているようにProvisioned Concurrencyを設定する事で実行可能なコンテナが消えないように設定を行います。

# CloudWatch Logsの有効期限を設定する

[Serverless Framework](https://github.com/serverless/serverless) が作成するCloudWatch Logsはdefaultでは有効期限なし（つまり無尽蔵に保存される）です。

放っておくと料金がどんどん高くなってしまうので、こちらも対策を行っておいたほうが良いでしょう。

[こちら](https://github.com/serverless/serverless/blob/master/docs/providers/aws/guide/resources.md#override-aws-cloudformation-resource) に載っているように、標準で作成される `/aws/lambda/{service_name}-{stage}-{function_name}` のログGroupの定義を上書きする事で対応を行います。

下記に設定例を記載します。

関数名が `refreshToken` だった場合は `RefreshTokenLogGroup` というResourcesを定義する事で上書きが行えます。 `RetentionInDays: "30"` は30日間保存する設定です。

`WarmUpPluginLogGroup` に関しては コールドスタート対策で入れた [serverless-plugin-warmup](https://github.com/FidelLimited/serverless-plugin-warmup) を導入すると自動で作成される関数です。

こちらの場合は1日分くらいのログが残っていれば十分なので `RetentionInDays: "1"` としてあります。

```yaml:serverless.yml
functions:
  refreshToken:
    handler: build/functions/token.refreshToken
    warmup: true
    events:
      - http:
          path: tokens/refresh
          method: post
          cors: true

resources:
  Resources:
    RefreshTokenLogGroup:
      Properties:
        RetentionInDays: "30"
    WarmUpPluginLogGroup:
      Properties:
        RetentionInDays: "1"
```

## 2018-03-28 追記

Serverless Frameworkv1.26.0以降はより簡潔にCloudWatch Logsの有効期限を設定出来るようになりました。

（参考）[Serverless Framework v1.26.0リリースノート](https://qiita.com/horike37/items/86d1626b3ee1a2e86b9a)

以下のように `provider` セクションに `logRetentionInDays` を設定するだけでOKです。

下記の例では10日で設定しています。

```yaml:serverless.yml
provider:
  logRetentionInDays: 10
```

全ての関数が同じ保存日数であれば問題はないですが、個別の関数毎に有効期限を設定したいという場合はやはりLogGroupのリソースを上書きしてあげる必要があります。

# 総容量が原因でデプロイ出来なくなる事を防ぐ

AWS Lambdaのコードストレージの総容量は75GBが最大でこれ以上の容量になるとデプロイ自体が出来なくなります。

しばらく使っていると下記の画像のように容量がどんどん増えていきます。

![before-prune.png](https://qiita-image-store.s3.amazonaws.com/0/71899/3c07d480-ae2e-65f8-70dd-a6760ae04ac6.png)

これはデプロイ毎に古い関数のバージョンもストレージに残ってしまう為、起こっている現象です。

容量不足になる前に [serverless-prune-plugin](https://github.com/claygregory/serverless-prune-plugin) を利用して対策を行いましょう。

インストール方法は [serverless-prune-plugin](https://github.com/claygregory/serverless-prune-plugin) の通りに進めれば問題ありません。

私の場合は `yarn` を利用して `yarn add serverless-prune-plugin --dev` でインストールを行いました。

インストール完了後 `serverless.yml` に以下のように記述します。

```yaml:serverless.yml
plugins:
  - serverless-prune-plugin

custom:
  prune:
    automatic: true
    number: 3
```

`automatic` はPluginをデプロイ時に自動で動作させる為の設定で、`number: 3` は直近の3つのバージョンまでコードストレージに残すという意味です。

このあたりは各自で調整すると良いでしょう。

設定完了後、 `serverless deploy -v` を実行すると以下のようにデプロイ時のログでPluginが動作している事が確認出来るかと思います。

```
// 中略
Serverless: Prune: Running post-deployment pruning
Serverless: Prune: Querying for deployed versions
Serverless: Prune: authorizer-dev-findClient has 70 additional versions published and 0 aliases, 67 versions selected for deletion
Serverless: Prune: Deleting authorizer-dev-findClient v67...
Serverless: Prune: Deleting authorizer-dev-findClient v66...
Serverless: Prune: Deleting authorizer-dev-findClient v65...
Serverless: Prune: Deleting authorizer-dev-findClient v64...
Serverless: Prune: Deleting authorizer-dev-findClient v63...
Serverless: Prune: Deleting authorizer-dev-findClient v62...
Serverless: Prune: Deleting authorizer-dev-findClient v61...
Serverless: Prune: Deleting authorizer-dev-findClient v60...
```

デプロイ完了後にコードストレージの容量を確認すると以下のように容量が減っている事が確認出来るかと思います。

![after-prune.png](https://qiita-image-store.s3.amazonaws.com/0/71899/57219b4a-72df-fc36-d7d5-fc52709ddb62.png)

# 監視

Serverlessだからと言って監視が不要になるという訳ではありません。

問題が起きた際には素早く検知出来る仕組みを構築する事が必要です。

CloudWatchのカスタムメトリクスで特定のエラーコードが発生した際に [Slack](https://get.slack.help/hc/ja) に通知するような仕組みを作成すると良いでしょう。

サンプルを用意出来れば良かったのですが、時間がなくサンプルの用意が間に合いませんでした。。。
サンプルが完成次第この記事を更新します。

それまでは参考になりそうな記事を記載させて頂きます。

- [API Gateway＋Lambda環境でのCloudWatchメトリクスとアラーム設定のはなし](http://blog.serverworks.co.jp/tech/2017/02/16/apigateway-lambda-cloudwatch-alarm/)
- [サーバーレス・アーキテクチャで構築したシステムの運用はどうやるのか？](https://cloudpack.media/28885)
- [CloudWatch LogsのLambdaによるログ監視](https://dev.classmethod.jp/cloud/aws/notify-error-cloudwatch-logs-with-lambda/)

# まとめ
記事の内容は以上になります。

今後も運用を続けていきますので新たに有効な情報が分かったら、発信して行きたいと思います。

他にも本番時に有効な設定等がありましたら、コメント等で教えて頂けると幸いです。

最後まで読んで頂きありがとうございました。