こんにちは、櫻井です。
この記事は、ギークフィード Advent Calendar 2023 第23日目の記事です。
外部から頻繁に利用されるlambdaや、以前紹介した、SQSを使ってlambdaを10秒ごとに定期実行するのような場合に、どのぐらいの期間でどのぐらいlambdaが実行されているかを定期的に確認したい場合があると思います。
今回はそういったケースで使える、定期的に特定のlambdaの実行回数を計測してSlackに通知する方法を紹介します。
目次
SlackでWebhook URLを発行する
特定のlambdaの実行回数を通知する先のSlackのWebhook URLを取得します。
まずはこちらからWebhook URL作成ページに移動します。
次に以下の画像のように、通知をしたいチャンネルを選択し “Incoming Webhook インテグレーションの追加”
をクリックします。
以下のような画面に遷移するので、Webhook URLを控えておきます。
AWSのリソースを作成する
CDK
今回はCDKを使ってリソースの作成を行います。
以下はCDKのソースコードの全体像です。各リソースの解説についてはこのあと行います。
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
import * as cdk from "aws-cdk-lib"; import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs"; import { Construct } from "constructs"; import * as lambda from "aws-cdk-lib/aws-lambda"; import * as iam from "aws-cdk-lib/aws-iam"; import * as events from "aws-cdk-lib/aws-events"; export interface CountLambdaInvocationStackProps extends cdk.StackProps { slackWebhookUrl: string; } export class CountLambdaInvocationStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: CountLambdaInvocationStackProps) { super(scope, id, props); // 実行数をカウントしてSlackに通知するLambda関数 const countLambdaInvocationFunction = new NodejsFunction(this, "CountLambdaInvocationFunction", { entry: "lambda/countLambdaInvocation.js", handler: "handler", environment: { SLACK_WEBHOOK_URL: props?.slackWebhookUrl ?? "", }, runtime: lambda.Runtime.NODEJS_18_X, }); countLambdaInvocationFunction.addToRolePolicy( new iam.PolicyStatement({ actions: ["cloudwatch:GetMetricData", "lambda:ListFunctions"], resources: ["*"], }) ); // 定期実行の設定 const countLambdaInvocationRule = new events.Rule(this, "CountLambdaInvocationRule", { schedule: events.Schedule.rate(cdk.Duration.minutes(10)), targets: [new cdk.aws_events_targets.LambdaFunction(countLambdaInvocationFunction)], }); // 実行数カウント対象のLambda関数 const targetLambdaFunction = new NodejsFunction(this, "TargetLambdaFunction", { entry: "lambda/targetLambda.js", handler: "handler", runtime: lambda.Runtime.NODEJS_18_X, }); const targetLambdaFunction2 = new NodejsFunction(this, "TargetLambdaFunction2", { entry: "lambda/targetLambda.js", handler: "handler", runtime: lambda.Runtime.NODEJS_18_X, }); } } |
実行数をカウントしてSlackに通知するLambdaとEventBridge
CDK抜粋
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// 実行数をカウントしてSlackに通知するLambda関数 const countLambdaInvocationFunction = new NodejsFunction(this, "CountLambdaInvocationFunction", { entry: "lambda/countLambdaInvocation.js", handler: "handler", environment: { SLACK_WEBHOOK_URL: props?.slackWebhookUrl ?? "", }, runtime: lambda.Runtime.NODEJS_18_X, }); countLambdaInvocationFunction.addToRolePolicy( new iam.PolicyStatement({ actions: ["cloudwatch:GetMetricData", "lambda:ListFunctions"], resources: ["*"], }) ); // 定期実行の設定 const countLambdaInvocationRule = new events.Rule(this, "CountLambdaInvocationRule", { schedule: events.Schedule.rate(cdk.Duration.minutes(10)), targets: [new cdk.aws_events_targets.LambdaFunction(countLambdaInvocationFunction)], }); |
“実行数をカウントしてSlackに通知するLambda” の環境変数に先ほど控えたSlackのWebhook URLを設定します。
CDKの環境変数の設定方法はいくつかあります。クラスメソッドさんのこちらのブログがとてもわかり易いので参考にしてみてください。
今回は、定期実行のを行うためのEventBridgeルールのscheduleの設定を10分ごとに設定しています。
間隔を変更したい場合は、 cdk.Duration.minutes()の引数を変更してください。
lambdaの中身については後ほど紹介します。
カウント対象のLambda
CDK抜粋
1 2 3 4 5 6 7 8 9 10 11 |
// 実行数カウント対象のLambda関数 const targetLambdaFunction = new NodejsFunction(this, "TargetLambdaFunction", { entry: "lambda/targetLambda.js", handler: "handler", runtime: lambda.Runtime.NODEJS_18_X, }); const targetLambdaFunction2 = new NodejsFunction(this, "TargetLambdaFunction2", { entry: "lambda/targetLambda.js", handler: "handler", runtime: lambda.Runtime.NODEJS_18_X, }); |
実行数カウント対象のlambdaを2つ作成します。
こちらのlambdaのソースコードも後ほど一応紹介しますが
“実行数をカウントしてSlackに通知するlambda” の動作確認をするためだけのものなので、中身はAWSコンソールでlambdaを作成したときに最初にサンプルで作成されるものと同じです。
Lambda
実行数をカウントしてSlackに通知するLambda
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
import { CloudWatchClient, GetMetricDataCommand } from "@aws-sdk/client-cloudwatch"; import { LambdaClient, ListFunctionsCommand } from "@aws-sdk/client-lambda"; import axios from "axios"; const cloudwatchClient = new CloudWatchClient(); const lambdaClient = new LambdaClient(); export const handler = async (event) => { const now = new Date(); const tenMinutesAgo = new Date(now.getTime() - 60 * 10 * 1000); const targetFunctions = await getTargetLambdaFunctions(); let slackMessageBody = ""; await Promise.all( targetFunctions.map(async (functionName) => { const count = await invocationCount(functionName, now, tenMinutesAgo); slackMessageBody += `過去10分の ${functionName} の実行回数: ${count}回\n`; }) ); console.log("slackMessageBody: ", slackMessageBody); const requestUrl = process.env["SLACK_WEBHOOK_URL"]; console.log("slackMessageBody", slackMessageBody); const requestBody = { text: slackMessageBody, username: "lambda実行回数", }; await axios.post(requestUrl, requestBody); return; }; const invocationCount = async (functionName, endTime, startTime) => { const input = [ { Id: "lambda_execution_count", MetricStat: { Metric: { Namespace: "AWS/Lambda", MetricName: "Invocations", Dimensions: [ { Name: "FunctionName", Value: functionName, }, ], }, Period: 60, Stat: "Sum", Unit: "Count", }, }, ]; const GetMetricDataInput = { MetricDataQueries: input, StartTime: startTime, EndTime: endTime, }; const getMetricDataCommand = new GetMetricDataCommand(GetMetricDataInput); const response = await cloudwatchClient.send(getMetricDataCommand); const totalExecutions = response.MetricDataResults[0].Values.reduce((a, b) => a + b, 0); return totalExecutions; }; const getTargetLambdaFunctions = async () => { // Lambdaの一覧を取得 let marker = undefined; const lambdaFunctions = []; do { const listFunctionsCommandInput = { Marker: marker, }; const command = new ListFunctionsCommand(listFunctionsCommandInput); const response = await lambdaClient.send(command); marker = response.NextMarker; lambdaFunctions.push(...response.Functions.map((f) => f.FunctionName)); } while (marker); // lambdaFunctionsの中からTargetLambdaFunctionを含むものを抽出 const targetFunctions = lambdaFunctions.filter((f) => f.includes("TargetLambdaFunction")); console.log("targetFunctions: ", targetFunctions); return targetFunctions; }; |
このlambdaでは以下を行っています。
- getTargetLambdaFunctions() で TargetLambdaFunction という文字列が含まれているlambda関数の一覧を取得
- invocationCount(functionName, endTime, startTime) でカウント対象のlambdaの過去10分間の合計実行回数を取得
- 取得したカウント対象のlambdaの過去10分間の合計実行回数をSlackに送信
カウント対象のLambda
1 2 3 4 5 6 7 8 9 |
export const handler = async (event) => { console.log("event", event); return { statusCode: 200, body: JSON.stringify({ message: "Hello from Lambda", }), }; }; |
こちらは”実行数をカウントしてSlackに通知するlambda” の動作確認をするためだけのものなので、中身はAWSコンソールでlambdaを作成したときに最初にサンプルで作成されるものと同じです。
動作確認
CDKのデプロイ後 、AWSコンソールで作成されたカウント対象のlambdaに移動してテスト実行を行います。
TargetLambdaFunctionを3回実行
TargetLambdaFunction2を5回実行しました。
TargetLambdaFunction
TargetLambdaFunction2
しばらく待つと、Slackにメッセージが届きます。
正常に動作していることが確認できました。
まとめ
今回は、定期的に特定のlambdaの実行回数を計測してSlackに通知する方法を紹介しました。
lambdaが本来想定しているより多く動作していないかが心配な場合などに使ってみてください。
- 特定の時間あたりのlambda実行数をSlackに通知する - 2023-12-23
- 公衆電話からでも使える電話帳サービスをLEX + AmazonConnectで作ってみた - 2023-12-19
- SQSを使ってlambdaを10秒ごとに定期実行する - 2023-12-14
- Slack と AWS の電話番号登録アプリ – Bolt + CDK ソースコード解説 - 2023-03-26
- SlackとAWSを組み合わせた電話番号登録アプリの開発 – Boltフレームワーク+CDKで作ってみた - 2023-03-26