Lambda を使用して、Amazon EC2 インスタンスを一定の間隔で停止および起動するにはどうすればよいですか?

最終更新日: 2021 年 2 月 15 日

EC2 インスタンスを自動的にで停止および起動することで、Amazon Elastic Compute Cloud (Amazon EC2) の使用量を削減したいと考えています。そのための、AWS Lambda と Amazon CloudWatch Events の使い方を教えてください。

簡単な説明

注: この設定例は、簡単な解決策です。さらに確実な解決には AWS Instance Scheduler を使用してください。詳細については、「AWS Instance Scheduler を使用してインスタンスを停止および開始する方法を教えてください」を参照してください。

Lambda を使用して、Amazon EC2 インスタンスを一定の間隔で停止および起動するには、次の手順を実行します。

1.    AWS Identity and Access Management (IAM) ポリシー、および Lambda 関数実行のためのロールを作成します。

2.    Lambda 関数を、EC2 インスタンスを停止および起動するように作成します。

3.    Lambda 関数のテスト

4.    スケジュールに合わせこの関数をトリガーするよう、CloudWatch Events のルールを作成します
注意: また、AWS アカウントで発生するイベントがトリガーするよう、ルールを作成することもできます

または、この記事の最後に提供されている AWS CloudFormation テンプレートを使用して、手順を自動化することもできます。

解決方法

注意: 以下の手順を完了した後、起動時のクライアントエラーが表示される場合は、この「起動時のクライアントエラーのトラブルシューティングの記事 」に記載されている手順を実行します。

停止して起動する EC2 インスタンスの ID を取得します。次に、以下を実行します。

Lambda 関数の IAM ポリシーと実行ロールを作成する

1.    JSON ポリシーエディターを使い IAM ポリシーを作成します。次の JSON ポリシードキュメントをコピーして、ポリシーエディターに貼り付けます。

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

2.    Lambda のための IAM ロールを作成します

重要: アクセス許可ポリシーを Lambda にアタッチする場合は、必ずここで作成した IAM ポリシーを選択してください。

EC2 インスタンスを停止および起動する Lambda 関数を記述する

1.    AWS Lambda コンソールで、[Create function] クリックします。

2.    [Author from scratch] をクリックします。

3.    [Basic information] に次の事項を追加します。
[Function name] に EC2 インスタンスを停止させる関数の名前を入力します。たとえば「StopEC2Instances」のようになります。
[Runtime] では [python 3.8] を選択します。
[Permissions] で [Choose or create an execution role] を展開します。
[Execution role] で [Use an existing role] を選択します。
[Role] で今回作成した IAM ロールを選択します。

4.    [Create function] をクリックします。

5.    [Function code] で、次のコードをコピーして 、コードエディターのエディタペインに貼り付けます (lambda_function)。このコードでは指定したEC2 インスタンスを停止します。

関数コード例 - EC2 インスタンスの停止

import boto3
region = 'us-west-1'
instances = ['i-12345cb6de4f78g9h', 'i-08ce9b2d7eccf6d26']
ec2 = boto3.client('ec2', region_name=region)

def lambda_handler(event, context):
    ec2.stop_instances(InstanceIds=instances)
    print('stopped your instances: ' + str(instances))

注意: [region] で、「us-west-1」を自分のインスタンスがある AWS リージョンと置き換えます。[instances] で、サンプルの EC2 インスタンス ID を、実際に停止および起動したいものの ID と置き換えます。

6.    [Basic settings] の [Timeout] に 10 秒を設定します。

注意: 自分のユースケースに合わせ、Lambda 関数の設定を行います。例えば、複数のインスタンスを停止および起動したい場合は、TimeoutMemory に違う値が必要になることがあります。

7.    [Deploy] を選択します。

