Techfirm Cloud Architect Blog

テックファーム株式会社クラウドインフラグループのブログ

AWS Step Functionsを使ってEC2サーバを特定の時間に一斉起動・一斉停止する

はじめに

プロジェクトなどでEC2サーバを運用している場合、コスト最適化の観点から定期的なサーバの起動・停止が必要になってくるかと思います。
本稿ではそういった運用の手間を削減したい方向けに、特定のタグが付与されたEC2サーバを特定の時間に一斉起動・一斉停止する構成の作成手順をご紹介します。

EC2に付与するタグは以下のような特徴を持ちます。
State-Startタグ : 値がonの状態で起動用ステートマシンが動作するとEC2が起動します State-Stopタグ : 値がonの状態で停止用ステートマシンが動作するとEC2が停止します

値がoffの場合はいずれも動作しません。

必要になる手順は以下になります。
必要なリソースを作成し、実際に動作させるところまで実施します。

  • IAMロールの作成 ステートマシンを動作させるために必要な権限を付与したIAMロールを作成します。
  • ステートマシンの作成 ステートマシンの作成を行います。
  • ステートマシンの動作確認 作成したステートマシンが正常に動作するか、実際にEC2を起動・停止させて確認します。
  • Amazon EventBridgeルールの作成 特定の時間にステートマシンを動作させるためのルールを作成します。
  • Amazon EventBridgeルールの動作確認 作成したルールが正常に動作するか、ステートマシンを介してEC2が起動・停止するかを確認します。

リソース作成

IAMロールの作成

「IAM」を検索します。

iam-search

画面左側の「Roles」をクリックします。

roles

「ロールを作成」をクリックします。

role-create

信頼されたエンティティタイプを「AWSのサービス」、ユースケースを「EC2」として「次へ」をクリックします。

next

許可を追加では何も選択せずに「次へ」をクリックし、名前、確認、および作成ではロール名を入力して「ロールの作成」をクリックします。
ロールを作成したら作成したロールの詳細画面に移動し、信頼関係タブの「信頼ポリシーを編集」をクリックします。

editing-a-trust-policy

以下を貼り付け、「ポリシーの更新」をクリックします。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "states.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

続いて許可タブの「許可を追加」をクリックし、「インラインポリシーを作成」をクリックします。

inline-policy

ポリシーエディタはJSONを選択し、以下を貼り付けて「次へ」をクリックします。
なおポリシーはEC2の起動停止を行うために必要な最小限の権限のみを付与します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeInstances",
                "ec2:StartInstances",
                "ec2:StopInstances"
            ],
            "Resource": "*"
        }
    ]
}

ポリシー名は「EC2Management」とし、「ポリシーの作成」をクリックします。

policy-create

もう1つロールを作成します。
先ほどと同様の手順でロールを作成し、作成したロールの詳細画面に移動して信頼関係タブの「信頼ポリシーを編集」をクリックします。
以下を貼り付け、「ポリシーの更新」をクリックします。
※「aws:SourceAccount」の<アカウントID>にはご自身のアカウントIDを入力してください。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "scheduler.amazonaws.com"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": "<アカウントID>"
                }
            }
        }
    ]
}

ステートマシンの作成

「Step Functions」を検索します。

step-functions

「今すぐ始める」をクリックします。

start

今回は1から作成するのでテンプレートなどは使いません。

batu

画面上部の「{}コード」をクリックし、JSONによる編集モードに切り替えます。

code

まずはインスタンスを自動起動するステートマシンを作成します。
JSONによる編集モードに切り替わったら、以下を貼り付けてください。

