LoginSignup
0
2

More than 3 years have passed since last update.

スケジュールに従ってEC2を自動起動・自動停止する(Lambda編)

Last updated at Posted at 2021-04-10

IAMロールの作成

Lambda関数の新規作成画面からカスタムロールを新規作成できなくなっているようなので、あらかじめIAMのメニューからロール、ポリシーを作成します。以下の権限が必要となります。

  • LambdaからEC2を停止/起動するための権限
  • CloudWatchLogsにログを出力するための権限

image.png

ロール作成時、信頼されたエンティティはLambdaを選択し、以下のポリシーで作成します。通常はec2:DescribeInstancesの権限は不要ですが、今回はNameタグをキーにインスタンスIDを取得してからEC2を停止するので付与しています。

{
    "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",
                "ec2:DescribeInstances"
            ],
            "Resource": "*"
        }
    ]
}

Lambda関数の作成

ランタイムはPython3.8、実行ロールは事前に作成したIAMロールを選択します。

image.png

Lambdaが作成できたらlambda_function.pyのコードを以下のように修正して「Deploy」ボタンを押して保存します。
printの2行はログに出力させるためのものです。

image.png

import boto3

def lambda_handler(event, context):
  # valiables
  instance_name = event["InstanceName"]

  # get instance id
  client = boto3.client("ec2")
  instance_id = client.describe_instances(
    Filters=[{"Name": "tag:Name","Values": [instance_name]}]
  )["Reservations"][0]["Instances"][0]["InstanceId"]

  # start / stop ec2
  if event["Action"] == "start":
      response = client.start_instances(InstanceIds=[instance_id])
  elif event["Action"] == "stop":
      response = client.stop_instances(InstanceIds=[instance_id])

  print(event)
  print(response)

全然知らなかったのですが、Lambdaではデフォルトではlambda_function.pylambda_handler関数が呼ばれるようになっています。ランタイム設定のハンドラを見ればわかります。

image.png

なので、初めからあるlambda_function.pyのファイル名を変えてしまうと中身のコードが正しくても動かなくなりますし、コードはlambda_handler関数の中に記述する必要があります。def lambda_handler(event, context):の1行はお決まりのコードだと理解しておけば問題なさそう。

Python の AWS Lambda 関数ハンドラー

また、def lambda_handler(event, context):の第1引数のeventでイベントを渡しています。
コードの中で利用する値をJSON形式で渡すことができるようです。今回のコードでいうと、以下の2か所をJSONで渡すことにしています。(こうすることで、コードに直接パラメータを埋め込む必要がなくなり汎用性が高まるのだと理解)

  • event["InstanceName"]
  • event["Action"]

InstanceNameはNameタグの値、Actionはstart/stopのいずれかを渡す想定です。
このあたりは、クラスメソッドさんの以下の記事を参考にしています。

できるだけシンプルな仕組みで簡単にEC2の自動起動・停止を実現したい!

Lambda関数の実行

「Test」ボタンを押すとJSONで渡す値(イベント)を指定する画面になります。
イベント名は「StartStopEC2」として、JSONの中身は以下のように書き換えて作成します。
まずは起動(start)から。

image.png

{
  "Action": "start",
  "InstanceName": "AmazonLinux01"
}

テストイベントが作成できたらもう一度「Test」ボタンを押すと関数が実行されます。
Function Logsを見ると、もともとstoppedだったのがpendingになっていることがわかります。
EC2の画面を見てみると「実行中」に変わっていました。

image.png
image.png

Test → Configure test eventからテストイベントの設定を開いて、今度はstopに変更して保存します。

image.png

再度テストを実行してみると、今度はもともとrunningだったのがstoppingになっていることがわかります。
EC2の画面では「停止中」になっていて、しばらくすると「停止済み」となりました。

image.png
image.png

CloudWatch Eventsにスケジュールを登録

スケジュールに従ってLambda関数を実行するにはCloudWatch Eventsを利用します。CloudWatchのメニューの中にあるイベント → ルールから登録できます。とりあえずの動作確認が目的なので、スケジュールは5分おきに実行するように指定しています。cronの書き方は以下の公式ドキュメントを見ればだいたいわかります。

ルールのスケジュール式

image.png

ターゲットの入力の設定では「定数(JSON テキスト)」を選択し、ActionとInstanceNameの項目を入れたJSONを渡してあげます。以下は、EC2を起動させる場合のJSONですが、テストイベントの時と同じようにstartstopに変えてあげればEC2を停止するルールを作成することができます。

{"Action": "start", "InstanceName": "AmazonLinux01"}

つまり、ルールを2つ作成して、EC2起動は平日の朝の9時、EC2停止は平日の20時といった形で設定することになります。

ルールを登録してしばらく待つと、停止済みだったEC2が起動してきました。
ログもCloudWatch Logsに出力されていました。

image.png

まとめ

python(boto3)でコードを書くのが敷居が高いイメージがあり、なかなかちゃんと触る機会がなかったのですが、やってることはWindowsのタスクスケジュラやLinuxのcronと変わりはないので、基本的な使い方を覚えてしまえばサーバレスならではのメリットを享受できそうだと感じました。

ここまで書いておきながらなんですが、EC2起動/停止はLambdaでコードを書かなくてもSSM Automationで実現できるので、また試してみて投稿したいと思います!

参考リンク

Python の AWS Lambda 関数ハンドラー

できるだけシンプルな仕組みで簡単にEC2の自動起動・停止を実現したい!

ルールのスケジュール式

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2