1. システムとオフィスの融合
  2. media
  3. AWS
  4. AWSのRI、SPで購入した方がよいリソースを自力で抽出してみた

QESブログ

AWSのRI、SPで購入した方がよいリソースを自力で抽出してみた

AWS
  • LINEで送る
  • このエントリーをはてなブックマークに追加

こんにちは、クラウドプラットフォーム本部の小原です。

AWSでコスト削減をしたいとき、どなたでもRI/SPの購入を真っ先に考えると思います。
では何のRIを買えばよいのか、もしくはどれぐらいのSPを買えばよいのか、みなさんはどのように確認しますか?
これはAWSマネジメントコンソール内に用意されていて、[請求とコスト管理]内の[予約]と[Savings Plans]の[推奨事項]で確認できます。まだ見たことがない人は是非確認してみてください!

さて、実はAWSアカウントの組織体系によっては[請求とコスト管理]を閲覧する権限がない場合があります。
今回はそういった場合にRI/SPで購入した方がよいリソースを自力で抽出する方法を試してみます。

RI、SPで購入・適用されるAWSサービス

現状、RIで購入できるAWSサービスには以下があります。
・Amazon EC2
・Amazon RDS
・Amazon ElastiCache
・OpenSearch Service
・Amazon Redshift
・Amazon DynamoDB

また、SPが適用されるAWSサービスには以下があります。
・Amazon EC2
・AWS Fargate
・AWS Lambda
・Amazon SageMaker

今回は特に購入機会が多いであろうEC2とFargateについて抽出してみます。

RI/SPを購入した方がよいEC2インスタンスの抽出

RI/SPで購入した方がよいEC2インスタンスは長時間稼働されているもの、そして長期的なコミットメントが予測されるものになります。
長期的なコミットメントが予測されるかどうかについてはアプリの性質や性能十分なインスタンスタイプであるか各々の判断に委ねられるところになるため、ここでは長時間稼働しているEC2インスタンスを抽出します。

インスタンスの稼働時間割合を判定するLambda関数(Python)コードを記載します。
各インスタンスごとに以下の内容で情報取得、計算することでインスタンスが起動している時間の割合が分かります。
・直近24時間のCPU使用率のメトリクスを1時間ごとに取得
・CPU使用率が1%以上の時間帯の数を数え、24時間で割る

import boto3
from datetime import datetime, timedelta

def lambda_handler(event, context):
    # CloudWatch クライアントを作成
    cloudwatch = boto3.client('cloudwatch')
    
    # 現在のタイムスタンプを取得し、24時間前のタイムスタンプを計算
    end_time = datetime.now()
    start_time = end_time - timedelta(days=1)
    
    # EC2 インスタンスのリストを取得
    ec2 = boto3.client('ec2')
    instances = ec2.describe_instances()
    
    for reservation in instances['Reservations']:
        for instance in reservation['Instances']:
            instance_id = instance['InstanceId']
            
            # CPU 使用率のメトリクスを取得
            metrics = cloudwatch.get_metric_statistics(
                Namespace='AWS/EC2',
                MetricName='CPUUtilization',
                Dimensions=[
                    {
                        'Name': 'InstanceId',
                        'Value': instance_id
                    },
                ],
                StartTime=start_time,
                EndTime=end_time,
                Period=3600,  # 1時間ごとのデータ
                Statistics=['Average']
            )
            
            # CPU 使用率が1%以上の時間帯をカウント
            active_periods = sum(1 for point in metrics['Datapoints'] if point['Average'] > 1)
            
            # 起動時間の割合を計算
            total_periods = 24  # 24時間
            running_percentage = (active_periods / total_periods) * 100
            
            # 結果を出力
            print(f"Instance ID: {instance_id}, Running Time: {active_periods} hours, Running Percentage: {running_percentage}%")
    
    return {
        'statusCode': 200,
        'body': 'Execution completed'
    }

実行すると以下のような結果が得られます。

Instance ID: i-xxxxxxxxxxxxxxxxx, Running Time: 24 hours, Running Percentage: 100.0%
Instance ID: i-xxxxxxxxxxxxxxxxx, Running Time: 0 hours, Running Percentage: 0.0%

これでインスタンスごとの稼働率を得られるため、RI/SPを購入した方がよいEC2インスタンスの抽出もできました。
稼働率が高く、運用に用いられているインスタンスであればRI/SPの購入をおすすめします。

また、こちらのコードは数字をいじれば直近1週間から稼働率を求められたり1分ごとの情報を取得することでより精度の高い数字を得ることもできます。
ただしCloudWatchのメトリクス取得に結構なコストが発生するため、必要な粒度についてはお気を付けください。

