SSM State Manager の各ノードに紐づいている関連付け数を監視する仕組みを、EventBridge + Lambda 関数 を使って実装してみた
はじめに
テクニカルサポートの 片方 です。
コツコツと積み重ね続け、ついにブログ執筆数が 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 アラームの作成をご検討ください。本ブログが誰かの参考となれば幸いです。
参考資料
- State Manager 関連付けのターゲットとレート制御について - AWS Systems Manager
- State Manager の関連付け実行時、ステータスが “Failed” 、詳細ステータスが “LimitExceeded” になっている際の対処法 | DevelopersIO
- DescribeInstanceAssociationsStatus - AWS Systems Manager
- describe_instance_associations_status - Boto3 1.34.84 documentation
アノテーション株式会社について
アノテーション株式会社は、クラスメソッド社のグループ企業として「オペレーション・エクセレンス」を担える企業を目指してチャレンジを続けています。「らしく働く、らしく生きる」のスローガンを掲げ、様々な背景をもつ多様なメンバーが自由度の高い働き方を通してお客様へサービスを提供し続けてきました。現在当社では一緒に会社を盛り上げていただけるメンバーを募集中です。少しでもご興味あれば、アノテーション株式会社WEBサイトをご覧ください。