AWS SAMとServerless Frameworkから学ぶAPI Gatewayリソースの分割手法

最近、次男の体重長男の体重 を超え2台目の電動アシストサイクルを
買い足しました。 やっさん でございます。

電動アシストサイクル

今回は、 CloudFormation における API Gateway の定義方法を、
AWS SAMServerless Framework の事例から理解し、
API Gateway リソースの分割方法について模索してみます。

API Gateway HTTPエンドポイントの定義方法について

API Gateway HTTPエンドポイントは、 2種類の定義方法 があります。
それぞれを見ていきます。

その1:「AWS::ApiGateway::RestApi」のBodyに記述

Body定義に、全てのエンドポイントを記述します。
一度に全てのエンドポイントを記述でき、
複数のリソースに分割しないため内容を理解しやすいです。
外部のswaggerファイルを参照することで、記述内容をより簡潔に出来ます。

AWS SAM はこの記述方法を採用しており、
Bodyの定義を省略した場合にも、AWS SAM が自動生成します。

サンプル例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
"ApiGatewayApi1": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Body": {
"info": {
"version": "1.0",
"title": {
"Ref": "AWS::StackName"
}
},
"paths": {
"/v1/test1": {
"get": {
"x-amazon-apigateway-integration": {
"httpMethod": "POST",
"type": "aws_proxy",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${api1.Arn}/invocations"
}
},
"responses": {}
}
}
},
"swagger": "2.0"
},
"Name": "api1"
}
},

その2:複数のリソースに定義

前述のBody定義は利用せず、複数のCloudFormationリソースに分割して定義します。
下記のリソースが登場します。

リソース名 役割
AWS::ApiGateway::RestApi REST API が作成されます
AWS::ApiGateway::Resource API にリソースを作成します
AWS::ApiGateway::Method リクエストを送信する必要があるパラメータと本文を定義する
AWS::ApiGateway::Deployment API Gateway RestApi リソースをステージにデプロイ
AWS::ApiGateway::Stage デプロイするためのステージを作成します

リレーション的な関係は以下のようになります。

1
2
3
4
5
AWS::ApiGateway::RestApi
├ AWS::ApiGateway::Resource
│ └ AWS::ApiGateway::Method
└ AWS::ApiGateway::Deployment
└ AWS::ApiGateway::Stage

Serverless Framework はこの定義方法を採用しており、
RestApiの論理ID を参照すれば ResourceMethod を複数のスタックに容易に分割可能です。

本題:API Gatewayリソースの分割方法

AWS SAM および、 Serverless Framework API Gateway のリソース例から、
リソース分割方法を模索しました。

必ず突き当たる、CloudFormationのリソース数制限200の壁について

CloudFormation は一つのテンプレートに含めることができるリソース数の上限を
200 としており、これは 上限解放申請をすることが出来ない 制限 となります。
AWS SAM および、 Serverless Framework でリソース制限に引っかかる
主な理由は、 API Gateway のHTTPエンドポイントが増えることにあります。

AWS SAMの場合

AWS SAM は、AWS::ApiGateway::RestApi のBodyに
全てのエンドポイントを記述しますので、
リソース数的には余裕があるように一見すると見えます。
しかし 2019年9月15日 時点での仕様として、
API Gateway のリソース と 関連する Lambdaファンクション は同じスタックに
記述しないとデプロイに成功しません。エラーが出力されます。

つまり、単一の API Gateway と 関連する Lambda を含めたリソース数は 200が限界です (2019年9月15日時点) 。

この限界は意外にすぐにやってきます。
なぜなら、 AWS SAMLambda 毎に最低2つのPermissionリソースを作成するため、
Lambda が増えれば増えるほど、Permissionが増え、
リソースの大半を占めるようになります。

AWS SAM はこれについていくつかの Issue が発行されており議論されています。

Serverless Frameworkの場合

Serverless Framework は、Permissionの数は Lambda1:1 であるため、
Permissionの数がリソースの大半を占めるようなことはありません。
しかしながら、 API Gateway 関連の複数のリソースが作成されるため、
場合によっては AWS SAM よりもリソース数を早く消費します。
特に、HTTPエンドポイントが多い大規模なAPIシステムの場合、
Serverless Framework のほうがリソース数の限界 200
すぐに達してしまうでしょう。

しかしながら、 Serverless Framework の場合は AWS SAM と比較して
容易に スタック分割 できるため、実際には AWS SAM よりも
多くのHTTPエンドポイントを含めることが出来ます。これについては後述します。

それぞれのデプロイフレームワークにおける解決策を探る

それでは、それぞれのデプロイフレームワークにおける
解決策を模索していきましょう。

AWS SAM : ベースパスマッピングによる複数のAPI Gatewayリソースの統合

API Gatewayにはベースパスマッピングが利用できます。
これにより、複数のAPI Gatewayリソースを一つのドメインに集約することが出来き、
API Gatewayの標準のURLよりも簡潔で分かりやすくなります。

ベースパスマッピング

ただし、注意点があります。
ベースパスマッピングは複数の階層を含めることができません。
例えば、下記のように分割することはできます。

パスマッピング リソース名:ステージ名
user user-api:dev
company company-api:dev

この場合、https://example.co.jp/user/
https://example.co.jp/company のエンドポイントを利用してアクセスできます。

下記のように分割することは できません。

パスマッピング リソース名:ステージ名
v1/user user-api:dev
v1/company company-api:dev

パスマッピングは / を許可しません。つまり、エンドポイントの設計によっては、
ベースパスマッピングだけでは実現できない場合があります。

Nested Applicationsによる、複数のAPI Gatewayリソースの作成サンプルはこちらです。
aws-sam-nested-application

Serverless Framework : split-stacksを用いたネストされたテンプレートへの分割

Serverless Frameworksplit-stacks プラグインを用いて、
ネストされたスタックを作成することが出来ます。
これにより、複数のAPI Gatewayリソースを作る必要がなく、
LambdaAPI Gateway リソースを別々のスタックに分割できます。

分割方法は現在、下記の選択肢があります。

  • perFunction:Lambdaの単位で分割する
  • perType:リソースのタイプで分割する
  • perGroupFunction:最大スタック数からLambdaをグループに振り分けて分割する

例えば、下記のように perGroupFunction を指定し、最大スタック数を 20 とし、 API Gateway に関連する 3つのLambda をデプロイしてみます。

設定値:

1
2
3
4
5
6
custom:
splitStacks:
perFunction: false
perType: false
perGroupFunction: true
nestedStackCount: 20

この場合リソース分割は下記のような結果になり、Rootが最も少ないリソース数になり適切に分割されているのが分かります。

分割結果:

1
2
3
4
5
- (root): 9
- 11NestedStack: 15
- 15NestedStack: 13
- 4NestedStack: 3
- 6NestedStack: 13

一つ注意点として、 一度分割方法を設定したあとは、分割方法を変更することが出来ません。
ネストされるテンプレートの計画を変更したい場合は一度リソースを再作成する必要があります。

split-stacks による、スタックの分割のサンプルはこちらです。
aws-sls-split-stacks

総評:サーバレスのデプロイフレームワークをより理解し、最適な利用方法を模索しよう

AWS SAMServerless Framework で比較しましたが、
どちらがよいという話はしません。 それぞれの特徴を把握し、
最適な利用方法を模索していくことがもっとも大事です。

サーバーレスの、酸いも甘いも嚙み分けていきましょう!
以上です!

このエントリーをはてなブックマークに追加