AWSへServerlessFrameworkでアプリケーションデプロイしている時のTips

Serverless Frameworkをいくつかのプロジェクトで使ってみたので、いくつかTipsをまとめておきます。


# 同じyaml定義で複数環境にデプロイする
ServerlessFrameworkでは `--stage develop` のようにstageを指定してコマンドを実行することで複数環境をデプロイすることが可能になります。

ただ、 `stage` - `division` - `team` のように複数階層でデプロイかける時に少し工夫が必要でした。


stageがただの変数以上の意味を持つので何も考えず、以下のように実行してしまうと、Export名が重複してしまったり不具合が出てきました。

```sh
sls deploy --stage prod --division dev101 --team team101
```

stageの値ですが、いかのような優先順位で定義されます。


```yaml
${opt:stage, self:provider.stage, "dev"}.
```

https://www.serverless.com/framework/docs/providers/aws/guide/variables/
https://github.com/serverless/serverless/blob/v2.67.0/lib/configuration/variables/sources/instance-dependent/get-sls.js#L25-L30

つまり極端な方法を採るとすると、以下のように実行すれば上手くいきます。ただ、この方法だと `devision` や `team` の変数の取り回しが上手くいきません。

```sh
sls deploy --stage prod-dev101-team101
```

そこで以下のように `stage` の値をyamlの中で定義する方法で対応することにしました。

```yaml
custom:
  stageName: ${opt:stageName}
  division: ${opt:division}
  team: ${opt:team}
  stage: ${self:custom.stageName}-${self:custom.division}-${self:custom.team}
```

この場合は `--stage` の代わりに `--stageName` として引数を渡します。

```sh
sls deploy --stageName prod --division dev101 --team team101
```






# 不要ファイルはパッケージから除外する
デプロイ速度に関わるので不要ファイルを除外します。



```yaml
package:
  patterns:
    - "!node_modules/**"
    - "!venv/**"
```

[デフォルト](https://www.serverless.com/framework/docs/providers/aws/guide/packaging#patterns)でも以下は除外されているようです。

```
.git/**
.gitignore
.DS_Store
npm-debug.log
.serverless/**
.serverless_plugins/**
```

# 古いLambdaを削除
有名ですがLambdaの過去バージョンパッケージがストレージを圧迫する問題です。知らないと古いバージョンが蓄積されていって、75GB（[2021/11/30時点](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/gettingstarted-limits.html)）で時限爆弾のようにエラーを吐いて後で困ることになります。

こちらの[serverless-prune-plugin](https://www.serverless.com/plugins/serverless-prune-plugin)を利用することで、未然に防ぐことが可能です。

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

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


# CloudWatchログの削除設定
CloudWatch Logも設定しないと永遠と溜まっていくので、適切な期間を設定して削除します。


```yaml
provider:
  logRetentionInDays: 30
```

# タグ付け
後でコスト管理できるようにしっかりタグ付けします。


[serverless-plugin-resource-tagging](https://www.serverless.com/plugins/serverless-plugin-resource-tagging)のプラグインを利用すると、関連するもの一通りタグ付けしてくれます。

```yaml
plugins:
  - serverless-plugin-resource-tagging

provider:
  stackTags:
    division: devA
    team: teamA
```

# CloudFormationスタックの説明文修正

何も気にせずデプロイすると全てのスタックが 「`The AWS CloudFormation template for this Serverless application`」と説明文に表示されて何がデプロイされているのかわかりにくくなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116068/5b35f338-fdab-60ab-d540-892e9390419c.png)

結局はCloudformationなのでresoucesのところにDescriptionを入れることで説明文を変更可能です。


```yaml
resources:
  Description: My API Stack ${self:custom.stage}
```

外部ファイルとすることも可能です。

```yaml
resources:
  - ${file(./description.yml)}
```

その場合、外部ファイルはDescriptionだけでもOKです。

```description.yml
Description: My API Stack ${self:custom.stage}
```
# デプロイ環境
以下のようなDockerfileからビルド用のコンテナを作成してデプロイを行っています。

PythonでLambdaを書いて `lambci/lambda:build-python3.8` を使っています。

lambciのイメージは少し重いですがCの拡張ライブラリをLambdaで使う場合、このイメージからビルドすると動かしやすいので使っています。

```docker
FROM lambci/lambda:build-python3.8

ARG SLS_VERSION=2.67.0

# EC2のIAMロールを利用する場合は通常無効化
# ~/.aws/configを利用する場合は有効化
#ENV AWS_SDK_LOAD_CONFIG 1

RUN curl -sL https://rpm.nodesource.com/setup_17.x | bash -
RUN yum install -y nodejs 
RUN npm install -g serverless@${SLS_VERSION}

RUN mkdir /opt/sls
WORKDIR /opt/sls
```