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