ForgeVision Engineer Blog

フォージビジョン エンジニア ブログ

AWS LambdaとCloudWatchでEC2インスタンスをスケジュール起動停止する

こんにちは。ソリューション技術部の栗原です。

弊社では社内で開発環境にAWSのEC2を利用しています。 EC2インスタンスは従量課金なのでなるべく必要なときだけ起動しておくようにしたいですが、 毎回コンソールにログインして起動や停止をするのは意外と面倒ですし、停止し忘れると使用していない間も課金されてしまうので AWS LambdaとCloudWatch Eventsを利用してEC2インスタンスの起動、停止を自動化する仕組みを取り入れました。

今回はその設定方法についてご紹介したいと思います。

方針

設定方法

EC2インスタンスのタグ付け

まず、EC2インスタンスにタグを付けます。

起動させるインスタンスにはstartタグを、停止させるインスタンスにはstopタグを付け、値に時刻をJSTで設定します。

3台のEC2インスタンスに、以下のように設定しました。 f:id:kurihara_fv:20180618133539p:plain f:id:kurihara_fv:20180618133545p:plain f:id:kurihara_fv:20180618133551p:plain

Lambda用IAMロールの作成

次に、Lambdaが使用するIAMロールを作成します。

IAMコンソールを開き、[ロールの作成]を選択します。 f:id:kurihara_fv:20180619184421p:plain

以下のように選択し、[次のステップ:アクセス権限]を選択します。 f:id:kurihara_fv:20180619182001p:plain

アタッチするポリシーとして「AmazonEC2ReadOnlyAccess」を選択し、[次のステップ:確認]を選択します。 f:id:kurihara_fv:20180619182008p:plain

ロール名に任意の名前を入力後、[ロールの作成]を選択し、IAMロールを作成します。 f:id:kurihara_fv:20180619182015p:plain

さらに、インラインポリシーを追加します。

先ほど作成したIAMロールの詳細画面を開き、[インラインポリシーの追加]を選択します。 f:id:kurihara_fv:20180620195335p:plain

以下のポリシーを設定し、ポリシーを作成します。

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

インラインポリシーが追加されました。 f:id:kurihara_fv:20180620195347p:plain

これでIAMロールの作成は完了です。

Lambda関数の作成

最後に、Lambda関数を作成します。

Lambdaコンソールを開き、[関数の作成]を選択します。 f:id:kurihara_fv:20180618131701p:plain

以下の値を入力して[関数の作成]を選択し、Lambda関数を作成します。 f:id:kurihara_fv:20180618131708p:plain

これでLambda関数が作成できました。残りの設定をしていきます。

関数コード

Lambda実行時刻(12時に実行された場合は12)が startタグもしくはstopタグの値に設定されているEC2インスタンスを抽出し、起動停止します。

from datetime import datetime
import boto3

def lambda_handler(event, context):
    now_hour = datetime.now().strftime('%H')

    ec2 = boto3.resource('ec2')
    start_instances = ec2.instances.filter(
        Filters=[
            {
                'Name': 'tag:start',
                'Values': [
                    now_hour,
                ],
            },
            {
                'Name': 'instance-state-name',
                'Values': [
                    'stopped',
                ],
            },
        ],
    )
    stop_instances = ec2.instances.filter(
        Filters=[
            {
                'Name': 'tag:stop',
                'Values': [
                    now_hour,
                ],
            },
            {
                'Name': 'instance-state-name',
                'Values': [
                    'running',
                ],
            },
        ],
    )
    for i1 in start_instances:
        i1.start()
        print('start: ' + i1.instance_id)
    for i2 in stop_instances:
        i2.stop()
        print('stop: ' + i2.instance_id)

環境変数

EC2インスタンスのタグに時刻をJSTで設定していますので、 以下のように設定し、LambdaのタイムゾーンUTCからJSTに変更します。 f:id:kurihara_fv:20180618131715p:plain

タイムアウト

処理がタイムアウトしないように1分に変更します。 f:id:kurihara_fv:20180618131721p:plain

トリガー

トリガーにCloudWatch Eventsを選択し、追加します。 f:id:kurihara_fv:20180618131727p:plain

ルールタイプは「スケジュール式」を選択し、以下の値を入力します。

cron(0 * ? * MON-FRI *)

月曜日から金曜日の毎時0分(UTC)にLambdaが実行されます。

[追加]を選択してルールを作成します。 f:id:kurihara_fv:20180618131732p:plain

これで設定はすべて完了です。

動作確認

13時前はこの状態。 f:id:kurihara_fv:20180618131738p:plain

13時を過ぎるとLambdaが実行され、demo01が起動、demo02が停止しました。 f:id:kurihara_fv:20180618131743p:plain 期待通りの動きですね。

最後に

EC2インスタンスの起動、停止を自動化することができました。 Lambdaを使って自動化できることが他にもいろいろとありそうです。