Amazon EC2の自動起動・停止をAWS CloudFormationで実装してみた

AWS

2023.8.4

Topics

はじめに

こんにちは、hengeです。
今回はEC2インスタンスの自動起動・停止という機能をCloudFormationで実装する方法についてまとめました。

皆様、AWSの利用料金…お困りではないですか?
AWSのコスト削減方法には、使用するリソースの設定見直しやログファイルなどのライフサイクルの設定など様々な方法があります。
それらの方法の1つに、EC2インスタンスの稼働時間の削減があります。

ご存じの通りEC2利用料金はインスタンスの稼働時間によって増加します。
そこで、インスタンスを利用しない時間にインスタンスを停止しておくことでコストの削減が見込めます。

今回はCloudFormationとEventBridgeを用いて、EC2インスタンスを自動的に停止・起動してくれる機能を楽に実装する方法をお伝えいたします。

EventBridgeによる自動化

いざコスト削減のためにEC2インスタンスを勤務時間外には停止しておくようにしよう!とルールを定めても、全てが上手くいくわけではありません。
手動でインスタンスを停止するということになると、

  • 停止し忘れる
  • 誤って停止ではなく終了(インスタンスの削除)してしまう
  • インスタンス数が多い場合面倒

といった問題が考えられます。
そこで、こういった単純作業は自動化しましょう。
インスタンスの停止をシステム上で行うことで、人の手を介さないことからヒューマンエラーの防止になりますし、工数削減にもなります。

今回は自動化のためにEventBridgeというサービスを利用します。
EventBridgeでは予め「ルール」と「ターゲット」という設定をすると、ルールに従ってターゲットで記述された動作を実行します。
つまり、「指定の時刻になったらEC2インスタンスを起動・停止する」というルールを作成して、起動・停止を自動化したいEC2インスタンスをターゲットで指定すると、自動起動・停止が実現できるわけですね。
EventBridgeについて詳しく知りたい方は以下の記事をご覧ください。
Amazon EventBridge とは – Amazon EventBridge

CloudFormationによるリソースの作成

CloudFormationとは、コードによってAWSリソースを管理するサービスです。
初めてAWSを触る方はコンソール画面からぽちぽちとリソースの詳細設定を行うかと思いますが、毎回それを行うのは面倒ですよね。
一方でCloudFormationを利用すれば、予めリソースの詳細設定などを記述したファイルをアップロードするだけで複数のリソースを作成できます。

つまり、CloudFormationを利用することで、

  • ヒューマンエラー防止
  • 工数削減

といったメリットが見込めます。
この2つは自動化のメリットでもありましたね。
せっかく自動化でこれらのメリットを享受できてもその設定段階でミスをしたり手間がかかっては本末転倒ですから、設定から実際の稼働までメリットを生かしましょう。

CloudFormationについてより詳しく知りたい方は以下の記事をご覧ください。

関連記事
【初心者の目線でわかりやすい】AWS CloudFormation1~概念を学ぼう

テンプレート紹介

早速ですが、今回用いたテンプレートをご紹介します。
本テンプレートは東京リージョンでの使用を想定しています。

AWSTemplateFormatVersion: "2010-09-09"
Description: EventBridge Rule Test

# ------------------------------------------------------------#
#  VPC
# ------------------------------------------------------------#
Resources:
  VPC:
    Type: "AWS::EC2::VPC"
    Properties:
      CidrBlock: "10.0.0.0/16"

# ------------------------------------------------------------#
#  Subnet
# ------------------------------------------------------------#
  PrivateSubnet:
    Type: "AWS::EC2::Subnet"
    Properties:
      AvailabilityZone: ap-northeast-1a
      CidrBlock: "10.0.0.0/24"
      VpcId: !Ref VPC

# ------------------------------------------------------------#
#  EC2
# ------------------------------------------------------------#
  EC2Instance:
    Type: "AWS::EC2::Instance"
    Properties:
      ImageId: ami-0e1a141f1d5c52056
      InstanceType: t2.micro
      SubnetId: !Ref PrivateSubnet
      Tags:
        - Key: Name
          Value: henge-test-instance


