🐟

boto3使ったCloudWatch DashBoardのAutoScalingの設定をしているEC2のウィジェットを更新する

2024/06/30に公開

概要

CloudWatchDashboardのウィジェット(グラフ)のメトリクスデータをboto3を使って更新するためのサンプルコードの紹介です。
「思ったより簡単できるよ」という事を言いたいのと「月1で何かブログ投稿を」という目標達成したいという自己満のための投稿です。

何が言いたいか、伝えたいか

  • Dashboardの更新は思ったより簡単なコードでできますよ
  • EC2に限らず他のメトリクスデータ更新の処理にも参考になると思います

(*)注意
後のコードを見ていただければ分かりますが、全然そのまま使えるものではありません。エラーハンドリングなしの最小限なもので、更新したい内容によっても色々と修正が必要になるものです。
あとコード書くのも得意では無い人間による物なので、あくまで参考レベルで流し見してもらえればと。

サンプルコード

もう先にコード出しておきましょう。

import boto3
import json
import sys

cloudwatch_client = boto3.client('cloudwatch', region_name='ap-northeast-1')
ec2_client = boto3.client('ec2', region_name='ap-northeast-1')

Dashboard='hoge-dashboard'
ec2_name_suffix = sys.argv[1]
instance_name = 'ec2-' + ec2_name_suffix
asg_name = 'hoge-autoscaling-' + ec2_name_suffix


#ダッシュボードの取得
def get_dashboard():
    response = cloudwatch_client.get_dashboard(
        DashboardName=Dashboard
    )

    #ダッシュボードbodyを辞書型に変換
    dashboard_body_dict=json.loads(response['DashboardBody'])

    return dashboard_body_dict

#インスタンスID一覧の取得
def get_instanceids(instance_name):
    response = ec2_client.describe_instances(
        Filters=[
            {'Name': 'instance-state-name', 'Values': ['running']},
            {'Name': 'tag:Name', 'Values': [instance_name]}
        ]
    )

    instance_ids = []
    for reservation in response['Reservations']:
        for instance in reservation['Instances']:
            instance_ids.append(instance['InstanceId'])

    return instance_ids


def main():
    dashboard_body = get_dashboard()
    # ウィジェット一覧の取得
    widget_list = dashboard_body['widgets']

    # ウィジェットリストをループ処理
    for wl in widget_list:

        # ウィジェットタイプが"metric", titleに 定義した名前が入ってたら処理
        widget_name = instance_name + '-memory'
        if wl['type']=="metric" and widget_name in wl['properties']['title']:

            # 既存のウィジェットのメトリクスを削除
            wl['properties']['metrics'].clear()

            # 起動中のインスタンスID一覧の取得
            instance_id_list = get_instanceids(instance_name)

            for ec2_id in instance_id_list:

                #追加要素
                addlist=[
                          [
                            "CloudWatchCustomMetrics/EC2",
                            "mem_used_percent",
                            "InstanceId",
                            ec2_id,
                            "AutoScalingGroupName",
                            asg_name
                          ]
                        ]

            # 定義した新たなメトリクスデータの追加
                wl['properties']['metrics'].extend(addlist)

    #再度json化
    after_dashboard_body=json.dumps(dashboard_body)

    #ダッシュボードの更新
    cloudwatch_client.put_dashboard(
        DashboardName=Dashboard,
        DashboardBody=after_dashboard_body
    )

if __name__ == "__main__":
    main()

このページはどんな人向けか

皆さん、CloudWatchDashboardは使ってますか?
これは使ってる人は多少なりともいるんじゃないでしょうか。

そしてそのDashboardでAutoScaling設定をしているEC2の何らかのメトリクスを見ている人いますか?
また、AutoScaling設定をしてるEC2は週1など定期的に入れ替えていて、
そのEC2のグラフをAutoScalingで入れ替わるたびに手動で入れ替えてて、あー面倒だなー、なんてなってる人はいませんか?(ここまで来るとそんな人はいない気がしますね。)
しかし、これはそんな極々限られた人向けのboto3を使ったコードの紹介なのです。

あとこれはEC2に限らず他のメトリクスデータ更新の処理にも参考になると思います。

想定される使用方法

  • これをlambdaにすれば、EventBridgeなどで定期的にインスタンス入れ替わり後のタイミングで実行することで、ダッシュボードも自動更新されますね。

  • もうちょっとコードを足して、AutoScalingのアクションとしてSNS連携からのLambda実行とかすればAutoScaling発生時にダッシュボードも更新、となりそうですね。

  • ちなみに筆者の運用環境では色々な処理をcronで実行するインスタンスがいるので、そいつにこのコードをEC2のAutoScaling後に実行させてダッシュボードを定期的に更新してます。

とにかく何が言いたいか、Dashboardの更新は思ったより簡単なコードでできますよ、という事です。
(色々エラーハンドリングや複雑な条件などなければ)

サンプルコード説明をダラダラと

ではコード内のコメントをベースにざっと説明してこうと思います。

関数定義の部分の説明は省略しちゃいます。

"#" ウィジェット一覧の取得
現状のダッシュボードのデータから、ウィジェット(グラフ)の一覧リストを作ります。

"#" ウィジェットリストをループ処理
ウィジェット(グラフ)の一覧リストをforで回してきます。

"#" ウィジェットタイプが"metric", titleに 定義した名前が入ってたら処理
forで回ってきたウィジェットのタイプがメトリクスデータのもの"metric"、
かつウィジェットのタイトル(名前)に定義した変数(widget_name)の名前が含まれてたら、
そのウィジェットを更新対象として次の処理に進みます。

"#" 既存のウィジェットのメトリクスを削除
取得してきたダッシュボードの対象のウィジェットのメトリクスデータを削除します。

"#" 起動中のインスタンスID一覧の取得
起動中のインスタンスID一覧を取得してリスト化します。
ステータスとNameタグでフィルターしてます。

"#"追加要素
追加するメトリクスデータを定義してます。
ここで事前取得した起動中インスタンスのidが指定されてるので、
新たなインスタンスのメトリクスデータになるわけです。
更新するメトリクスによって名前空間やらディメンションが変わってくるので、そこは適宜必要な値を設定します。

"#" 定義した新たなメトリクスデータの追加
ここで追加要素として定義したデータを対象のウィジェットに追加します。

"#"再度json化
一通りウィジェットの更新処理が終わったのちに、最初にJSONから辞書型にしてたダッシュボードの中身を元に戻します。

"#"ダッシュボードの更新
JSONに戻した新たなダッシュボードの中身(JSON)をアップロードしてダッシュボード更新

最後に使用してたboto3のリファレンスのリンクも貼っておきます。
こちらでより詳細はパラメータやエラーハンドリング処理なども見ていただくと、より良いコードができるのかと思います。

参考
boto3 のリファレンス
get_dashboard
put_dashboard
describe_instances

Discussion