CloudWatchメトリクスのグラフ画像をSlackに定期通知するLambdaを作る
先日の記事でAWS Chatbotを使って不快指数アラートをSlackに通知できるようになった。
これに加えて、朝起きた時に昨夜のセンサ情報を確認して振り返りができるようにしたい。
ただ、Chatbotの場合はアラートの発生/解決時しか通知ができないため、グラフ画像の定期通知は独自のLambdaを作る必要があった。
AWS SAMでLambda開発
- sam initでテンプレート作成
sam init --runtime nodejs
- s3バケットへの書き込みとCloudWatchの読み込み権限を付与
Scheduleで毎日朝9:00に通知がされるように設定。
# template.yaml
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
sam-app
Sample SAM Template for sam-app
Globals:
Function:
Timeout: 30
Resources:
HomeMetricsImageFunction:
Type: AWS::Serverless::Function
Properties:
Policies:
- S3CrudPolicy:
BucketName: !Ref BucketName
- CloudWatchReadOnlyAccess
CodeUri: src/
Handler: app.lambdaHandler
Runtime: nodejs10.x
Events:
HomeMetricsImage:
Type: Schedule
Properties:
Schedule: cron(0 0 * * ? *) # JST 9:00
Environment:
Variables:
SLACK_WEBHOOK_URL: !Ref SlackWebhookUrl
BUCKET_NAME: !Ref BucketName
Outputs:
HomeMetricsImageFunction:
Description: "Hello World Lambda Function ARN"
Value: !GetAtt HomeMetricsImageFunction.Arn
HomeMetricsImageFunctionIamRole:
Description: "Implicit IAM Role created for Hello World function"
Value: !GetAtt HomeMetricsImageFunctionRole.Arn
Parameters:
BucketName:
Type: String
Default: metrics-image
SlackWebhookUrl:
Type: String
- 日付操作用のライブラリを追加
yarn add date-utils
- lambdaコードの実装
CloudWatchメトリクスからグラフ画像を取得し、s3に保存する。
そして、s3のダウンロードURLを取得してSlackのWebhookで通知する。
// app.js
require("date-utils");
const axios = require("axios");
const aws = require("aws-sdk");
const bucket = process.env.BUCKET_NAME;
const cloudwatch = new aws.CloudWatch();
const s3 = new aws.S3();
const dt = new Date();
const formattedDate = dt.toFormat("YYYYMMDD");
let response;
const metricsImageApi = {
metrics: [
["Home", "RaspberryPiZero/discomfort_index", { period: 60 }],
[".", "RaspberryPiZero/humidity", { period: 60 }],
[".", "RaspberryPiZero/temperature", { period: 60 }]
],
view: "timeSeries",
stacked: false,
title: "BME280",
width: 800,
height: 400,
start: "-PT12H",
end: "P0D",
timezone: "+0900"
};
const postToSlack = async imageUrl => {
const params = new URLSearchParams();
const payload = {
text: "Good morning :home:",
attachments: [
{
fields: [
{
title: `${formattedDate}.png`
}
],
image_url: imageUrl
}
]
};
params.append("payload", JSON.stringify(payload));
const result = await axios.post(process.env.SLACK_WEBHOOK_URL, params);
console.log(result);
};
exports.lambdaHandler = async (event, context) => {
try {
const result = await cloudwatch
.getMetricWidgetImage({ MetricWidget: JSON.stringify(metricsImageApi) })
.promise();
const key = `metrics/${formattedDate}.png`;
await s3
.putObject({
Bucket: bucket,
Key: key,
Body: result.MetricWidgetImage
})
.promise();
const downloadUrl = s3.getSignedUrl("getObject", {
Bucket: bucket,
Key: key,
Expires: 60*60*24*7 // max 7days
});
await postToSlack(downloadUrl);
response = {
statusCode: 200,
body: JSON.stringify({
message: "success"
})
};
} catch (err) {
console.log(err);
return err;
}
return response;
};
metricsImageApiの部分にはメトリクスの表示画面にある「ソース」=>「イメージAPI」で出力される内容を記述する。
- ローカルで動作確認
sam local invoke -e event.json
問題なくSlack通知されることを確認したらデプロイ。
- deploy.shを作成
# deploy.sh
sam package \
--template-file template.yaml \
--output-template-file packaged.yaml \
--s3-bucket {bucket-name}
sam deploy \
--template-file packaged.yaml \
--stack-name home-metrics-image \
--capabilities CAPABILITY_IAM \
--parameter-overrides SlackWebhookUrl=$SLACK_WEBHOOK_URL
- デプロイ実行
./deploy.sh
午前9時になると昨夜の状況がSlack通知される。
これでメトリクスのグラフ画像がSlackに定期通知されるようになり、都度マネージメントコンソールから確認する必要がなくなった。
追記
Expiresの指定より前にトークンが期限切れになってしまう問題があったので、専用のIAMユーザを利用する方法に変更した。