特定の時間あたりのlambda実行数をSlackに通知する

こんにちは、櫻井です。

この記事は、ギークフィード 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のソースコードの全体像です。各リソースの解説についてはこのあと行います。

 

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抜粋

// 実行数をカウントして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抜粋

// 実行数カウント対象の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

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

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が本来想定しているより多く動作していないかが心配な場合などに使ってみてください。

 

 

 

この記事が気に入ったら
いいね ! しよう

Twitter で
The following two tabs change content below.
櫻井
櫻井
2022年3月にギークフィードに入社。 エンジニア完全未経験からSAP・SAAを三週間で取得することが出来ました。そのためAWSに関することを中心に記事を作成する予定です。 自分が初心者だからこそわかる、エンジニア未経験の方や、エンジニアを始めたばかりの方の躓きポイントをうまく説明できるように頑張ります。

【採用情報】一緒に働く仲間を募集しています

採用情報
ページトップへ