{
  "StartAt": "ChoiceTagState",
  "States": {
    "ChoiceTagState": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$$.Execution.Input.Action",
          "StringEquals": "Start",
          "Next": "DescribeInstancesTagStateStart"
        },
        {
          "Variable": "$$.Execution.Input.Action",
          "StringEquals": "Stop",
          "Next": "DescribeInstancesTagStateStop"
        }
      ]
    },
    "DescribeInstancesTagStateStart": {
      "Type": "Task",
      "Next": "ChoiceActionStart",
      "ResultSelector": {
        "InstanceIds.$": "$.Reservations[*].Instances[*].InstanceId",
        "length.$": "States.ArrayLength($.Reservations[*].Instances[*].InstanceId)"
      },
      "Resource": "arn:aws:states:::aws-sdk:ec2:describeInstances",
      "Parameters": {
        "Filters": [
          {
            "Name": "tag:State-Start",
            "Values": [
              "on"
            ]
          }
        ]
      }
    },
    "DescribeInstancesTagStateStop": {
      "Type": "Task",
      "Next": "ChoiceActionStop",
      "ResultSelector": {
        "InstanceIds.$": "$.Reservations[*].Instances[*].InstanceId",
        "length.$": "States.ArrayLength($.Reservations[*].Instances[*].InstanceId)"
      },
      "Resource": "arn:aws:states:::aws-sdk:ec2:describeInstances",
      "Parameters": {
        "Filters": [
          {
            "Name": "tag:State-Stop",
            "Values": [
              "on"
            ]
          }
        ]
      }
    },
    "ChoiceActionStart": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.length",
          "NumericEquals": 0,
          "Next": "Pass"
        },
        {
          "Variable": "$.length",
          "NumericGreaterThan": 0,
          "Next": "StartInstances"
        }
      ]
    },
    "ChoiceActionStop": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.length",
          "NumericEquals": 0,
          "Next": "Pass"
        },
        {
          "Variable": "$.length",
          "NumericGreaterThan": 0,
          "Next": "StopInstances"
        }
      ]
    },
    "Pass": {
      "Type": "Pass",
      "End": true
    },
    "StopInstances": {
      "Type": "Task",
      "Resource": "arn:aws:states:::aws-sdk:ec2:stopInstances",
      "Parameters": {
        "InstanceIds.$": "$.InstanceIds"
      },
      "End": true
    },
    "StartInstances": {
      "Type": "Task",
      "Resource": "arn:aws:states:::aws-sdk:ec2:startInstances",
      "Parameters": {
        "InstanceIds.$": "$.InstanceIds"
      },
      "End": true
    }
  }
}

このコードでは、タグ「State-Start」、値「on」が付与されているEC2インスタンスが起動し、タグ「State-Stop」、値「on」が付与されているEC2インスタンスが停止するようになっています。

続いてIAMロールを設定します。
画面上部の「設定」をクリックしてください。

ステートマシン名を入力し、実行ロールに先ほど作成した「StepFunctions-EC2Management」を選択して「作成」をクリックしてください。

state-create

ステートマシンの動作確認

ここまでで自動起動と自動停止のステートマシンが作成できたので動作確認をします。
実際にEC2インスタンスが起動・停止するので、問題ないか確認した上で実施してください。

ステートマシン「ec2-state-change」の詳細画面に移動し、「実行を開始」ボタンをクリックしてください。

state-create-test

入力 - オプションの欄に以下のJSONを貼り付けて「実行を開始」をクリックします。

{
  "Action": "Start"
}

実行するとEC2の状態が変わりました。

state-change

しばらくすると無事起動されました。

state-start

次に停止の動作確認を行います。
再度「実行を開始」ボタンをクリックし、入力 - オプションの欄に以下のJSONを貼り付けて「実行を開始」をクリックします。

{
  "Action": "Stop"
}

実行するとEC2の状態が変わりました。

state-change2

しばらくすると無事停止しました。

state-stop

起動・停止の動作は確認できたので、次にタグの制御が正常に動いているかを確認します。
「RHEL-EC2」の「State-Start」タグを「off」にしてみます。

state-start-tag-off

入力 - オプションの欄に以下のJSONを貼り付けて「実行を開始」をクリックします。

{
  "Action": "Start"
}

実行すると「Ubuntu-EC2」のみ状態が変わりました。

state-change-ubuntu

起動しました。

ubuntu-start

続いて「RHEL-EC2」を手動で起動し、今度は「RHEL-EC2」の「State-Stop」の値を「off」にします。

state-stop-tag-off

入力 - オプションの欄に以下のJSONを貼り付けて「実行を開始」をクリックします。

{
  "Action": "Stop"
}

実行すると「Ubuntu-EC2」のみ状態が変わりました。

ubuntu-stop

停止しました。

state-change-ubuntu2

タグの制御も問題ないことが確認できました。

Amazon EventBridgeルールの作成

続いて作成したステートマシンを特定の時刻に呼び出すためのAmazon EventBridgeルールを作成します。
開始ルールと停止ルールの2つを作成します。
まずは開始ルールを作成するために、Amazon EventBridgeサービスのページに移動し、「ルールを作成」をクリックします。