SPを購入した方がよいFargateの抽出

購入した方がよいFargateの条件についてはEC2と同じであるため割愛します。
長期的に利用予定の各クラスター単位で予約されているメモリ、vCPU数を取得する必要があります。
コードが長くなってしまうため今回は特定のクラスターにて直近24時間から1時間ごとに予約されているメモリ、vCPU数を取得します。
SPの購入においては稼働率は関係ないため、1時間ごとにどの程度リソースが使われているのか分かるようにします。

import boto3
from datetime import datetime, timedelta

def lambda_handler(event, context):
    cloudwatch = boto3.client('cloudwatch')
    
    # 現在のタイムスタンプを取得し、24時間前のタイムスタンプを計算
    end_time = datetime.now()
    start_time = end_time - timedelta(days=1)
    
    # クラスター名を指定
    cluster_name = 'ClusterName'
    
    # CPU予約量の合計メトリクスを取得
    total_cpu_reserved = get_cluster_metric_statistics(cloudwatch, cluster_name, 'CpuReserved', start_time, end_time)
    
    # メモリ予約量の合計メトリクスを取得
    total_memory_reserved = get_cluster_metric_statistics(cloudwatch, cluster_name, 'MemoryReserved', start_time, end_time)
    
    # 合計値を出力
    for i in range(len(total_cpu_reserved)):
        print(f"Time: {total_cpu_reserved[i]['Timestamp']}, Total CPU Reserved: {total_cpu_reserved[i]['Value']}, Total Memory Reserved: {total_memory_reserved[i]['Value']}")
    return {
        'statusCode': 200,
        'body': 'Execution completed'
    }

def get_cluster_metric_statistics(cloudwatch, cluster_name, metric_name, start_time, end_time):
    metrics = cloudwatch.get_metric_statistics(
        Namespace='ECS/ContainerInsights',
        MetricName=metric_name,
        Dimensions=[
            {
                'Name': 'ClusterName',
                'Value': cluster_name
            }
        ],
        StartTime=start_time,
        EndTime=end_time,
        Period=3600,  # 1時間ごとのデータ
        Statistics=['Average'] 
    )
    
    # メトリクスデータをタイムスタンプでソート
    sorted_metrics = sorted(metrics['Datapoints'], key=lambda x: x['Timestamp'])
    
    # タイムスタンプと値のペアをリストに追加
    return [{'Timestamp': str(point['Timestamp']), 'Value': point['Average']} for point in sorted_metrics]

実行すると以下のようにリソースが使われていた時間帯ごとに結果を得られます。

Time: 20xx-xx-xx xx:xx:xx+00:00, Total CPU Reserved: 4096.0, Total Memory Reserved: 8192.0

こちらで抽出した結果から1日当たり無駄にならない容量分のSPを計算して購入することになります。
計算方法はAWS FargateのCompute Savings Planにて該当リージョンの料金を確認し、1時間あたりのGB単位の料金と1時間あたりのvCPU単位の料金をそれぞれ掛け合わせることになります。

また、コードを修正することで各クラスター、各サービスから使われているメモリ、vCPU数を取得することも可能です。
時間軸がずれてしまうと購入金額に間違いが発生しかねないため、複数クラスターを運用されている場合は一度に全体で使われているメモリ、vCPU数を取得することをおすすめします。

まとめ

力技になりますがRI、SPで購入した方がよいリソース(EC2、Fargate)を自力で抽出することができました。
RDS等の他リソースについても似たように取得することができると思いますのでお試しください。

[請求とコスト管理]を閲覧する権限がない場合、結果的にコスト削減がうまくいっているのか把握も難しいためコスト面での最適化が難しいと思います。
AWSアカウント運用やコスト最適化などで気になる点がございましたらQESへご連絡ください。

もし「このサービスについて知りたい」「AWS環境の構築、移行」などのリクエストがございましたら、弊社お問合せフォームまでお気軽にご連絡ください! のちほど当ブログにてご紹介させていただくか、複雑な内容に関するお問い合わせの内容の場合には直接営業からご連絡を差し上げます。


また、よろしければ以下のリンクもご覧ください!
<QES関連ソリューション/ブログ>

<QESが参画しているAWSのセキュリティ推進コンソーシアムがホワイトペーパーを公開しました>

※Amazon Web Services、”Powered by Amazon Web Services”ロゴ、およびブログで使用されるその他のAWS商標は、米国その他の諸国における、Amazon.com, Inc.またはその関連会社の商標です。

  • LINEで送る
  • このエントリーをはてなブックマークに追加

お気軽にお問い合わせください。

ページのトップへ