8.    他にも関数が必要なときは、前出の 1~7 のステップを繰り返します。関数に EC2 インスタンスを起動させるには、前出の例を次のように変更します。
ステップ 3 で、以前に使用した関数名とは異なる関数名を入力します。たとえば「StartEC2Instances」のようになります。
ステップ 5 で、コードエディター (lambda_function) のエディターペインには、次のコードをコピーして、貼り付けます 。

関数コード例 - EC2 インスタンスの開始

import boto3
region = 'us-west-1'
instances = ['i-12345cb6de4f78g9h', 'i-08ce9b2d7eccf6d26']
ec2 = boto3.client('ec2', region_name=region)

def lambda_handler(event, context):
    ec2.start_instances(InstanceIds=instances)
    print('started your instances: ' + str(instances))

注意: region および instances では、EC2 インスタンスを停止させたコードと同じ値を使用します。

Lambda 関数のテスト

1.    AWS Lambda コンソールで、[Functions] をクリックします。

2.    作成した関数から 1 つ選択します。

3.    [Test] を選択します。

4.    [Configure test event] ダイアログボックスで、[Create new test event] を選択します。

5.    イベント名を入力します。 その後、[Create] を選択します。

注意: 今回の関数では使用しないため、このテストに使うイベントに関する JSON を修正する必要はありません。

6.    [Test] を選択して関数を実行します。

7.    先に作成した関数で行った、1~6 のステップを繰り返します。

ヒント: 作成した関数が希望通りに動作するかのテストの前後で、EC2 インスタンスの状態をチェックします。

Lambda 関数をトリガーするよう、CloudWatch Events のルールを作成します。

1.    Amazon CloudWatch コンソールを開きます。

2.    左ナビゲーションペインの [Events] の下にある [Rules] をクリックします。

3.    [Create rule] を選択します。

4.    [Event Source] の [Schedule] をクリックします。

5.    次に、以下のいずれかを行います。
[Fixed rate of] に、分、時間、もしくは日の単位で時間間隔を入力します。
[Cron expression] に、Lambda にインスタンスを停止させる時刻を表すスケジュール式を入力します。式の構文に関する情報はルールのスケジュール式を参照してください。
注意
: Cron 式 は UTC で評価されます。適切なタイムゾーンに式を調整して使用してください。

6.    [Targets] の [Add target] をクリックします。

7.    [Lambda function] をクリックします。

8.    [Function] から EC2 インスタンスを停止させる関数を選択します。

9.    [Configure details] を選択します。

10.    [Rule definition] で次の事項を行います。
[Name] には「StopEC2Instances」といったようなルールを指定する名称を入力します。
(任意) [Description] でルールを記述します。たとえば「Stops EC2 instances every night at 10 PM.」のようになります。
[State] で [Enabled] チェックボックスをオンにします。

11.    [Create rule] を選択します。

12.    1~11 のステップを繰り返し EC2 インスタンスを起動するためのルールを作成します。そのために、次の事項を変更して指定します。
ステップ 5 での [Cron expression] には、 Lambda にインスタンスを起動させる時刻を表すスケジュール式を入力します。
ステップ 8 での [Function] では、EC2 インスタンスを起動する関数名を選択します。
ステップ 10 の [Rule definition] で、[ 名前 ] (StartEC2Instancesなど) を入力します。
(オプション)「EC2 インスタンスを毎朝 7 時に開始する」などの説明を入力します。

(オプション) AWS CloudFormation テンプレートを使用して手順を自動化する

1.    次の AWS CloudFormation テンプレートを YAML ファイルとして保存します。

2.    YAML ファイルで、必要な変数を入力します。

3.    Visual Studio または AWS コマンドラインインターフェイス (AWS CLI) で AWS CloudFormation テンプレートをデプロイします。

注意: AWS CLI コマンドの実行時にエラーが発生した場合は、AWS CLI の最新バージョンを使用していることを確認してください

テンプレートの使用方法の詳細については、「 AWS CloudFormation テンプレートを使用する 」を参照してください。

Lambda を使用して EC2 インスタンスを定期的に停止および起動するための AWS CloudFormation テンプレート

