Serverlessで発生するリソース200個問題について解決方法の紹介

# 導入
**「serverlessでの運用が始まる前に知っておきたかった \_(:3 」∠)_ 」**
**「この問題を、環境を構築していた時の自分に教えたい (;'∀')」‼**

Serverless Framework の大規模運用を始める際に知っておきたい
**「リソース200個問題」**について、その解決方法を整理してみました。

# 要点
- AWSでServerless Frameworkを使うときは複数スタックに分割して、リソース200個問題を回避すべし‼‼
- 複数スタックに分割する方法２つを紹介‼‼
  - スタックをネストさせる方式 ： 比較的簡便にスタック分割が可能。
  - クロススタック参照する方式 ： スタック更新時に他リソースへ影響を与えにくい。

# 想定読者
「AWS CloudFormation 経験者」 && 「Serverless Framework 初心者」<br>(CloudFormationで[ネステッドスタックとクロススタック参照](https://qiita.com/uramotot/items/4a55ccad14f44f5006f6#%E3%82%AF%E3%83%AD%E3%82%B9%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF%E5%8F%82%E7%85%A7%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%A6%E5%85%B1%E6%9C%89%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9%E3%82%92%E3%82%A8%E3%82%AF%E3%82%B9%E3%83%9D%E3%83%BC%E3%83%88%E3%81%97%E3%81%BE%E3%81%99)が分かると良いです。)

# Q&A
__Q1.__
Serverless Frameworkって…そもそも何ですか？　(-。-)y-゜゜゜
__A1.__
PCにインストールしてCUIで操作するフレームワークです。
クラウド上でのサーバーレスインフラ環境の構築を簡単にしてくれます。
AWSに対して使う場合、Serverless Frameworkを実行すると裏側でCloudFormationが実行されます。
競合となるフレームワークとしては、[AWS SAM](https://aws.amazon.com/jp/serverless/sam/)が挙げられます。
私見ですが、Serverless Frameworkでは長いため、Serverlessと略されることが多いように感じます。
Serverless Frameworkの詳細は、[公式のページ](https://serverless.com/)をご参照ください。

__Q2.__
それじゃあ、「リソース200個問題」って何ですか？　(*'▽')
__A2.__
A1でも書いた通り、AWSに対してServerless Frameworkを使うと、裏でCloudFormationが動きます。
CloudFormationには[制約](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html)があり、1つのスタックの中でリソースを200個までしか作れません。
そのため、拡張を続けていきリソースが200個を超えた時、エラーが出力されて更新が失敗するという事象が発生します。
私の所属しているチームでは、この事象を リソース200個問題 と呼んでいます。公式の用語ではありませんので、使用される際はお気を付けください。


# 解決策
ひととおり調べたところ、**リソース200個問題に対する解決策は下記の2つがありました。**
(この2つの方式名も、便宜上、私が個人的に名付けたものであり公式の用語ではありません。ご了承ください。)

- 解決策① ： [ネステッドスタック方式](https://wp-kyoto.net/serverless-plugin-split-stacks)
- 解決策② ： [クロススタック参照方式](https://qiita.com/hamanakamakoto/items/a14fab3c7b9ddbd80fee)



# 解決策① ネステッドスタック方式
- Lambda等のリソースをネステッドスタック内に作成し、元々のスタック(親スタック)から分離する方式です。
- `serverless-plugin-split-stacks`という名前の[Plugin](https://github.com/dougmoscrop/serverless-plugin-split-stacks)を利用します。詳細は[こちらの記事](https://wp-kyoto.net/serverless-plugin-split-stacks)をご参照ください。
- npm でインストールした後に、serverless.yml内の`plugins: `の中で、下記の記述を追加する必要があります。

```yaml:serverless.yml(抜粋)
# プラグイン定義
plugins:
  - serverless-plugin-split-stacks  # npm でPluginをインストールしてからこの行を記述してください。
```
- 必要に応じて、下記の`serverless-plugin-split-stacks`用オプションも追記してください。
- 現時点(2019/11/8)では、`serverless-plugin-split-stacks`の最新バージョンは1.9.3 のため まだ未対応ですが、2.x 系からはオプションが増えて**Lambda毎の自動スタック分割**が可能になるようになるようです。

```yaml:serverless.yml(抜粋)
# serverless-plugin-split-stacksのオプション定義
custom:
  splitStacks:               # serverless-plugin-split-stacksがデプロイ時にこの定義を読み込む
    nestedStackCount: 20     # 作成されるネステッドスタックの個数をコントロール
    perFunction: false       # 2.x系から利用可能になる模様
    perType: false           # Lambda系、WAF系それぞれのタイプごとにスタックを分離するか
    perGroupFunction: true
```
- `serverless deploy -v`で環境が更新されれば、親ネストとネステッドスタックの両方が最新の状態になります。

```shell
# ディレクトリ構成
ルートディレクトリ           # serverless.ymlを更新した時はこのディレクトリ直下で、`serverless deploy -v` を実行する。
    ┣━ serverless.yml       
   (…省略)
```



# 解決策② クロススタック参照方式
- 特別なPlugin は使用しません。例に倣って詳細は[こちらの完成された記事](https://qiita.com/hamanakamakoto/items/a14fab3c7b9ddbd80fee)をご参照ください。
- クロススタック参照の参照先と参照元で別々のプロジェクトディレクトリ/別々のserverless.ymlを用意します。
- スタックどうしが CloudFormation の`Outputs: `で出力されたパラメータ (key-value 形式) を仲立ちとして連携します。

```yaml:serverless.yml(抜粋、参照先スタック：data-stack)
# リソース定義
resources:
  Resources:  
    # (省略)
  Outputs: 
    DynamoDBTableName               # Bucket名を他のスタックが参照できるように出力
      Value: Ref! DynamoTableTable  # AWSマネジメントコンソール → CloudFormationサービス の「出力」タブからも確認できます。
```

```yaml:serverless.yml(抜粋、参照元スタック：func-stack)
functions:
  hello:
    environment: 
      DYNAMODB: ${cf:data-stack.DynamoDBTableName}  # 参照先スタックの出力パラメータを参照してLambda環境変数にセット
```

- 更新が必要なリソースが所属するスタックのみを`serverless deploy -v`を実行します。他のスタックには影響が無い点がメリットです。
- ただし、構築時は参照先(参照される側)のスタックから構築されるように、スタック構築の順番を考える必要があります。

```shell
# ディレクトリ構成
rootLayer/
    ┣━ cacheLayer/          # 更新が必要なリソース用のディレクトリ直下でのみ再デプロイすればOK
    ┃      ┣━ serverLess.yml
    ┃     (…省略)
    ┃         
    ┣━ functionLayer01/     # 更新が必要なリソース用のディレクトリ直下でのみ再デプロイすればOK
    ┃      ┣━ serverLess.yml
    ┃     (…省略)
    ┃         
    ┣━ functionLayer02/     # 更新が必要なリソース用のディレクトリ直下でのみ再デプロイすればOK
    ┃      ┣━ serverLess.yml
    ┃     (…省略)
    ┃         
    ┣━ dataLayer/           # 更新が必要なリソース用のディレクトリ直下でのみ再デプロイすればOK
    ┃      ┣━ serverLess.yml
    ┃     (…省略)
```

# 表にして一覧化

|        | 何も対応しない方式 |   解決策① ネステッドスタック方式    |    解決策② クロススタック参照方式    |
|:-----------------:|:-----------------:|:------------------:|:------------------:|
|    スタック分割するか？     | しない |       する        |        する        |
|       Pluginは必要か？       | 不要 |      必要       |       不要       |
|       serverless.ymlの中で<br>修正が必要な箇所は？       | ― |      `plugins: ～`       |    参照先スタック：`Outputs: ～`<br>参照元スタック：`${cf:～}`       |
|       Lambda毎に<br>スタックを分割できるか？       | できない |      Pluginの<br>バージョン次第<br>(2.x以上)       |    できない     |
|       設定変更時の<br>リスクは？       | 普通 |      普通       |    低い    |
|       設定変更時の<br>デプロイ回数は？       | 1回のみ |      1回のみ       |       更新するスタックの個数分      |


# 結論
- Serverless Frameworkで大規模な環境を構築する際は、AWS(CloudFormation) のリソース数制限に引っかからないように、スタックを分割しておくべし‼
  - スタック分割の方式の内、解決策①「ネステッドスタック方式」は、全体のディレクトリ構成を変更しないため、より簡便に環境に適用できる。
  - 逆に、解決策②はディレクトリごとにスタックを分離するため、特定リソース更新時に他スタックへ影響を与えにくい。


# 参考資料
- (本家公式) [Serverless Framework](https://serverless.com/)
- (入門に最適) [Serverless Frameworkの使い方まとめ - Qiita](https://qiita.com/horike37/items/b295a91908fcfd4033a2)
- (ステップアップ) [Serverless FrameworkでAPIGateway・Lambda・DynamoDBを構築する - Qiita](https://qiita.com/t_okkan/items/546330b5f4da720c71a7)
- (解決策① ネステッドスタック方式) [serverless-plugin-split-stacksで200以上のリソースをServerless Frameworkで管理する](https://wp-kyoto.net/serverless-plugin-split-stacks)
- (解決策② クロススタック参照方式) [serverlessフレームワークで複数スタックに分割して、リスクを軽減する方法](https://qiita.com/hamanakamakoto/items/a14fab3c7b9ddbd80fee)

# Appendix

- [Serverless Frameworkを本番運用する際にやっておいたほうが良い事](https://qiita.com/keitakn/items/22cbc3e00acb79560b87)
- [Serverless Frameworkで環境変数を外部ファイルから読み込み、環境毎に自動で切り替えてみる](https://dev.classmethod.jp/cloud/serverless-framework-conf-change/)

環境構築時に併せてご一読されると良いかと思います。

# 謝辞
上記の参考資料を執筆された皆様、大変勉強になりました。ありがとうございます。
失礼の無いように細心の注意を払って紹介させていただいたつもりですが
至らない点がございましたら、ご指摘いただけますと幸いです。

#### 以上