見出し画像

#2 【Serverless】 Serverless Frameworkでのスタック分割のすすめ

こんにちは!Queueソフトウェアチームの塩月です。

弊社ではAWSのマイクロサービスを用いて開発を行うことで開発スピードが上がり、サーバーのメンテナンスや環境管理の時間コストを大幅に削減できるようになりました。

とりわけServerless Frameworkではymlを定義すれば変数の値を変えるだけで環境毎にデプロイすることができるため大変便利です。Serverless Frameworkの裏側ではAWS Cloud Formationが走っており、AWS公式によるとAWS Cloud Formationとは、AWSリソースをJSONで記述したテンプレートでリソースの作成や編集、削除が可能です。そのテンプレートで作成された環境群をスタックと呼びます。


一方でserverlessを運用する中でAWS Cloud Formatonのスタックに関して直面した課題があったため今回はseverless.ymlのスタック分割をテーマに紹介したいと思います。

直面した課題

運用を行っているとLambdaのfunction数が増えたりAppSyncのmapping templateを追加するなどがしばしばですが、1つのserverless.ymlで管理していると1箇所しか変更していないのにもかかわらず全てのリソースをデプロイするため時間がかかってしまう点が課題として挙げられました。またAWS Cloud Formationのデプロイスタック数上限は200と決められておりスタックの数は出来る限り抑えた方が良いと言える状況でした。

解決策

結果的に言えば1つのserverless.ymlで管理することに無理があったため、使用するリソースごとにymlを分割し最小単位でデプロイした後、必要に応じて別スタックを参照することで解決することができました。以下は分割した際の例と実際の記述方法をまとめていますので同じような課題を持っている方の参考になればと思います。

serverless.ymlを分割した構成を考えてみる

通常Serverless Frameworkに関して調べてヒットする記事ではLambda + API Gatewayのfunctionに対してserverless.ymlが1つ置かれている以下のようなものが多いと思います。

project
  |-- handler.py
  |-- serverless.yml

テストで試す際にはこの形式で記述すれば問題ないですが、本格的にサービスとして運用することを考えるとfunction数が増えることは無論、AppSync・Cognito・DynamoDB・IAMなどその他のリソースが増えてきたときにはスタック数が増えます。この場合1つのserverless.ymlですべてデプロイするとすぐにスタック上限に達してしまうことが問題になります。

理想の形としては、
- デプロイに時間がかからない(変更箇所のみデプロイできる)
- スタック数上限を気にする必要がない

などが挙げられます。

AppSync・Cognito・DynamoDB・IAM・Lambdaなどのリソースを含めて希望の形をまとめると以下のようになります。

project
  |-- app
  |    |-- api
  |    |   |-- handler.py
  |    |   |-- serverless.yml
  |    |
  |    |-- data
  |    |   |-- handler.py
  |    |   |-- serverless.yml
  |    |
  |    |-- appsync
  |    |   |-- handler.py
  |    |   |-- serverless.yml
  |
  |-- mapping-templates
  |   |-- request.vtl
  |   |-- response.vtl
  |
  |-- schema
  |   |-- schema.graphql
  |
  |-- conf
  |   |-- appsync.yml
  |   |-- dynamodb.yml
  |   |-- cognito.yml

project直下にserverless.ymlを配置する形から各リソース群それぞれにymlを持たせる形に変更しています。
各serverless.ymlに任意のservice名(ex. ◯◯-stack)を付け管理します。
デプロイする際は/api/dataのディレクトリで sls deployを実行します。

/api

lambdaのfunctionを管理します。デプロイする際はdynamodbなどfunction内で参照するリソースを先に作成する必要があります。

/data

主にdynamodbなどデータ関連のリソースを管理します。
参照先となることが多いと思いますので他のスタックより先にデプロイすると良いです。

/appsync

appsyncを管理します。参照先のdynamodbが作成されていなければデプロイに失敗します。AppSyncの認証にcognitoを使用する場合は必要に応じてここに記載しても良いと思います。

/mapping-templates

appsyncで使用するmapping templateファイルを管理します。appsync.ymlから参照すると良いでしょう。

/schema

appsyncのschemaを管理します。mapping templateと同様にappsync.ymlで参照します。

/conf

appsync、dynamodb、cognitoなど各リソースの定義ファイルを記述します。

このようにserverless.ymlを配置し任意のまとまりに分割することでymlの肥大化を防ぐことができます。
注意点としてはyml内での参照先スタックが先に作成されていないとエラーがでるのでデプロイ時に確認するようにします。


severless.ymlを書いてみる

上記の構成を踏まえた上で実際に分割されたserveless.ymlを書いてみます。
まず/conf/dynamodb.ymlの例です。共通部分は省略させて頂きます。

serverlessの基本事項や環境管理や各リソースの記述方法などに興味がある方は以前記事にまとめていますのでそちらをご覧下さい。

#1 【Serverless】 serverless.ymlでの環境管理とAWSリソースの記述方法

Outputs:
   SampleTable:
     Value: SampleTable-${self:provider.stage}

このように記述することで定義したものを出力した後参照できるようにします。

出力したdynamodbのスタックを実際にappsyncにて参照します。

service: ◯◯-stack
provider:
name: aws
runtime: python3.7
stage: ${opt:stage, self:custom.defaultStage}  
profile: ${opt:profile, self:custom.defaultProfile}
region: ${opt:region, self:custom.defaultRegion}
environment:
  env: ${self:provider.stage}    
custom:
defaultStage: dev  
defaultProfile: default
defaultRegion: ap-northeast-1  
otherfile:
  environment:
    dev: # 環境定義ファイルの参照先Path
stackName: ◯◯-datastack
# データスタックのCloudFormation名
cfName: ${self:custom.stackName}-${self:provider.stage}  
appSync:
  - ${file(../../conf/appsync.yml)}
# データスタックの出力値
SampleTable: ${cf:${self:custom.cfName}.SampleTable}
resources:
- ${file(../../conf/cognito.yml)}
plugins:
# プラグインがある場合はここに記述
functions:
handler:
  handler: app/api/handler.sample
  events:
  - http:
    path: /api/sample
    method: GET
    cors: true

分割しないserverless.ymlと異なるのはこの箇所です。

stackName: ◯◯-datastack
# データスタックのCloudFormation名
cfName: ${self:custom.stackName}-${self:provider.stage}  
appSync:
- ${file(../../conf/appsync.yml)}
# スタックの出力値を参照
SampleTable: ${cf:${self:custom.cfName}.SampleTable}

stackNameには参照先となるdata stack名を記述します。
SampleTable/dataのOutputsで出力した値を参照しています。

例として載せているのはDynamoDBとAppSyncですが同様に分割していくことで、AWS Cloud Formationのスタック上限をある程度緩和できるのと同時に最小単位でのデプロイにすることで効率化が図れるのではないでしょうか。Serverless Frameworkに関して運用中に気づいた点やヒントなどがあれば今後も紹介していきたいと思います。


株式会社Queueではソフトウェア開発の記事だけでなく海外のスタートアップイベント報告や独自のマーケティング調査などの記事も書いていますので興味がある方はそちらの記事も読んで頂けると幸いです。

インドネシアで150人のスマホスクリーン覗いてみた

Tech Open Air 2019 参加レポート [Part. 1]

この記事が気に入ったらサポートをしてみませんか?