いろいろなcloud watchアラームの使い方(2) - 通知
まあやっぱアラーム付けたら鳴りっぱなしもアレなんで通知してえよな的な
現状
諸々改造してSimpleUploader3みたいな名前になってるのはおいといて、とりあえずタスクの数が0になっている。オートスケールを解除しているので、これを手動で上げ下げしてみるとしよう。
タスクの数に応じたアラーム
これは実はそのままではデーターが取れない。これはクラスターにて
Container Insitesをonにする必要がる。説明の通りに追加のコストがかかるから実験が終わったらoffにしといてもいいかも。
アラームを作る
https://ap-northeast-1.console.aws.amazon.com/cloudwatch/home?region=ap-northeast-1#alarmsV2:
いつものようにcloudwatchより
ContainerInsightsが増えている。これが68個ある。個数に応じて課金されるみたいだから必要ないサービスとかは消した方がいいんだろうけどねえ
Taskの数はどうもクラスターが全て把握するよなので、これを選択するっぽい
こんな感じでタスクが1よりデカくなったらアラームにしてみよう。監視周期1分とした。
SNSの登録(メール通知)
SNSといってもソーシャルなんとかじゃなくてSimple Notification Serviceらしい。
こんな感じでトピックの作成とするとメールがとんでくる
もちろんConfirmする
まこのconfirmationをやってる間アラームを作り切ってしまう。ここではNumberOfTasksGreaterThan1 とかした
実験ぞい
タスクの数を2にして暫く放置する。これだけ。うまくいけばアラーム状態となり、メールが飛んでくるだろう。
ちなみにタスクの数を2以下にしたときのalermは書いてないから、そういうのも欲しかったら頑張ってみてちょ。
lambdaを使う
ここまではemail通知しか行っていなかったが、実はこの通知は単純にlambdaにブン投げてそこで諸々の操作をする事ができる。まあシンプルなのはslackに通知とかだろう。なお、lambdaを使うので
こんな感じの事ができる。ここでは一番上の通知送信においてslackを使う(emailはそもそもテンプレで事足りているしっていう)
slack通知
の前にSlack の Incoming Webhook 設定
https://slack.com/services/new/incoming-webhook
こんなURLに存在している。しかしこれは古い
https://api.slack.com/apps
こちらからappを作るのが推奨されておる。
何か適当に作って
Incoming WebhooksをOnにする事。
そうすると
Webhook URLが発行されるだろう。
curlでテスト
シェルにて
curl -X POST -H 'Content-type: application/json' --data '{"text":"Hello, World!"}' https://hooks.slack.com/services/*****
みたいな形でtestメッセージが届く事を確認しておく。
AWS Lambda 関数の作成
https://ap-northeast-1.console.aws.amazon.com/lambda/home?region=ap-northeast-1#
まあ何でもいいすよ
親の顔より見慣れたダッシュボード
import json
import urllib.request
def lambda_handler(event, context):
webhook_url = "YOUR_SLACK_WEBHOOK_URL"
slack_message = {
'text': json.dumps(event)
}
req = urllib.request.Request(webhook_url, data=json.dumps(slack_message).encode('utf-8'), headers={'Content-Type': 'application/json'})
response = urllib.request.urlopen(req)
return {
'statusCode': 200,
'body': json.dumps('Message posted to Slack')
}
webohook_urlは書き換えといて。でDeployしたあとに
testすると
eventの引数がそのまんまslackに飛んでくればok
alermに組込む
そしたらもうalermに組みこむだけ(本当か?)
メールの通知は残しておくとemailとslack両方通知される。必要なければ消しとけっていうんだけど、ここではまだ残しておくのを推奨する。
ここで再度タスクの数を「2」とかにして放置しておこう。まあメールは飛んでくるだろうが、slackは?
まあある程度AWSをシバいてるとわかると思うんだけど、こういうのはイチイチ権限が必要なのだ。まあそりゃそうだよね。勝手にlambdaでいろいろ操作されたらマズいでしょう。ここではまたlamdaのコンソールに戻るよ
とか。
下の方に「リソースベースのポリシーステートメント」ってのがあるいから権限を追加するわさ
まず徐にInvokeFunctionを見つけにいく。で、プリンシパルには「lambda.alarms.cloudwatch.amazonaws.com」を設定する。まあそういうもんらしい。
IDは適当についけといて。で、またalermのon/off確認テスト。インフラはテスト&テスト&テストやね。
ボヤカスのが面倒くさくなってきたけど(まあaccountidくらい対した話じゃねえのかな…)このようにeventのフルダンプが送られてくる
{
"source": "aws.cloudwatch",
"alarmArn": "arn:aws:cloudwatch:ap-northeast-1:****:alarm:NumberOfTasksGreaterThan1",
"accountId": "****",
"time": "2024-02-11T23:22:42.361+0000",
"region": "ap-northeast-1",
"alarmData": {
"alarmName": "NumberOfTasksGreaterThan1",
"state": {
"value": "ALARM",
"reason": "Threshold Crossed: 1 out of the last 1 datapoints [2.0 (11/02/24 23:21:00)] was greater than the threshold (1.0) (minimum 1 datapoint for OK -> ALARM transition).",
"reasonData": "{\"version\":\"1.0\",\"queryDate\":\"2024-02-11T23:22:42.358+0000\",\"startDate\":\"2024-02-11T23:21:00.000+0000\",\"statistic\":\"Average\",\"period\":60,\"recentDatapoints\":[2.0],\"threshold\":1.0,\"evaluatedDatapoints\":[{\"timestamp\":\"2024-02-11T23:21:00.000+0000\",\"sampleCount\":1.0,\"value\":2.0}]}",
"timestamp": "2024-02-11T23:22:42.361+0000"
},
"previousState": {
"value": "OK",
"reason": "Threshold Crossed: 1 out of the last 1 datapoints [0.0 (11/02/24 23:18:00)] was not greater than the threshold (1.0) (minimum 1 datapoint for ALARM -> OK transition).",
"reasonData": "{\"version\":\"1.0\",\"queryDate\":\"2024-02-11T23:19:42.353+0000\",\"startDate\":\"2024-02-11T23:18:00.000+0000\",\"statistic\":\"Average\",\"period\":60,\"recentDatapoints\":[0.0],\"threshold\":1.0,\"evaluatedDatapoints\":[{\"timestamp\":\"2024-02-11T23:18:00.000+0000\",\"sampleCount\":1.0,\"value\":0.0}]}"
},
"configuration": {
"metrics": [{
"id": "c6c6cd35-94f3-6de0-d99c-e835592d0d67",
"metricStat": {
"metric": {
"namespace": "ECS/ContainerInsights",
"name": "TaskCount",
"dimensions": {"ClusterName": "simpleUploader"}
},
"period": 60,
"stat": "Average"
},
"returnData": True
}]
}
}
}
とまあ整形するとこんな具合の構造になっているので、あとは
View this alarm in the AWS Management Console:
https://ap-northeast-1.console.aws.amazon.com/cloudwatch/deeplink.js?region=ap-northeast-1#alarmsV2:alarm/NumberOfTasksGreaterThan1
Alarm Details:
- Name: NumberOfTasksGreaterThan1
- Description:
- State Change: INSUFFICIENT_DATA -> ALARM
- Reason for State Change: Threshold Crossed: 1 out of the last 1 datapoints [2.0 (11/02/24 22:44:00)] was greater than the threshold (1.0) (minimum 1 datapoint for OK -> ALARM transition).
- Timestamp: Sunday 11 February, 2024 22:45:07 UTC
- AWS Account: 132110897268
- Alarm Arn: arn:aws:cloudwatch:ap-northeast-1:132110897268:alarm:NumberOfTasksGreaterThan1
Threshold:
- The alarm is in the ALARM state when the metric is GreaterThanThreshold 1.0 for at least 1 of the last 1 period(s) of 60 seconds.
Monitored Metric:
- MetricNamespace: ECS/ContainerInsights
- MetricName: TaskCount
- Dimensions: [ClusterName = simpleUploader]
- Period: 60 seconds
- Statistic: Average
- Unit: not specified
- TreatMissingData: missing
State Change Actions:
- OK:
- ALARM: [arn:aws:sns:ap-northeast-1:132110897268:sendTaskNotificationMaiil]
- INSUFFICIENT_DATA:
こんなノリに作りかえたらいい
つっても面倒なので触りだけだけど
import json
import urllib.request
def lambda_handler(event, context):
webhook_url = "https://hooks.slack.com/services/*****"
alarm_name = event['alarmData']['alarmName']
reason = event['alarmData']['state']['reason']
timestamp = event['alarmData']['state']['timestamp']
message = f"*アラーム名*: `{alarm_name}`\n*理由*: {reason}\n*時刻*: {timestamp}"
slack_message = {'text': message}
#slack_message = {
# 'text': json.dumps(event)
#}
req = urllib.request.Request(webhook_url, data=json.dumps(slack_message).encode('utf-8'), headers={'Content-Type': 'application/json'})
response = urllib.request.urlopen(req)
return {
'statusCode': 200,
'body': json.dumps('Message posted to Slack')
}
さっきのイベントのjsonをテストにぶっこんでおく
まあ、あとの可能性は(スクリプトさえ書けば)無限大なのでいろいろ考えてみてぽ
import json
import urllib.request
from datetime import datetime
from dateutil import tz
def lambda_handler(event, context):
webhook_url = "https://hooks.slack.com/services/***"
alarm_name = event['alarmData']['alarmName']
reason = event['alarmData']['state']['reason']
timestamp_utc = datetime.strptime(event['alarmData']['state']['timestamp'], '%Y-%m-%dT%H:%M:%S.%f%z')
tz_jst = tz.gettz('Asia/Tokyo')
timestamp_jst = timestamp_utc.astimezone(tz_jst).strftime('%Y-%m-%d %H:%M:%S %Z')
# メトリック情報
metric_namespace = event['alarmData']['configuration']['metrics'][0]['metricStat']['metric']['namespace']
metric_name = event['alarmData']['configuration']['metrics'][0]['metricStat']['metric']['name']
dimensions = event['alarmData']['configuration']['metrics'][0]['metricStat']['metric']['dimensions']
period = event['alarmData']['configuration']['metrics'][0]['metricStat']['period']
statistic = event['alarmData']['configuration']['metrics'][0]['metricStat']['stat']
treat_missing_data = event.get('alarmData', {}).get('treatMissingData', 'missing') # デフォルト値: missing
# メトリック情報をメッセージに組み込む
metric_info = f"""
*Monitored Metric:*
- MetricNamespace: {metric_namespace}
- MetricName: {metric_name}
- Dimensions: {dimensions}
- Period: {period} seconds
- Statistic: {statistic}
- Unit: not specified
- TreatMissingData: {treat_missing_data}
"""
message = f"""
*アラーム名*: `{alarm_name}`
```{reason}```
*時刻*: {timestamp_jst}
{metric_info}
"""
slack_message = {'text': message.strip()} # .strip() は先頭と末尾の改行を削除する
req = urllib.request.Request(webhook_url, data=json.dumps(slack_message).encode('utf-8'), headers={'Content-Type': 'application/json'})
response = urllib.request.urlopen(req)
return {
'statusCode': 200,
'body': json.dumps('Message posted to Slack')
}
この記事が気に入ったらサポートをしてみませんか?