SSM State Manager の各ノードに紐づいている関連付け数を監視する仕組みを、EventBridge + Lambda 関数 を使って実装してみた

SSM State Manager の各ノードに紐づいている関連付け数を監視する仕組みを、EventBridge + Lambda 関数 を使って実装してみた

Clock Icon2024.05.01 02:20

はじめに

テクニカルサポートの 片方 です。
コツコツと積み重ね続け、ついにブログ執筆数が 200 本に迫りました。そのため 199 本目の本ブログと、記念すべき 200 本目、201 本目のブログは Lambda 関数を利用した内容を紹介します。
こちらは、私の所属するテクニカルサポートチームへ「〇〇〇 を実現するサービスや機能はないか」と実際にお客様より頂いたお問い合わせを参考 (ヒント) にしてカスタムソリューションを作成してみました。

本ブログでは、SSM State Manager の各ノードに紐づいている関連付け数を、Lambda 関数を使って CloudWatch カスタムメトリクスとして表示させてみました。

背景

SSM の State Manager で各マネージドノードに対しての関連付け数は 20 が上限となっています。この上限に抵触している場合は、ステータスが "Failed" 、詳細ステータスが "LimitExceeded" になり関連付けが失敗します。

各マネージドノードは、最大 20 件の関連付けの対象にすることができます。

こちらの数を確認したい場合はマネジメントコンソール画面から確認する方法が存在せず、現時点では AWS CLI コマンドを実行して確認する手段になります。
そのため、SSM State Manager の各ノードに紐づいている関連付け数を、Lambda 関数 を使って CloudWatch カスタムメトリクスとして表示させてみました。
カスタムメトリクスとして可視化できれば、EventBridge で Lambda 関数 を定期的に呼び出し CloudWatch アラームなどを利用して、上限に抵触しそうなことを早期に検知可能です。

構成

EventBridge で Lambda 関数 を定期的に呼び出します。Lambda 関数内の "DescribeInstanceAssociationsStatus" より各ノードに紐づいている関連付けの一覧を取得して、こちらの結果から関連付けられている総数を取得する流れです。

以下はイメージです。

やってみた

ロール

Lambda 関数にアタッチするロールを作成します。信頼関係は以下です。

{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "lambda.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

ロール名: LambdaFunctionCheckStateManagerStatusRole

アタッチするポリシー例

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "ec2:DescribeInstances",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "ssm:DescribeInstanceAssociationsStatus",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "cloudwatch:PutMetricData",
            "Resource": "*"
        }
    ]
}

Lambda 関数

ランタイムは Python 3.12 を使用しました。
アタッチする実行ロールは、先ほど作成した "LambdaFunctionCheckStateManagerStatusRole" を選択します。

関数名: LambdaFunctionCheckStateManagerStatus

冒頭でお伝えした通り、この Lambda 関数の目的は、SSM State Manager の マネージドノード (EC2 インスタンス) の関連付け数を取得し、その情報を CloudWatch カスタムメトリクスとして送信することです。
これにより、各マネージドノード毎にどれだけ関連付けられているかを監視できます。

サンプルコード
import boto3

def get_association_count(instance_id):
    ssm_client = boto3.client('ssm')
    response = ssm_client.describe_instance_associations_status(InstanceId=instance_id)

    association_count = 0
    if 'InstanceAssociationStatusInfos' in response:
        association_count = len(response['InstanceAssociationStatusInfos'])

    return association_count

def send_cw_metric(metric_dimension_value, metric_value):
    cw_client = boto3.client('cloudwatch')

    try:
        cw_client.put_metric_data(
            Namespace='SSM State Manager',
            MetricData=[
                {
                    'MetricName': 'State Count',
                    'Dimensions': [
                        {
                            'Name': 'Instance',
                            'Value': metric_dimension_value
                        }
                    ],
                    'Value': metric_value,
                    'Unit': 'Count'
                }
            ]
        )
    except Exception as e:
        print(e)

def lambda_handler(event, context):
    ec2_client = boto3.client('ec2')
    instances = ec2_client.describe_instances()

    for reservation in instances['Reservations']:
        for instance in reservation['Instances']:
            instance_id = instance['InstanceId']
            association_count = get_association_count(instance_id)
            send_cw_metric(instance_id, association_count)

EventBridge

作成した Lambda 関数と同じリージョンで実装してください。

ルール名: LambdaFunctionCheckStateManagerStatusRule

イベントバスは default で、ルールタイプはスケジュールを選択します。

通常のレートで実行されるスケジュール (10 分ごとなど)。を選択します。
rate 式は、今回 6 時間 に設定しました。こちらはお好みで設定してください。

ターゲットのセクションで、ターゲットタイプを AWS のサービスを選択し、ターゲットを選択では Lambda 関数 を選択します。
先ほど作成した関数名 "LambdaFunctionCheckStateManagerStatus" を選択してルール作成を行えば定期実行の設定は終了です。

以上で実装は終了です。お疲れさまでした。

検証してみた

検証用に EC2 インスタンスを 3 台起動させてマネージドノードにします。マネージドノードにする方法は割愛させて頂きます。

State Manager より、AWS-RunPatchBaseline を検証用マネージドノードに全て関連付けしました。

AWS-UpdateSSMAgent を SSM State Manager Server-2 と SSM State Manager Server-3 にのみ関連付けしました。

AWS-UpdateEC2Config をSSM State Manager Server-3 にのみ関連付けしました。

現在では、以下の関連付け数が想定されます。

  • i-038f4f0ae83c89ba0 Test-StateManager-1 合計 1
  • i-02ca225730294b253 Test-StateManager-2 合計 2
  • i-01388f492127f08ae Test-StateManager-3 合計 3

念のため、AWS CLI コマンドで関連付け数を確認します。結果、想定通りでした。
"aws ssm describe-instance-associations-status --instance-id i-xxxxxxxxxxxx | jq '.InstanceAssociationStatusInfos | length'"

作成した、Lambda 関数をテストします。
遅延して上手く処理がされないといった状況であれば、Lambda 関数の一般設定より "タイムアウト値" を伸ばす、"メモリ" を増やすなどのご対応をしてください。

成功したので CloudWatch カスタムメトリクスとして表示されているか確認します。
問題なく表示されていることを確認しました。なお、統計は最小でご確認ください。

次は、関連付けを削除してみます。
AWS-RunPatchBaseline の関連付けのみ残しているので、どのマネージドノードも関連付け数は 1 が想定されます。

念のため、AWS CLI コマンドで関連付け数を確認します。

想定通り、どのマネージドノードも関連付け数は 1 でした。改めて CloudWatch のカスタムメトリクスを確認します。 結果、全て 1 であることを確認しました。成功です!

関連付けを全て削除しました。

再度 CloudWatch のカスタムメトリクスを確認します。こちらも成功です!

まとめ

通知する場合は CloudWatch アラームの作成をご検討ください。本ブログが誰かの参考となれば幸いです。

参考資料

アノテーション株式会社について

アノテーション株式会社は、クラスメソッド社のグループ企業として「オペレーション・エクセレンス」を担える企業を目指してチャレンジを続けています。「らしく働く、らしく生きる」のスローガンを掲げ、様々な背景をもつ多様なメンバーが自由度の高い働き方を通してお客様へサービスを提供し続けてきました。現在当社では一緒に会社を盛り上げていただけるメンバーを募集中です。少しでもご興味あれば、アノテーション株式会社WEBサイトをご覧ください。

この記事をシェアする

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.