# ------------------------------------------------------------#
#  EventBridge
# ------------------------------------------------------------#
  # イベント実行に必要なIAMロールを作成
  AmazonSSMAutomationRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
              # EventBridgeのサービスプリンシパル
              - "events.amazonaws.com" 
            Action: sts:AssumeRole
      ManagedPolicyArns:
        # オートメーションのためのIAMマネージドポリシー
        - arn:aws:iam::aws:policy/service-role/AmazonSSMAutomationRole 
  # EC2インスタンスを自動起動するルール
  StartEC2InstanceRule:
    Type: AWS::Events::Rule
    Properties:
      Name: StartEC2Instance
      Description: StartEC2Instance
      # ルールを実行するスケジュール
      ScheduleExpression: cron(20 05 ? * * *)
      State: ENABLED
      Targets:
      # EventBridgeが呼び出す操作のARN
      - Arn: arn:aws:ssm:ap-northeast-1::automation-definition/AWS-StartEC2Instance:$DEFAULT
        Id: StartEC2Instance
        # 先ほど作成したIAMロールのARN
        RoleArn: !Sub ${AmazonSSMAutomationRole.Arn}
        # EC2インスタンスの自動起動の操作時に入力するパラメータ
        Input: !Sub '{"InstanceId":["${EC2Instance}"]}'
  StopEC2InstanceRule:
    Type: AWS::Events::Rule
    Properties:
      Name: StopEC2Instance
      Description: StopEC2Instance
      ScheduleExpression: cron(25 05 ? * * *)
      State: ENABLED
      Targets:
      - Arn: "arn:aws:ssm:ap-northeast-1::automation-definition/AWS-StopEC2Instance:$DEFAULT"
        Id: StopEC2Instance
        RoleArn: !Sub ${AmazonSSMAutomationRole.Arn}
        Input: !Sub '{"InstanceId":["${EC2Instance}"]}'

以下で今回のポイントであるEventBridge関連の記述について解説いたします。
なお、インスタンス自動停止のルールについては、ほぼインスタンス自動起動のルールと同様であるため割愛いたします。

EventBridgeがイベントを実行するためのIAMロール作成

# ------------------------------------------------------------#
#  EventBridge
# ------------------------------------------------------------#
  # イベント実行に必要なIAMロールを作成
  AmazonSSMAutomationRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
              # EventBridgeのサービスプリンシパル
              - "events.amazonaws.com"
            Action: sts:AssumeRole
      ManagedPolicyArns:
        # オートメーションのためのIAMマネージドポリシー
        - arn:aws:iam::aws:policy/service-role/AmazonSSMAutomationRole

本リソースではEventBridgeが自動的にEC2インスタンスを操作するためのIAMロールを作成しています。
これがないとIAM権限不足で自動起動・停止を行うことができません。
EventBridgeがイベントを実行するときに、本ロールを呼び出します。
本IAMロールにアタッチされたIAMマネージドポリシーの詳細については以下をご覧ください。
AmazonSSMAutomationRole – AWS マネージドポリシー

EC2インスタンス自動起動のルール作成

  # EC2インスタンスを自動起動するルール
  StartEC2InstanceRule:
    Type: AWS::Events::Rule
    Properties:
      Name: StartEC2Instance
      Description: StartEC2Instance
      # ルールを実行するスケジュール
      ScheduleExpression: cron(20 05 ? * * *)
      State: ENABLED
      Targets:
      # EventBridgeが呼び出す操作のARN
      - Arn: arn:aws:ssm:ap-northeast-1::automation-definition/AWS-StartEC2Instance:$DEFAULT
        Id: StartEC2Instance
        # 先ほど作成したIAMロールのARN
        RoleArn: !Sub ${AmazonSSMAutomationRole.Arn}
        # EC2インスタンスの自動起動の操作時に入力するパラメータ
        Input: !Sub '{"InstanceId":["${EC2Instance}"]}'

