環境
"serverless-plugin-split-stacks": "^1.9.3",
"serverless-appsync-plugin": "^1.3.1",
"serverless": "^1.78.1",
appsyncをserverless fwでデプロイするのはつらい
serverless-appsync-plugin でappsyncをデプロイしてみると、以下のようなエラーによく遭遇する。
template may not exceed 460800 bytes in size
具体的には、Resolverをプロビジョンする部分のテンプレートが大きくなりがちという問題がある。
ResolverのCloudFormationを見てみる
"GraphQlResolverQuerylistUsers": {
"Type": "AWS::AppSync::Resolver",
"DependsOn": [],
"Properties": {
"ApiId": {
"Ref": "GraphQlApiApiIdParameter"
},
"TypeName": "Query",
"FieldName": "listCards",
"RequestMappingTemplate": "\n #set( $limit = (中略) n$util.toJson($ctx.result)\n ",
"Kind": "UNIT",
"DataSourceName": {
"Ref": "GraphQlDsUserNameParameter"
}
}
},
こんな感じのJSONがResolverの数だけ並んでいる。
例では略しているが、RequestMappingTemplate
フィールドにvtlのソースがすべて入っているのがサイズの増大に寄与していることがわかる
serverless-plugin-split-stacks
素直に使う
serverless fwでデプロイされるcloudformationテンプレートを分割してくれるやつ。
serverless-appsync-plugin では、以下のように使うよう書いてある。
You can use serverless-plugin-split-stacks to migrate AppSync resources in nested stacks in order to work around the 200 resource limit.
略
Create stacks-map.js in the root folder
module.exports = {
'AWS::AppSync::ApiKey': { destination: 'AppSync', allowSuffix: true },
'AWS::AppSync::DataSource': { destination: 'AppSync', allowSuffix: true },
'AWS::AppSync::FunctionConfiguration': { destination: 'AppSync', allowSuffix: true },
'AWS::AppSync::GraphQLApi': { destination: 'AppSync', allowSuffix: true },
'AWS::AppSync::GraphQLSchema': { destination: 'AppSync', allowSuffix: true },
'AWS::AppSync::Resolver': { destination: 'AppSync', allowSuffix: true }
}
この設定の意味するところはAWS::AppSync::Resolver
タイプのリソースをAppSyncNestedStack
みたいな名前のテンプレートに移動させるというものになる。
ただ、見ての通り、この通りにしたところで、AWS::AppSync::Resolver
タイプのリソースの分割先は一つのままなので、Resolverのリソースが十分に大きい場合、サイズ問題は解決できない。
工夫する
serverless-plugin-split-stacks のドキュメントをよく読むと、動的に移動先のテンプレートを決められるとある。
なのでこれをうまく利用してみる。
結果的に以下のようになった。
// stacks-map.js
const def = {
'AWS::AppSync::ApiKey': { destination: 'AppSyncApi', allowSuffix: true },
'AWS::AppSync::DataSource': { destination: 'AppSync', allowSuffix: true },
'AWS::AppSync::FunctionConfiguration': { destination: 'AppSyncFunction', allowSuffix: true },
'AWS::AppSync::GraphQLApi': { destination: 'AppSyncApi', allowSuffix: true },
'AWS::AppSync::GraphQLSchema': { destination: 'AppSyncSchema', allowSuffix: true },
}
let count = 0
module.exports =
(resource, logicalId) => {
if (resource.Type in def) {
return def[resource] // 何か設定したいとき向け
}
if (resource.Type === 'AWS::AppSync::Resolver') {
count++
return {
destination: `AppSyncResolver${Math.floor(count / 100)}`, // 100個ずつにする
force: true // 既存のリソースが別のテンプレートにあった場合も、強制的に異動させる(Resolverは永続層などではなく単なるソースコードなので、移動しても影響はない)
}
}
// return undefinedすると、デフォルトの分割先が自動的にあてがわれるので特に指定不要
};
すると、デプロイすると以下のようになる。
Serverless: Packaging service...
Serverless: [serverless-plugin-split-stacks]: Summary: 204 resources migrated in to 6 nested stacks
Serverless: [serverless-plugin-split-stacks]: Resources per stack:
Serverless: [serverless-plugin-split-stacks]: - (root): 106
Serverless: [serverless-plugin-split-stacks]: - AppSyncApiNestedStack: 1
Serverless: [serverless-plugin-split-stacks]: - AppSyncFunctionNestedStack: 10
Serverless: [serverless-plugin-split-stacks]: - AppSyncNestedStack: 36
Serverless: [serverless-plugin-split-stacks]: - AppSyncResolver0NestedStack: 99
Serverless: [serverless-plugin-split-stacks]: - AppSyncResolver1NestedStack: 57
Serverless: [serverless-plugin-split-stacks]: - AppSyncSchemaNestedStack: 1
意図したとおり、AppSyncResolver0NestedStack
AppSyncResolver1NestedStack
のように、動的に採番されたテンプレートが生成されるようになった。