3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

低稼働な AWS EC2 を自動で停止する

Posted at

モチベーション

高額な EC2 (r4.16xlarge とか)を立ち上げた後、うっかり止め忘れるととんでもない料金を請求されてしまう。
ただ、利用中の ec2 を停止されるのも困る。
CPU 使用率を見て、使ってなさそうなら止めるようにしたい。

やったこと

Lambda, CloudWatch を使って実装。

停止対象の EC2

  • 特定の tag がつけられていること
  • 過去3時間のメトリクスが取得できること
  • 過去3時間の CPU 使用率が閾値以下であること

構成

構成.jpg

処理フロー

  1. CloudWatch から1時間に1度 Lambda を実行する
  2. CloudWatch から EC2 のメトリクスを取得する
  3. 条件を満たす EC2 を停止する

言語

Python3.6

Lambda コードのバージョン管理

lambda のバージョン管理を利用する。
classmethod さんの資料を参考

Lambda の実装

  1. Key="AutoStop", Value="true" のタグが付与された EC2 の Instance Id を取得する
  2. CloudWatch から Instance Id 引きで CPU 使用率の datapoint を取得する
  3. 使用中の EC2 か判定する
  4. 条件を満たす EC2 の Instance Id を取得・停止する
import boto3
import datetime

CPU_UTILIZATION_THRESHOLD = 1.0
METRIC_PERIOD_SECONDS = 300
METRIC_TIME_DELTA_HOURS = 3

def lambda_handler(event, context):
    target_id_list = get_target_instance_id_list()
    if target_list:
        client = boto3.client('ec2', "ap-northeast-1")
        client.stop_instances(InstanceIds=target_list)


def get_target_instance_id_list():
    client = boto3.client('ec2', "ap-northeast-1")
    resp = client.describe_instances()
    target_list = []
    for reservation in resp['Reservations']:
        print(len(reservation['Instances']))
        for instance in reservation['Instances']:
            instance_id = get_instance_id_of_target_tag(instance)
            if instance_id and not is_using_ec2(instance_id):
                target_list.append(instance_id)
        return target_list

def get_instance_id_of_target_tag(instance):
    if 'Tags' not in instance:
        return None

    if instance['State']['Name'] != 'running':
        return None

    for tag in instance['Tags']:
        if tag['Key'] == 'AutoStop' and tag['Value'] == 'true':
            return instance['InstanceId']

def get_datapoints(instance_id):
    client = boto3.client('cloudwatch', region_name='ap-northeast-1')

    # Get EC2 CPUUtilization
    response = client.get_metric_statistics(
        Namespace='AWS/EC2',
        MetricName='CPUUtilization',
        Dimensions=[
            {
                'Name': 'InstanceId',
                'Value': instance_id
            }
        ],
        StartTime=datetime.datetime.now() - datetime.timedelta(hours=METRIC_TIME_DELTA_HOURS),
        EndTime=datetime.datetime.now(),
        Period=METRIC_PERIOD_SECONDS,
        Statistics=['Average']
    )
    return response['Datapoints']

def is_using_ec2(instance_id):
    datapoints = get_datapoints(instance_id)

    if len(datapoints) < METRIC_TIME_DELTA_HOURS * 60 * 60 / METRIC_PERIOD_SECONDS:
        return False
    is_using = False
    for datapoint in datapoints:
        if datapoint['Average'] > CPU_UTILIZATION_THRESHOLD:
            is_in_use = True
    return is_using

さいごに

今回は Lambda で実装したが、数が少なければ CloudWatch を使った自動停止 の方がスマートな解決方法かも。
営業日以外は止めたい場合は Instance Scheduler で対応できそう。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?