eventbridge

ルール名を入力し、ルールタイプには「スケジュール」を選択して「続行してルールを作成する」をクリックします。

eventbridge-rule-create

スケジュールパターンは「特定の時刻」を選択し以下のcron式を設定します。

cron(0 0 ? * 2-6 *)

上記のcron式は、日本時間で月曜日から金曜日の毎朝9:00に動作するものになります。
ただ画像の例では動作確認を行う関係で2分おきに動作するように設定しておきます。

「次へ」をクリックしてターゲット選択画面に移動したら、以下の情報を入力します。

ターゲットタイプ : AWSのサービス
ターゲットを選択 : Step Functions ステートマシン
ステートマシン : <作成したステートマシンを選択してください>
実行ロール : この特定のリソースについて新しいロールを作成
追加設定 : ターゲット入力を設定 : 定数(JSONテキスト)

以下のJSONを貼り付け、「次へ」をクリックします。

{
  "Action": "Start"
}

タグは設定せずに「次へ」をクリックし、確認画面で確認したら「ルールの作成」をクリックします。

eventbridge-rule-create-check

同様に停止ルールも作成します。
作成手順は開始ルールとほぼ同様です。
cron式を入力するところでは以下を入力してください。

cron(0 17 ? * 2-6 *)

上記のcron式は、日本時間で月曜日から金曜日の毎夕17:00に動作するものになります。
画像の例では動作確認を行う関係で3分おきに動作するように設定しておきます。

「次へ」をクリックしてターゲット選択画面に移動したら、以下の情報を入力します。

ターゲットタイプ : AWSのサービス
ターゲットを選択 : Step Functions ステートマシン
ステートマシン : <作成したステートマシンを選択してください>
実行ロール : この特定のリソースについて新しいロールを作成
追加設定 : ターゲット入力を設定 : 定数(JSONテキスト)

以下のJSONを貼り付け、「次へ」をクリックします。

{
  "Action": "Stop"
}

タグは設定せずに「次へ」をクリックし、確認画面で確認したら「ルールの作成」をクリックします。
これでルールの作成が完了しました。

Amazon EventBridgeルールの動作確認

作成したAmazon EventBridgeルールの動作確認を行います。
あらかじめ動作確認用に起動を2分おきに、停止を3分おきに実行するように設定しておいたので、あとは放置しておくだけでEC2インスタンスは起動停止を繰り返すはずです。

少し待つとEC2インスタンスの状態が変わりました。

eventbridge-test-start

このまま実行中のステータスになるかと思いきや、しばらくすると停止中になりました。

eventbridge-test-stop

やはり起動2分、停止3分だと間隔が短かったようですが、時間経過で自動起動・自動停止が行われていることを確認できました。

まとめ

本稿では特定のタグが付与されたEC2サーバを特定の時間に一斉起動・一斉停止する構成の作成手順をご紹介しましたが、以下のように用途に応じてカスタマイズできます。

  • 特定の時間帯や曜日でのみ起動・停止を行いたい → Amazon EventBridgeルールのcron式を変更することで任意の時間帯・曜日にカスタマイズできます。
  • 開発サーバや本番サーバなど、環境ごとに異なる起動・停止ルールで運用したい → 開発環境用・本番環境用のAmazon EventBridgeルールをそれぞれ作成し、どちらも同じステートマシンをターゲットに設定します。 ステートマシン自体を複製する必要はありません。
  • EC2に付与するタグ名を変更したい → ステートマシンJSON内の「"tag:State-Start"」と「"tag:State-Stop"」を変更することで任意のタグ名にカスタマイズできます。 またタグの値を変更したいときは「"Values": [on]」を任意の値に変更します。
{
"Name": "tag:State-Start",
"Values": [
    "on"
  ]
}
・
・
・
{
  "Name": "tag:State-Stop",
  "Values": [
    "on"
  ]
}

EC2サーバの起動・停止を手動で実施していると、ふとした時に対応漏れが発生するかもしれません。
とくにプロジェクトに新しいメンバーが加わった場合や、担当者の入れ替えなどがあった場合にこうした事故は発生しやすいかと思います。
本稿でご紹介した構成を利用することでEC2に特定のタグを付与するだけという比較的簡単な方法で対応できます。
EC2サーバの起動・停止を自動化することを検討している方はぜひ活用してみてください。