Amazon EC2インスタンスの起動停止をAmazon EventBridge + AWS Lambdaでコントロールする


きっかけ

仕事の中で複数持っている自分のタスクを効率よくこなしていくのとともに、散らばっている情報を一つに集約して常に整理していくのはとても重要なことだと思っています。前者に関して、自分のプロジェクト内のタスクコントロールはヌーラボさんのBacklogで完結していて、ものすごく重宝しているのですが、Backlogで管理している課題よりももっと細かい粒度のタスクを個人レベルで管理するにあたって、ガントチャートレベルで期限日を重要度を意識しながらコントロールしていくのがどうも苦手なので、とりあえずこの目的を達するために管理の手段としてredmine使おうと思い、どうせだったら個人で保有しているAWSアカウントの中に、redmineのAMIを使用したEC2インスタンスを立ててしまえばいいじゃん、となったわけです。

ところが自分のAWSアカウント、使い始めてかれこれ数年になるので、残念ながら無料枠は使えません。なので、コスト削減のために、自分が普段のルーチンとしてタスクの棚卸をする時間帯だけ起動させるようにしたい、と思ったのがそもそものきっかけです。

せっかくならAWS Lambdaを使いたい

ちょうど同時進行で、AWS Lambdaの勉強を掘り下げていたこともあったので、せっかくならば自分でコードを書いてLambda関数を作り、その機能でコントロールさせてあげればいいなと。AWS Lambdaの特徴としては、

  1. インフラストラクチャの運用管理が不要
  2. イベントドリブンであること
  3. コスト効率が高い

の3つが挙げられるのですが、何気に2番目って、自分の観測範囲ではなかなか使われていなかったので、この機会に使ってみようと思ったわけです。使いたい時間だけ使えるようにすればいいので、Amazon EventBridgeを使ってLambda関数と連携してあげればOK。

というわけでさっくりとLambda関数上でワークフローを組み上げました。そんなに難しいことはしていません。

とりあえず器として”EC2-Auto-Start-Stop”というLambda関数を作成し、トリガーとしてEC2インスタンスを起動するためのEventBridgeトリガーと、EC2インスタンスを停止するEventBridgeトリガーを追加。一応念のため、ちゃんとEC2インスタンスが起動停止したよということをSMSとして送信するためのAmazon SNSトピックも送信先として追加。

これだけなんですけれどもね。

Amazon EventBridge側の設定

まず最初に、それぞれのEventBridgeのトリガーとして、今回はスケジュールで実行するので、起動・停止したい時刻をcron式で定義してあげます。

次に、イベントを起動してあげたいLambda関数を指定します。この時、EC2インスタンスのタグを読み取ってイベントを起動する、というやり方もあるのですが、自分の場合は、Lambda関数のevent引数の中に、起動対象のEC2インスタンスが所属するリージョン情報と、起動したいのか、停止したいのかを示すフラグをjsonで渡してあげるようにしました。

Lambda関数にコードを書く

ここで、いよいよLambda関数のコードを書いていきます。今回はサクッと書きたいためにラインタイムは”Python3.9″を選択。せっかくなのでアーキテクチャは採用されたばかりの”arm64″を選択します。

で、デフォルトで作成される”lambda_function.py”に書いたコードは以下の通り。

import boto3
import logging
import traceback

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):

    try:
        region = event['Region']
        action = event['Action']
        client = boto3.client('ec2', region)
        responce = client.describe_instances(Filters=[{'Name': 'tag:AutoStartStop', 'Values': ['1']}])
        target_instance_ids = []

        for reservation in responce['Reservations']:
            for instance in reservation['Instances']:
                target_instance_ids.append(instance['InstanceId'])
        
        print(target_instance_ids)

        if not target_instance_ids:
            print('There are not instances subject to automatic start or stop.')
        else:
            if action == 'start':
                client.start_instances(InstanceIds=target_instance_ids)
                print('Started Instancess.')
            elif action == 'stop':
                client.stop_instances(InstanceIds=target_instance_ids)
            else:
                print('Invalid action.')

        return {
            "statusCode": 200,
            "message": 'Finished automatic start or stop EC2 instances process. [Region: {}, Action: {}]'.format(event['Region'], event['Action'])
        }

    except Exception as e:
        logger.error(e)
        print(traceback.format_exc())
        return {
            "statusCode": 500,
            "message": 'An error occured at automatic start or stop EC2 instances process.'
        }

ざっくり解説すると、EventBridgeの入力の設定で設定したリージョン情報と起動・停止のフラグを読み込んで、EC2インスタンスのタグで自動起動停止設定が”1″(true)になっているインスタンスIDを探し出し、順繰りに起動または停止してあげるだけというものすごく簡単なコードです。簡単すぎて申し訳ない。。。

まとめ

こんな感じで、意外にも簡単に、EC2インスタンスを決まった時間に自動的に起動停止する仕組みが出来上がりました。

実際にこうして簡単なコードを書いてみることで、Amazon EventBridgeからLambda関数がイベントドリブンでどのように連携されていくのかということをハンズオンレベルで理解ができるというのはいいですね。

またこういった問題って、AWS認定SysOps アドミニストレーター アソシエイト試験(AWS SOA)でも出てきたりするので侮れないです。

これからもこんな感じで楽しみながらコーディングしたり設計したりしつつ、AWSの持つ各サービスの仕組みを理解していきたいと思います。

カテゴリー: AWS タグ: , , パーマリンク