概要
AWS CloudWatch Metrics でいろいろリソースの状況が確認できますが、Discord に送ることができたら、簡単にスマホでリソース監視ができるので、やってみたいと思います。
ステップ
1. Discord Webhook
Discord Webhook の URL 作成、作ったあとに
みたいなリンクが取得できるので、その Webhook URL を使います。
マニュアル:https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks
2. CloudWatch Metrics
CloudWatch Metrics にアクセスするして、ほしいグラフを設定してから、Source のタブで直接設定JSONがもらえます。すごく便利な機能でそのまま貼り付けると使えます。もちろん公式ドキュメントもあるので合わせて読んだらもっと理解できると思います。
マニュアル:https://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_GetMetricData.html
3. Lambda を作成
CloudWatch Metricsを取得して、Discord Webhook で送る Lambda を作ります。role がわからない/テストの時に、一旦 CloudWatchReadOnlyAccess で良いかもしれません。
ソースコード紹介
Metrics の設定
今回は Invocations, Errors, Duration 三つの Metrics を取得
metrics = {
"Invocations": [
[ "AWS/Lambda", "Invocations", "FunctionName", "funtion1", { "period": 900, "stat": "Sum", "region": "ap-northeast-1" } ],
[ "...", "funtion2", { "period": 900, "stat": "Sum", "region": "ap-northeast-1" } ],
[ "...", "funtion3", { "period": 900, "stat": "Sum", "region": "ap-northeast-1" } ],
[ "...", "funtion4", { "period": 900, "stat": "Sum", "region": "ap-northeast-1" } ]
],
"Errors": [
[ "AWS/Lambda", "Errors", "FunctionName", "funtion1", { "period": 300, "stat": "Sum", "region": "ap-northeast-1" } ],
[ "...", "funtion2", { "period": 300, "stat": "Sum", "region": "ap-northeast-1" } ],
[ "...", "funtion3", { "period": 300, "stat": "Sum", "region": "ap-northeast-1" } ],
[ "...", "funtion4", { "period": 300, "stat": "Sum", "region": "ap-northeast-1" } ]
],
"Duration": [
[ "AWS/Lambda", "Duration", "FunctionName", "funtion1", { "period": 300, "stat": "Average", "region": "ap-northeast-1" } ],
[ "...", "funtion2", { "period": 300, "stat": "Average", "region": "ap-northeast-1" } ],
[ "...", "funtion3", { "period": 300, "stat": "Average", "region": "ap-northeast-1" } ],
[ "...", "funtion4", { "period": 300, "stat": "Average", "region": "ap-northeast-1" } ]]
],
}
Metrics の設定(続き)
画像の大きさなどの共通設定はこちらに設定
for metric, _ in metrics.items():
widget_definition = json.dumps(
{
"liveData": False,
"stacked": False,
"width": 800,
"height": 400,
"start": '-PT3H',
"end": "P0D",
"timezone": '+0900',
"view": "timeSeries",
"metrics": metrics[metric],
}
)
画像の取得
get_metric_widget_image を使用して画像をローカルに保存
response = cloudwatch.get_metric_widget_image(MetricWidget = widget_definition)
image_path = f'/tmp/{metric}.png'
with open (image_path, 'wb') as f:
f.write(response["MetricWidgetImage"])
Discord Webhook
Webhook URL を使って、ローカルに保存した画像を Discord に送る
webhook_url = os.getenv('discord_webhook_url')
discord = Discord(url=webhook_url)
with open(image_path, 'rb') as f:
discord.post(username="monitoring", content=metric, file={ "attachment": f })
全体ソースコード
import json
import boto3
from discordwebhook import Discord
import time
import os
def lambda_handler(event, context):
cloudwatch = boto3.client('cloudwatch')
metrics = {
"Invocations": [
[ "AWS/Lambda", "Invocations", "FunctionName", "funtion1", { "period": 900, "stat": "Sum", "region": "ap-northeast-1" } ],
[ "...", "funtion2", { "period": 900, "stat": "Sum", "region": "ap-northeast-1" } ],
[ "...", "funtion3", { "period": 900, "stat": "Sum", "region": "ap-northeast-1" } ],
[ "...", "funtion4", { "period": 900, "stat": "Sum", "region": "ap-northeast-1" } ]
],
"Errors": [
[ "AWS/Lambda", "Errors", "FunctionName", "funtion1", { "period": 300, "stat": "Sum", "region": "ap-northeast-1" } ],
[ "...", "funtion2", { "period": 300, "stat": "Sum", "region": "ap-northeast-1" } ],
[ "...", "funtion3", { "period": 300, "stat": "Sum", "region": "ap-northeast-1" } ],
[ "...", "funtion4", { "period": 300, "stat": "Sum", "region": "ap-northeast-1" } ]
],
"Duration": [
[ "AWS/Lambda", "Duration", "FunctionName", "funtion1", { "period": 300, "stat": "Average", "region": "ap-northeast-1" } ],
[ "...", "funtion2", { "period": 300, "stat": "Average", "region": "ap-northeast-1" } ],
[ "...", "funtion3", { "period": 300, "stat": "Average", "region": "ap-northeast-1" } ],
[ "...", "funtion4", { "period": 300, "stat": "Average", "region": "ap-northeast-1" } ]]
],
}
for metric, _ in metrics.items():
widget_definition = json.dumps(
{
"liveData": False,
"stacked": False,
"width": 800,
"height": 400,
"start": '-PT3H',
"end": "P0D",
"timezone": '+0900',
"view": "timeSeries",
"metrics": metrics[metric],
}
)
response = cloudwatch.get_metric_widget_image(MetricWidget = widget_definition)
image_path = f'/tmp/{metric}.png'
time.sleep(1)
with open (image_path, 'wb') as f:
f.write(response["MetricWidgetImage"])
webhook_url = os.getenv('discord_webhook_url')
discord = Discord(url=webhook_url)
with open(image_path, 'rb') as f:
discord.post(username="monitoring", content=metric, file={ "attachment": f })
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}