JavaScript
AWS
CloudFormation
lambda
serverless

Serverless管理下のリソース達が200制限に達したのでNested Stacksに放り込む(by serverless-plugin-split-stacks)

おーばーびゅー

こんな感じでどちゃくそAPI作っていたら、CloudFormationの制約に当たってしまいdeployできなくなりました...(1 stack内のresource数は最大200まで)
今回はこれを、Nested Stacksなるもので解決していきます。
deployには引き続きServerless Frameworkを用いているので、serverless-plugin-split-stacksというプラグインを導入してdeployするresourceを複数のstackに分けていきます。

環境

macOS Sierra v10.12.6
node.js v9.7.0
yarn v1.5.1
Serverless Framework v1.26.1
serverless-plugin-split-stacks v1.4.1

作業

install

yarn add -D serverless-plugin-split-stacks

すっごい不安定そうな指定...そのうち直します

edit files

serverless.yml内でpluginsの指定

serverless-webpackが入っていますがこれは必須ではなく、私が使っているだけです。
他のpluginはお使いの環境に合わせて適宜指定してください。

serverless.yml
...
plugins:
  - serverless-webpack
  - serverless-plugin-split-stacks
...

splitの仕方をカスタマイズ

運用しているリソース数がやっと200に到達したぐらいであれば以下の作業は行わなくともdeploy出来るかと思います。
私のリソース力は53万400弱まで膨らんでおり、このままではsplitしたNested Stacksでさえ200リソースの制限に引っかかってしまうので、splitのルールを追加で指定していきます。
今回はserverless.ymlのfunctions以下に定義しているfunction名の頭の文字列を見てどのNested Stacksに振り分けるか判断していきます。

下記stacks-map.jsはprojectのトップレベル(serverless.ymlと同階層)に配置してください。
serverless-plugin-split-stacks が自動で読み込みます。

stacks-map.js
const ServerlessPluginSplitStacks = require('serverless-plugin-split-stacks');

ServerlessPluginSplitStacks.resolveMigration = function(
  resource,
  logicalId,
  serverless
) {
  // logicalIdに関数名+リソース名が入ってきます
  // ここにはServerlessがdeployするあらゆるリソースが入ってくるので、'LambdaFunction'や'LambdaPermissionApiGateway'などの多様なリソース名で飛んでくるので、そのprefixのみを見て格納先を判断します
  if (logicalId.startsWith('Programs')) {
    // 返却するobjectのdestinationに振り分け先のNested Stacksのprefixを渡します 
    // e.g.: { destination: 'Programs' }を返却するとProgramsNestedStackが作成され、その中にリソースが格納されます
    return { destination: 'Programs' };
  }

  // 上は冗長に書きましたがこんな感じで並べていけば良いです
  if (logicalId.startsWith('Tags')) return { destination: 'Tags' };

  // Fallback to default:
  return this.stacksMap[resource.Type];
};

こうなる

deployの途中でこういう出力が出ます
(若干消している行があるので、このログのリソース数などは合っていません)

> sls deploy --stage local
...
Serverless: [serverless-plugin-split-stacks]: Summary: 238 resources migrated in to 9 nested stacks
Serverless: [serverless-plugin-split-stacks]:    Resources per stack:
Serverless: [serverless-plugin-split-stacks]:    - (root): 127
Serverless: [serverless-plugin-split-stacks]:    - APINestedStack: 44
Serverless: [serverless-plugin-split-stacks]:    - ProgramsNestedStack: 12
Serverless: [serverless-plugin-split-stacks]:    - TagsNestedStack: 18
Serverless: [serverless-plugin-split-stacks]:    - VersionsNestedStack: 13
...

いぇーーい

注意点

splitのカスタマイズは計画的に

途中で

運用しているリソース数がやっと200に到達したぐらいであれば以下の作業は行わなくともdeploy出来るかと思います。

などと書きましたが、特定のresourceはstackを跨いだ移動が不可能です。
今回使用しているプラグインのREADME.mdからも引用します。(2018/03/14)

Many kind of resources (as e.g. DynamoDB tables) cannot be freely moved between CloudFormation stacks (that can only be achieved via full removal and recreation of the stage)

"今はこれで大丈夫だからとりあえずこれでdeploy..."とかせずに、しっかりご自身の計画でsplitの仕方をカスタマイズしていった方が懸命かと思います。

多分すぐobsolete

こんなQiita記事を書いておいてなんですが、 serverless-plugin-split-stacks はversion 2からLambda function毎にNested Stacksを振り分ける方式になるようです。
その方式をPer Lambdaと呼んでおり、今回使用した方式はPer Typeというものです(実際には関数のprefixで大部分を分けているのでResource Typeによる分割は部分的なものですが)。
目下開発中なようなのでそちらも期待しましょう。

こちらからは以上です。
お疲れ様でした。