AWSTemplateFormatVersion: '2010-09-09'
Description: Lambda function with cfn-response.
Parameters: 
  instances: 
    Default: i-12345678
    Description: Instance ID's seperated by commers 
    Type: String
  Region: 
    Description: region only 1 region supported 
    Type: String
  StopScheduled: 
    Default: cron(0 18 ? * MON-FRI *)
    Description: enter an Schedule expression example cron(0 18 ? * MON-FRI *) see https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/ScheduledEvents.html
    Type: String
  StartScheduled: 
    Default: cron(0 7 ? * MON-FRI *)
    Description: enter an Schedule expression example cron(0 7 ? * MON-FRI * ) see https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/ScheduledEvents.html
    Type: String
Resources:
  StopEC2Instances:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: python3.8
      Role: !GetAtt Role.Arn
      Handler: index.lambda_handler
      Timeout: 60
      Environment:
         Variables:
          instances: !Ref instances
          Region: !Ref Region
      Code:
        ZipFile: |
          import json
          import re
          import os
          import boto3
          
          def lambda_handler(event, context):
            # TODO implement
            instances_str = os.environ['instances']
            region = os.environ['Region']
            ec2 = boto3.client('ec2', region_name=region)
            instances= re.findall(r"i-[0-9a-z]{17}|i-[0-9a-z]{8}", instances_str)
            print('stopped your instances: ' + str(instances) + "in Region "+ region)
            ec2.stop_instances(InstanceIds=instances)
            
            return {
              'statusCode': 200,
              'body': json.dumps('stopped your instances: ' + str(instances))
            }
      Description: Function that stops instances
  permissionForEventsToInvokeStopEC2Instances:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !GetAtt StopEC2Instances.Arn
      Action: lambda:InvokeFunction
      Principal: events.amazonaws.com
      "SourceArn" : !GetAtt StopScheduledRule.Arn

  StartEC2Instances:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: python3.8
      Role: !GetAtt Role.Arn
      Handler: index.lambda_handler
      Timeout: 60
      Environment:
         Variables:
          instances: !Ref instances
          Region: !Ref Region
      Code:
        ZipFile: |
          import json
          import re
          import os
          import boto3
          
          def lambda_handler(event, context):
            # TODO implement
            instances_str = os.environ['instances']
            region = os.environ['Region']
            ec2 = boto3.client('ec2', region_name=region)
            instances= re.findall(r"i-[0-9a-z]{17}|i-[0-9a-z]{8}", instances_str)
            print('started your instances: ' + str(instances)+ "in Region "+ region)
            ec2.start_instances(InstanceIds=instances)
            
            return {
              'statusCode': 200,
              'body': json.dumps('started your instances: ' + str(instances))
            }
      Description: Function that started instances
  permissionForEventsToInvokeStartEC2Instances:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !GetAtt StartEC2Instances.Arn
      Action: lambda:InvokeFunction
      Principal: events.amazonaws.com
      "SourceArn" : !GetAtt StartScheduledRule.Arn

  Role:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
              - lambda.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Path: /
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
      Policies:
        - PolicyName: Ec2permissions
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                - "ec2:StartInstances"
                - "ec2:StopInstances"
                Resource: '*'
                

  StopScheduledRule: 
    Type: AWS::Events::Rule
    Properties: 
      Description: "ScheduledRule"
      ScheduleExpression: !Ref StopScheduled
      State: "ENABLED"
      Targets: 
        - 
          Arn: !GetAtt StopEC2Instances.Arn
          Id: "TargetFunctionV1"
  StartScheduledRule: 
    Type: AWS::Events::Rule
    Properties: 
      Description: "ScheduledRule"
      ScheduleExpression: !Ref StartScheduled
      State: "ENABLED"
      Targets: 
        - 
          Arn: !GetAtt StartEC2Instances.Arn
          Id: "TargetFunctionV1"