見出し画像

いろいろな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個ある。個数に応じて課金されるみたいだから必要ないサービスとかは消した方がいいんだろうけどねえ

TaskCountを発見

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のコンソールに戻るよ

https://ap-northeast-1.console.aws.amazon.com/lambda/home?region=ap-northeast-1#/functions/slacknotice?tab=configure

とか。

下の方に「リソースベースのポリシーステートメント」ってのがあるいから権限を追加するわさ


InvokeFunctionを見つけるのだ

まず徐に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')
    }


この記事が気に入ったらサポートをしてみませんか?