本リソースではEventBridgeがEC2インスタンスを自動起動するルールを作成しています。
仕組みとしては、cron式で記述されたScheduleExpressionで指定された時間になるとInstanceIDで指定されたEC2インスタンスを自動的に起動するようになっています。
しかし、EventBridgeが直接EC2インスタンスをどうこうするわけではありません。
インスタンスの起動・停止を行うのはSystems Manager Automationというサービスであり、EventBridgeはSystems Manager Automationを呼び出しているだけですのでご注意ください。
ちなみに、本リソース内のRoleArnという変数に関する記述が今回の難所でした。

難所

RoleArn: !Sub ${AmazonSSMAutomationRole.Arn}

このRoleArnという変数では作成したIAMロールのARNを参照したいのですが、リソース名のみではIAMロール名が戻り値として返されてしまいます。
そこで、このようにリソース名の後ろに.Arnと入力するとこのリソースのARNが返されるようになります。

今まで戻り値を気にしたことがあまりなかったので個人的には良い学びになりました。

${MyInstance.PublicIp} などのリソース属性を指定すると、CloudFormation は Fn::GetAtt 組み込み関数を使用した場合と同じ値を返します。
出典:Fn::Sub – AWS CloudFormation

検証

さて、ここまでテンプレートの解説をしてきましたが、百聞は一見に如かずということで検証を行ってみましょう。
検証手順は以下の通りです。

  1. CloudFormationでスタックを作成
  2. EC2インスタンスを手動で停止
  3. EC2インスタンスの稼働状況を監視

CloudFormationでスタックを作成

コンソール画面から「henge-test-for-blog」という名前のスタックを作成します。

CloudFormationで「henge-test-for-blog」というスタックを作成

念のため作成したリソースを確認します。

CloudFormationで作成したEventBridgeルール「StartEC2Instance」「StopEC2Instance」が存在していることを確認

無事EventBridgeのルールが作成されました。
今回の検証ではインスタンスの起動時間を14時20分、停止時間を14時25分としています。

EventBridgeルール「StartEC2Instance」でEc2インスタンスの自動起動時間が14時20分であることを確認 EventBridgeルール「StopEC2Instance」でEC2インスタンスの自動停止時間が14時25分であることを確認

EC2インスタンスの稼働状況を監視

では、本当にEC2インスタンスが自動的に起動・停止されるのか確認しましょう。
14時18分現在、インスタンスの状態は「停止済み」です。

EC2インスタンスを手動で停止

このまま14時20分まで待ってみると、インスタンスの状態が「保留中」に。

EC2インスタンスが起動中

それから数十秒すると、インスタンスの状態が「実行中」に変化しました。

EC2インスタンスが起動

無事に自動起動できたようですね!
続いて14時25分まで待つと、今度はインスタンスの状態が「停止中」に。

EC2インスタンスが停止中

それから数十秒すると、インスタンスの状態が「停止済み」に変化しました。

EC2インスタンスが停止

自動停止についても上手くいったようです!

まとめ

今回はEventBridgeによるEC2インスタンスの自動起動・停止をCloudFormationで実装しました。

CloudFormationはデプロイの手軽さやヒューマンエラー防止の観点から見れば非常に便利な概念ですので、是非ご活用いただければと思います。
EventBridgeによる自動化も工数の削減やヒューマンエラー防止のために積極的に活用したい機能ですね。
せっかくクラウドというサービスを利用しているからには楽できる部分はどんどん楽していきましょう!

ここまで読んでいただきありがとうございました!

テックブログ新着情報のほか、AWSやGoogle Cloudに関するお役立ち情報を配信中!

henge

第二SAチームのhengeです。 ゲームとゴルフが好きなエンジニアです。 よろしくお願いします。

Recommends

こちらもおすすめ

Special Topics

注目記事はこちら