Datadogに連携させるLambdaを増やしすぎるとポリシーサイズでエラーが出た話
はじめに
AWSのLambdaのログをDatadogに連携させて使っていますが、最近それ関係でエラーに遭遇した際の対処法をまとめます。
具体的には、Lambdaのリソースベースのポリシーが肥大化し、上限値に達してしまったことが直接原因でした。
CDKのHigh Level Constructを用いていたこが間接的に影響していたので、その辺りにも触れたいと思います。
予備知識
Datadogとの連携方法については、自分の個人ブログの方に記載してます。
この方法を用いると、AWSアカウント側にDatadogにログを送信する用のLambdaが自動的に作成されます。これを以下Forwardarとします。
このForwardaを、Cloud Watch Logsのサブスクリプションフィルターに設定することで、
【Lambdaのログ蓄積】→【サブスクリプションフィルター経由でForwardarに伝達】→【Datadogに反映】という動きを取ることができます(下図)。

発生したエラー
発生したエラーは「The final policy size is bigger than the limit」というものです。
これは、公式にも言及している記事がありました。
それによると
Lambda 関数のリソースベースのポリシーが 20 KB を超える場合、Lambda は、[The final policy size is bigger than the limit」エラーを返します。
とあります。
原因となったコード
だいぶ端折っていますが、下記が原因となっていた実際のCDKのコードです。
作成したFunction中のlogGroupに対してaddSubscriptionFilterを呼び出して、サブスクリプションフィルターを作成しています。
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as logsn from 'aws-cdk-lib/aws-logs-destinations';
// ...略
const datadogForwarder; // Datadogから提供されたForwardar関数
const functionId = 'HogeFunction1';
// Lambda Functionの作成
const function = new lambda.Function(this, functionId, {
runtime: lambda.Runtime.NODEJS_16_X,
handler: 'index.handler',
code: lambda.Code.fromAsset(path.join(__dirname, 'hoge-function1')),
});
function.logGroup.addSubscriptionFilter(`${functionId}ErrorLog`, {
filterPattern: logs.FilterPattern.literal("ERROR"),
destination: new logsn.LambdaDestination(datadogForwarder)
})
ここでいうlogGroupは所謂High Level Constructに相当します。
CDKではAWSの特定のリソースを作成する記法が複数あり、High Level Constructは「複数のリソースをまとめて作成する」ような振る舞いをします。便利関数のようなものです。
具体的に説明すると、CloudWatchのログを利用するLambdaのサブスクリプションフィルターを作成するには、最低以下のリソースに変更が必要です。
- サブスクリプションフィルターそのもの
-
Lambdaのリソースベースのポリシーに当該のCloudWatchのログから呼び出しを許可する権限
CloudFormationを用いる場合等は上記2つを記載する必要がありますが、CDKではこれらの記載を簡略するために、それらをラップした便利関数・クラスが豊富に用意されています。それがHigh Level Constructです。
ここでいうlogGroup.addSubscriptionFilterは内部的に
- サブスクリプションフィルターそのものの作成
-
Lambdaのリソースベースのポリシーに当該のCloudWatchのログから呼び出しを許可する権限
という2種類の変更を行ってくれるのですが、これが曲者で、
自動でForwardarのリソースベースのポリシーにCloudWatchのログのARNを記載するため、addSubscriptionFilterを行った数だけForwardarのリソースベースのポリシーの行数が嵩んでいきます。これが直接原因で前述の20KBの制限に達してしまいました。
解消後のコード
続いて下記が解消後のコードです。
addSubscriptionFilterをやめて、CfnSubscriptionFilterを用いています。
CfnXXXXの形式はLow Level Constructと呼ばれ、1記述につき必ず1リソースという対応関係にあります。
意図として、
- サブスクリプションフィルターの作成は
CfnSubscriptionFilterで行い -
Forwardarとの連携用のリソースベースのポリシーはaddPermissionを用いて、ワイルドカード指定でおこなう- その結果、リソースベースのポリシーの肥大化は起こらない
という形にしてあります。
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as logs from 'aws-cdk-lib/aws-logs';
// ...略
const datadogForwarder; // Datadogから提供されたForwardar関数
const functionId = 'HogeFunction1';
// Lambda Functionの作成
const function = new lambda.Function(this, functionId, {
runtime: lambda.Runtime.NODEJS_16_X,
handler: 'index.handler',
code: lambda.Code.fromAsset(path.join(__dirname, 'hoge-function1')),
});
// ワイルドカードでsourceArnを指定して、肥大化しないようにする
datadogForwarder.addPermission("DatadogForwarderPermission", {
principal: new iam.ServicePrincipal("logs.amazonaws.com"),
action: "lambda:InvokeFunction",
sourceArn: `arn:aws:logs:us-east-1:${this.account}:log-group:/aws/lambda/*`
});
// CfnSubscriptionFilterを使ってサブスクリプションフィルターを作成
new logs.CfnSubscriptionFilter(stack, `${functionId}ErrorLog`, {
destinationArn: datadogForwarder.functionArn,
filterPattern: 'ERROR',
logGroupName: function.logGroup.logGroupName,
})
Low Level Constructを使っているため、元々のコードに比べて記述は長くなってしまいますが、そもそもリソースベースのポリシーをワイルドカードで記載したいというのはこちらのアプリケーション側の都合であるため、必要であると判断しました。
まとめ
今回はLambdaとDatadogを紐づけた際に発生したエラーについての原因の解説と対処方法について消会しました。
Datadogは抜きにして、似たような構成でAWSを利用する例はあるかと思います。
その際の対処の目安になりましたら幸いです。
Discussion