この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは、CX事業本部 IoT事業部の若槻です。
Amazon CloudWatch Alarmのアラーム発生時にSNS Topic経由で起動したLambda内で、アラームが発生したメトリクスの直近のデータポイント値を取得したいことがありました。
アラーム発生時に発行されるイベントメッセージには様々な情報が含まれるため、
- そもそもイベントメッセージ内に直近のデータポイント値は含まれているのか?
- 含まれている場合のデータポイント値の取り出し方は?
という観点で今回は検証を行ってみました。
やってみた
実装
AWS CDKで下記構成の実装を行います。
下記はCDK Stackのコードです。
lib/process-stack.ts
import { Construct } from 'constructs';
import {
aws_lambda_nodejs,
aws_sns,
aws_cloudwatch,
aws_cloudwatch_actions,
Duration,
Stack,
StackProps,
aws_lambda_event_sources,
} from 'aws-cdk-lib';
export class ProcessStack extends Stack {
constructor(scope: Construct, id: string, props: StackProps) {
super(scope, id, props);
//CloudWatch Alarm
const sampleAlarm = new aws_cloudwatch.Alarm(this, 'sampleAlarm', {
alarmName: 'sampleAlarm',
metric: new aws_cloudwatch.Metric({
namespace: 'StateMachinePublish',
metricName: 'temperature',
statistic: aws_cloudwatch.Statistic.MAXIMUM,
period: Duration.minutes(1),
}),
evaluationPeriods: 1,
threshold: 5,
comparisonOperator:
aws_cloudwatch.ComparisonOperator.LESS_THAN_OR_EQUAL_TO_THRESHOLD,
});
//SNS Topic
const cloudwatchAlarmNotifyTopic = new aws_sns.Topic(
this,
'cloudwatchAlarmNotifyTopic'
);
//Alarmアクション追加
sampleAlarm.addAlarmAction(
new aws_cloudwatch_actions.SnsAction(cloudwatchAlarmNotifyTopic)
);
//Lambda関数
const sampleFunc = new aws_lambda_nodejs.NodejsFunction(
this,
'sampleFunc',
{
functionName: 'sampleFunc',
entry: 'src/lambda/handler.ts',
}
);
//Lambdaイベントソース追加
sampleFunc.addEventSource(
new aws_lambda_event_sources.SnsEventSource(cloudwatchAlarmNotifyTopic)
);
}
}
下記はLambdaコードです。この中でアラーム発生時に発行されるイベントメッセージをパースし、コンソール出力しています。
src/lambda/handler.ts
interface Event {
Records: { Sns: { Message: string } }[];
}
export const handler = (event: Event): void => {
const message = JSON.parse(event.Records[0].Sns.Message);
console.log(message);
};
CDK Deployして実装をデプロイします。
アラームを発生させてデータポイントが含まれたメッセージを取得してみる
CloudWatch metricのデータポイントの値として1
をPutしてアラームを発生させます。
metric.json
[
{
"MetricName": "temperature",
"Value": 1,
"Unit": "Count"
}
]
$ NAME_SPACE=sampleNameSpace
$ aws cloudwatch put-metric-data \
--namespace $NAME_SPACE \
--metric-data file://metric.json
$ ALARM_NAME=sampleAlarm
$ aws cloudwatch set-alarm-state \
--alarm-name $alarmName \
--state-value "ALARM" \
--state-reason "test"
アラームが発生しました。
するとLambdaが起動し、CloudWatch Logsには次のログが出力されていました。このうちNewStateReason
フィールドに直近のデータポイントの情報(1 datapoint [1.0 (04/06/22 15:04:00)]
)が入っているのが確認できます。
{
AlarmName: 'sampleAlarm',
AlarmDescription: null,
AWSAccountId: 'xxxxxxxxxxxx',
AlarmConfigurationUpdatedTimestamp: '2022-06-04T15:01:50.499+0000',
NewStateValue: 'ALARM',
NewStateReason: 'Threshold Crossed: 1 datapoint [1.0 (04/06/22 15:04:00)] was less than or equal to the threshold (5.0).',
StateChangeTime: '2022-06-04T15:05:18.979+0000',
Region: 'Asia Pacific (Tokyo)',
AlarmArn: 'arn:aws:cloudwatch:ap-northeast-1:xxxxxxxxxxxx:alarm:sampleAlarm',
OldStateValue: 'INSUFFICIENT_DATA',
OKActions: [],
AlarmActions: [
'arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:ProcessStack-cloudwatchAlarmNotifyTopic85FD23D4-VAGGJ15VXMNK'
],
InsufficientDataActions: [],
Trigger: {
MetricName: 'temperature',
Namespace: 'sampleNameSpace',
StatisticType: 'Statistic',
Statistic: 'MAXIMUM',
Unit: null,
Dimensions: [],
Period: 60,
EvaluationPeriods: 1,
ComparisonOperator: 'LessThanOrEqualToThreshold',
Threshold: 5,
TreatMissingData: '',
EvaluateLowSampleCountPercentile: ''
}
}
上記は単一のデータポイントが評価対象の場合です。では評価期間を変更して、複数のデータポイントを評価対象とした場合はどうなるでしょうか。
CloudWatch Alarmの評価期間を1
から3
に変更します。CDK Deployして変更を反映します。
lib/process-stack.ts
//CloudWatch Alarm
const sampleAlarm = new aws_cloudwatch.Alarm(this, 'sampleAlarm', {
alarmName: 'sampleAlarm',
metric: new aws_cloudwatch.Metric({
namespace: 'sampleNameSpace',
metricName: 'temperature',
statistic: aws_cloudwatch.Statistic.MAXIMUM,
period: Duration.minutes(1),
}),
evaluationPeriods: 3,
threshold: 5,
comparisonOperator:
aws_cloudwatch.ComparisonOperator.LESS_THAN_OR_EQUAL_TO_THRESHOLD,
});
複数のデータポイントをPutし、アラームを発生させます。
すると次は2 datapoints [2.0 (04/06/22 15:41:00), 4.0 (04/06/22 15:39:00)]
と2つのデータポイントの値が取得できています。前方のデータポイントが直近のもののようですね。
{
AlarmName: 'sampleAlarm',
AlarmDescription: null,
AWSAccountId: 'xxxxxxxxxxxx',
AlarmConfigurationUpdatedTimestamp: '2022-06-04T15:33:59.042+0000',
NewStateValue: 'ALARM',
NewStateReason: 'Threshold Crossed: 2 datapoints [2.0 (04/06/22 15:41:00), 4.0 (04/06/22 15:39:00)] were less than or equal to the threshold (5.0).',
StateChangeTime: '2022-06-04T15:42:08.298+0000',
Region: 'Asia Pacific (Tokyo)',
AlarmArn: 'arn:aws:cloudwatch:ap-northeast-1:xxxxxxxxxxxx:alarm:sampleAlarm',
OldStateValue: 'INSUFFICIENT_DATA',
OKActions: [],
AlarmActions: [
'arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:ProcessStack-cloudwatchAlarmNotifyTopic85FD23D4-VAGGJ15VXMNK'
],
InsufficientDataActions: [],
Trigger: {
MetricName: 'temperature',
Namespace: 'sampleNameSpace',
StatisticType: 'Statistic',
Statistic: 'MAXIMUM',
Unit: null,
Dimensions: [],
Period: 60,
EvaluationPeriods: 3,
ComparisonOperator: 'LessThanOrEqualToThreshold',
Threshold: 5,
TreatMissingData: '',
EvaluateLowSampleCountPercentile: ''
}
}
NewStateReasonからデータポイント値を取り出す
newStateReasonはString型のため文字列処理をして直近のデータポイント値を取り出す必要があります。ちょっと無理やりですが次のようにすれば、含まれているデータポイントが1つまたは複数のいずれの場合でも直近のものを取り出せました。
> newStateReason="Threshold Crossed: 1 datapoint [1.0 (04/06/22 15:04:00)] was less than or equal to the threshold (5.0)."
> Number(
newStateReason
.split(']')[0]
.split('[')[1]
.split(',')[0]
.split('(')[0]
.trim()
);
1
> newStateReason="Threshold Crossed: 2 datapoints [2.0 (04/06/22 15:41:00), 4.0 (04/06/22 15:39:00)] were less than or equal to the threshold (5.0)."
> Number(
newStateReason
.split(']')[0]
.split('[')[1]
.split(',')[0]
.split('(')[0]
.trim()
);
2
参考
- put-metric-data — AWS CLI 1.25.2 Command Reference
- put-metric-data — AWS CLI 1.25.2 Command Reference
以上