Lambda 関数を利用してスポットプレイスメントスコア結果を SNS で通知してみた

Lambda 関数を利用してスポットプレイスメントスコア結果を SNS で通知してみた

Clock Icon2024.09.02

はじめに

テクニカルサポートの 片方 です。
Lambda 関数を利用して、スポットプレイスメントスコア 結果を SNS より通知する仕組みを作成してみました。

スポットプレイスメントスコアの仕組み
スポットプレイスメントスコア機能を使用する場合は、まずスポットインスタンスのコンピューティング要件を指定します。その後、Amazon EC2 は、スポットリクエストが成功する可能性が高い上位 10 リージョン、を返します。各リージョンまたはアベイラビリティーゾーンは、1~10 のスケールで採点されます。10 はスポットリクエストが成功する可能性が高いことを示し、1 はスポットリクエストが成功する可能性が低いことを示します。

実装してみた

インスタンスタイプとリージョンを指定して、スポットプレイスメントスコアを取得します。
SNS を利用しますが、SNS トピックとサブスクリプション設定については省かせていただきます。
なお、SNS トピックの arn は後ほど必要となるのでメモしてください。

https://docs.aws.amazon.com/ja_jp/sns/latest/dg/sns-configuring.html

ロール

ロールを作成します。Lambada 関数で使用するためのロールです。

※ 信頼関係

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

アタッチするポリシー例

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": "ec2:GetSpotPlacementScores",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "sns:Publish",
            "Resource": "arn:aws:sns:ap-northeast-1:123456789012:aaaaa-xxxxx"
        }
    ]
}

※ 適宜修正してください。
"sns:Publish" の "Resource" では利用する SNS の arn を記述してください。

Lambda 関数

Python 3.12 で作成しました。
実行ロールでは、既存のロールを使用するを選択し、先ほど作成したロールを指定します。

無題

実装する Lambda 関数例

import boto3
import json
import os

def lambda_handler(event, context):
    # EC2クライアントとSNSクライアントを作成
    ec2_client = boto3.client('ec2')
    sns_client = boto3.client('sns')

    # SNSトピックARN、インスタンスタイプ、リージョン名を環境変数から取得
    sns_topic_arn = os.environ['SNS_TOPIC_ARN']
    instance_type = os.environ['INSTANCE_TYPE']
    region_names = os.environ['REGION_NAMES'].split(',')  # カンマで区切られたリージョン名をリストに変換

    # ターゲット容量とターゲット容量の単位を設定
    target_capacity = 5
    target_capacity_unit_type = 'vcpu'

    try:
        # スポットプレイスメントスコアを取得
        response = ec2_client.get_spot_placement_scores(
            InstanceTypes=[instance_type],
            TargetCapacity=target_capacity,
            TargetCapacityUnitType=target_capacity_unit_type,
            MaxResults=10,
            RegionNames=region_names,
            SingleAvailabilityZone=True  # アベイラビリティゾーンごとのスコアを取得
        )

        # 結果を取得
        spot_scores = response.get('SpotPlacementScores', [])
        results = []

        for score in spot_scores:
            results.append({
                'AvailabilityZoneId': score.get('AvailabilityZoneId', 'No AvailabilityZoneId available'),
                'Score': score.get('Score', 'No Score available')
            })

        # SNS通知のサブジェクトを環境変数に基づいて設定
        subject = f'Spot Placement Scores for {instance_type}'

        # 結果をSNSトピックに通知
        message = json.dumps(results, indent=2)
        sns_client.publish(
            TopicArn=sns_topic_arn,
            Subject=subject,
            Message=message
        )

        # 結果をログに出力
        print(message)

        return {
            'statusCode': 200,
            'body': message
        }

    except Exception as e:
        print(f"Error: {str(e)}")
        return {
            'statusCode': 500,
            'body': json.dumps(f"Internal server error: {str(e)}")
        }

環境変数設定

  • SNS_TOPIC_ARN: SNSトピックのARNを設定します。
  • INSTANCE_TYPE: 例えば r7gd.metal のように、対象のインスタンスタイプを設定します。
  • REGION_NAMES: 例としてap-northeast-1、複数のリージョンを指定する場合は ap-northeast-1,us-west-2 のようにカンマ区切りで設定します。

無題1

これで、実装は完了です。お疲れさまでした!

検証してみた

Lambda 関数テストを利用して検証してみました。

無題2

暫くすると通知されました。
※ 一部マスクします

Spot Placement Scores for t1.micro

[
  {
    "AvailabilityZoneId": "apne1-az1",
    "Score": 3
  },
  {
    "AvailabilityZoneId": "usw2-az2",
    "Score": 3
  },
  {
    "AvailabilityZoneId": "usw2-az3",
    "Score": 3
  },
  {
    "AvailabilityZoneId": "usw2-az1",
    "Score": 3
  },
  {
    "AvailabilityZoneId": "apne1-az4",
    "Score": 3
  }
]

--
If you wish to stop receiving notifications from this topic, please click or visit the link below to unsubscribe:
https://sns.ap-northeast-1.amazonaws.com/unsubscribe.html?SubscriptionArn=arn:aws:sns:ap-northeast-1:123456789012:aaaaa-xxxxx:7ede8eb1-aaaa-1111-bbbb-1234de098765&Endpoint=nnnnnnn.aaaaaa@gmail.com

Please do not reply directly to this email. If you have any questions or comments regarding this email, please contact us at https://aws.amazon.com/support

無題3

東京リージョンで申し上げると、t1.micro は 2 つの AZ のみ利用可能なため、少なく表示されています。

無題4

以下は、c5.24xlarge で確認した際の結果です。

無題5

成功です!

まとめ

Amazon EventBridge と合わせて利用することで、定期実行を行うことも可能なのでご検討ください。
本ブログが誰かの参考になれば幸いです。

参考資料

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

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

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.