0
0

タグベースによるEC2インスタンスの夜間停止によるコスト削減

Last updated at Posted at 2024-08-05

背景

このAWS Lambda関数は、タグ値で制御してEC2インスタンスの起動や停止を行うことができます。EventBridgeを使用した夜間の定期停止や、AWS Change Calendarを利用した不定期な長期停止など、様々なシナリオで活用できる汎用的なEC2インスタンス管理ツールです。Spotインスタンスの停止ができるようになったので、さらなるコスト削減のために作りました。

実行環境

エンジン

Python3.12

環境

Lambda

関数の概要

この関数は以下の構造のJSONペイロードを受け取ります:

{
  "region": string,  # 対象リージョン
  "tagName": string, # 対象EC2インスタンスのタグ名
  "action": string,  # start(起動)またはstop(停止)
  "test": bool       # テストモード(true/false
}

機能

  1. 指定されたtagNameの値がactionまたは"true"であるEC2インスタンスIDを取得します。
  2. actionが"start"の場合、インスタンスIDとNameタグを表示し、インスタンスを起動します(testがtrueの場合を除く)。
  3. actionが"stop"の場合、インスタンスIDとNameタグを表示し、インスタンスを停止します(testがtrueの場合を除く)。

セットアップ

  1. AWSアカウントで新しいLambda関数を作成します。
  2. ランタイムとしてPython 3.12を使用します。
  3. 提供されたPythonコードをLambda関数にコピーします。
  4. 適切なIAMロールを設定し、必要最小限の権限を付与します(IAMポリシーセクションを参照)。

IAMポリシー

Lambda関数の実行ロールに以下のIAMポリシーを付与してください:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeInstances",
                "ec2:StartInstances",
                "ec2:StopInstances"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        }
    ]
}

このポリシーは、EC2インスタンスの詳細取得、起動/停止、およびログの書き込みに必要な権限を付与します。

使用方法

Lambda関数をJSONペイロードで呼び出します。例:

{
  "region": "ap-northeast-1",
  "tagName": "Environment",
  "action": "start",
  "test": false
}

これにより、ap-northeast-1リージョンで"Environment"タグの値が"start"または"true"であるすべてのEC2インスタンスが起動されます。

テスト

ペイロードのtestパラメータをtrueに設定すると、実際にインスタンスを起動/停止せずに関数を実行できます。これにより、どのインスタンスが影響を受けるかを確認できます。

ログ

関数は、インスタンスIDとそのNameタグ、および実行されたアクションをCloudWatch Logsに記録します。詳細な実行情報については、Lambda関数のCloudWatchロググループを確認してください。

関数の中身

import boto3
import json

def lambda_handler(event, context):
    region = event['region']
    tag_name = event['tagName']
    action = event['action']
    test = event['test']

    ec2 = boto3.client('ec2', region_name=region)

    # Get EC2 instances with the specified tag
    response = ec2.describe_instances(
        Filters=[
            {'Name': f'tag:{tag_name}', 'Values': [action, 'true']}
        ]
    )

    instances = []
    for reservation in response['Reservations']:
        for instance in reservation['Instances']:
            instance_id = instance['InstanceId']
            name = next((tag['Value'] for tag in instance.get('Tags', []) if tag['Key'] == 'Name'), 'N/A')
            instances.append((instance_id, name))

    if not instances:
        return {
            'statusCode': 200,
            'body': json.dumps(f'No instances found with tag {tag_name}={action} or {tag_name}=true')
        }

    for instance_id, name in instances:
        print(f"Instance ID: {instance_id} ({name})")

    instance_ids = [instance[0] for instance in instances]

    if not test:
        if action == 'start':
            ec2.start_instances(InstanceIds=instance_ids)
            message = f"Started instances: {', '.join([f'{id} ({name})' for id, name in instances])}"
        elif action == 'stop':
            ec2.stop_instances(InstanceIds=instance_ids)
            message = f"Stopped instances: {', '.join([f'{id} ({name})' for id, name in instances])}"
        else:
            message = f"Invalid action: {action}. Use 'start' or 'stop'."
    else:
        message = f"Test mode: Would have {action}ed instances: {', '.join([f'{id} ({name})' for id, name in instances])}"

    return {
        'statusCode': 200,
        'body': json.dumps(message)
    }
0
0
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
0
0