Lambda で特定の EC2 インスタンスのみを除外して、存在している EC2 インスタンスを全て停止してみた

アノテーションのスライマンです。
今回は特定の EC2 インスタンスのみを除外して、存在している EC2 インスタンスを全て停止する Lambda 関数を作成していきたいと思います。
EventBridge Scheduler では直接 EC2 の StopInstances API を利用して、EC2 インスタンスを停止することはできますが、インスタンスを沢山保持している際に、特定の EC2 インスタンスのみを除外したい場合は、対象のインスタンスを全てパラメーターに記載しなければなりません。
そのため、今回は Lambda を利用して、除外したい特定の EC2 インスタンスのみを記載することで、上記の内容を実現できる関数を作成していきます。

構成図

構成は下記の通りとなります。

EventBridge Scheduler で任意の時間帯に Lambda が起動されるように cron 式のスケジュールを設定して、Lambda をターゲットとするスケジュールを作成します。
指定した時間帯に Lambda が起動して、特定の EC2 インスタンスを除外して、存在している EC2 インスタンスを全て停止します。

概要

必要なリソースは下記となります。
今回は EC2 (3台)が既に作成されている前提で進めていきたいと思います。
そのため、Lambda 関数および EventBridge Scheduler を作成していきます。

  • EC2
  • Lambda 関数
  • EventBridge Scheduler

必要なリソースを準備した後、実際に動作を確認していきたいと思います。

実際にやってみた

Lambda 関数の作成

まずは、Lambda の実行ロールから作成していきます。
IAM > ポリシー > ポリシーの作成 より下記の IAM ポリシーを記入していきます。
その後、次へ進み、任意の名前を指定して作成します。

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

下記のように、IAM ポリシーが作成されていることを確認します。

次に IAM ロールを作成します。
IAM > ロール > ロールの作成 より信頼されたエンティティタイプを AWS のサービス、サービスを Lambda と選択します。
許可するポリシーにて先程作成した IAM ポリシーを選択し、名前を記入して IAM ロールを作成します。

IAM ロールの作成が完了しましたら、Lambda の関数を作成します。
今回はランタイムとして、Python 3.11 を利用します。
アーキテクチャは x86_64 のまま、実行ロールを先程作成した IAM ロールに選択して、関数を作成します。

タイムアウトが3秒となっているので、こちらを 設定 > 一般設定 > 編集より、1分に変更します。

コードの設定をして、Deploy します。
下記の通りとなります。

import boto3
import os

instance_id_excluded = os.environ['INSTANCE_ID'] 
region = 'ap-northeast-1' 

def lambda_handler(event, context):
    ec2 = boto3.client('ec2', region_name=region)

    # Describe EC2 Instances
    instances = ec2.describe_instances()

    # Check the EC2 Instances ID and State
    for reservation in instances['Reservations']:
        for instance in reservation['Instances']:
            if instance['InstanceId'] != instance_id_excluded and instance['State']['Name'] == 'running':
                # Stop EC2 Instances that not equal to instance_id_excluded and in running state
                ec2.stop_instances(InstanceIds=[instance['InstanceId']])
                print('Stopping instance: ', instance['InstanceId'])

    return 'Complete stopping instance except ' + instance_id_excluded

実施される処理は下記となります。

  • EC2 インスタンスの情報を取得する
  • EC2 インスタンスの情報より、インスタンスID が特定の EC2 インスタンスIDではない、かつ状態が Running となっているインスタンスが存在する場合、そのインスタンスを停止する

最後に 除外した特定の EC2 インスタンスID を環境変数に設定します。

キー:INSTANCE_ID
値:<instanceid>

動作確認

Lambdaのテスト実行より、動作確認をしていきます。
テストイベントを設定より、新しいイベントを作成して、イベント名を test と記入して、そのまま保存します。
その後、test を呼び出して、動作を確認します。
結果が出力され正常に動作できていることを確認できます。

EC2 インスタンスを確認しますと、特定のインスタンス以外、全て停止されていることが分かります。

EventBridge Schedulerの作成

Lambda が正常に動作していることを確認できましたので、EventBridge Scheduler を作成していきます。
EventBridge > スケジューラ > スケジュール > スケジュールを作成より、作成します。

スケジュール名、パターンを設定します。
Cronベースのスケジュールで毎日特定の時間に稼働するように設定しております。
(今回は毎日 12:10 JST に稼働するようになっております。)
フレックスタイムウィンドウはオフにします。
時間枠は何も設定せずにそのまま次へ進みます。

10 12 * * ? *

ターゲットとして、AWS Lambda Invoke を選択します。
作成した Lambda 関数を選択します。
それ以外はデフォルトのまま、最後まで進み、スケジュールを作成します。

作成後、指定した時間にスケジューラーが稼働していることを確認して、Lambda が起動すれば問題ございません。

最後に

設定は以上となります。
今回は停止のパターンのみ作成しましたが、こちらを参考にインスタンスの開始も、特定の EC2 インスタンスのみを除外して開始することもできます。
Lambda 関数で除外する方法を実現しましたが、この除外方法が、EventBridge Scheduler のパラメーターの指定で簡単にできるとかなり楽になりますね。

参考情報

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

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