AWSLab

EC2の自動起動・停止をLambda無しで組む【EventBridge Scheduler】

スポンサーラベル
EC2の自動起動・停止の構成比較。従来はEventBridge RulesからLambdaを呼び、LambdaがEC2 APIを叩いていた。EventBridge Schedulerではスケジュールから直接EC2 APIを呼ぶため、書くコードがゼロになる AWS

当サイトはアフィリエイト広告を利用しています。

検証用に立てたEC2は、放っておくと動かしていない時間もお金がかかります。かといって毎回手で起動・停止するのは続きません。

この手の自動化は長らく「EventBridgeでLambdaを起動し、LambdaがEC2 APIを叩く」構成が定番でしたが、いまはLambda無しでできます。 EventBridge Schedulerを使うと、スケジュールから直接 EC2 API を呼べるため、関数もランタイムもメンテナンス対象から消えます。

本記事では、Difyと Presenton の検証環境2台を「土日の9時から18時だけ動かす」設定にした手順をまとめます。実際に東京リージョンで作成して確認した内容です。

1. Lambdaを書かずに済むようになった

EC2の自動起動・停止の構成比較。従来はEventBridge RulesからLambdaを呼び、LambdaがEC2 APIを叩いていた。EventBridge Schedulerではスケジュールから直接EC2 APIを呼ぶため、書くコードがゼロになる

EventBridge Scheduler は 2022年に登場したサービスで、従来の EventBridge Rules とは別物です。EC2 の電源操作という用途では、次の3点が効きます。

  • ユニバーサルターゲットaws-sdk 形式のターゲットで、AWSのAPIを直接呼べる。Lambdaを挟む必要がない
  • タイムゾーン指定Asia/Tokyo をそのまま指定できる。cronをUTCに読み替える手間が消える
  • スケジュール単位の管理 — ルールとターゲットが1対1で、対象を後から変えやすい

従来の構成では、Lambdaのコードとその実行ロール、デプロイの仕組みが一式必要でした。いま同じ要件なら、書くのはスケジュールの定義だけです。

2. 稼働させる時間帯を決める

今回の要件は「土日の9:00〜18:00だけ動かす」でした。素直に組むなら次の2本です。

用途cron(Asia/Tokyo)
起動cron(0 9 ? * SAT,SUN *)
停止cron(0 18 ? * SAT,SUN *)

ただし、停止のほうは曜日を絞らず毎日にしました。

cron(0 18 ? * * *)

理由は消し忘れです。平日に手で起動して作業し、そのまま落とし忘れるのが一番ありがちな失敗で、起動は自動化しても、停止だけは手作業が残るという状態になりがちです。停止を毎日にしておけば、平日に起動したままでもその日のうちに止まります。

止まって困るものが動いていないなら、停止側は広めに掛けておくほうが安全です。

3. スケジューラ用のIAMロールを作る

EventBridge Scheduler が EC2 を操作するためのロールを用意します。信頼するのは scheduler.amazonaws.com です。

権限は対象のインスタンスだけに絞ります。 Resource: "*" にすると、アカウント内の全EC2を止められるロールになってしまいます。

policy = {
    "Version": "2012-10-17",
    "Statement": [{
        "Effect": "Allow",
        "Action": ["ec2:StartInstances", "ec2:StopInstances"],
        # 対象のインスタンスだけに絞る。他のインスタンスは触れない
        "Resource": [
            f"arn:aws:ec2:{REGION}:{account}:instance/{i}" for i in TARGETS
        ],
    }],
}

作りたてのロールはスケジューラ側からすぐに見えないことがあるので、作成直後にスケジュールを作るなら少し待つと安定します。

4. スケジュールを作る

ターゲットのARNに arn:aws:scheduler:::aws-sdk:ec2:startInstances を指定するのがポイントです。呼びたいAPIをARNの末尾に書く形式で、入力はそのAPIのリクエストをJSONで渡します。

sch.create_schedule(
    Name="ec2-start-weekend",
    GroupName="default",
    ScheduleExpression="cron(0 9 ? * SAT,SUN *)",
    # UTCへの読み替えが不要になる
    ScheduleExpressionTimezone="Asia/Tokyo",
    FlexibleTimeWindow={"Mode": "OFF"},
    Target={
        "Arn": "arn:aws:scheduler:::aws-sdk:ec2:startInstances",
        "RoleArn": role_arn,
        # EC2 の StartInstances に渡すリクエストそのもの
        "Input": json.dumps({"InstanceIds": ["i-xxxxxxxx", "i-yyyyyyyy"]}),
        "RetryPolicy": {"MaximumRetryAttempts": 3},
    },
)

FlexibleTimeWindowOFF にしないと、指定時刻から最大数分ずれて実行されます。電源操作では厳密である必要はありませんが、動作確認のときにずれると原因の切り分けがしづらいので切っています。

停止側は ArnstopInstances に変えるだけです。

4.1 インスタンスIDの直書きに注意

Input にインスタンスIDを書く方式は単純ですが、インスタンスを作り直すとIDが変わります。

CloudFormationで管理している環境だと、更新の内容によってはインスタンスが置き換わります。そうなるとスケジューラは存在しないIDを掴んだまま、エラーにもならず静かに何もしなくなります

タグで対象を選びたい場合は、SSM Automation の AWS-StopEC2Instance をターゲットにしてタグ指定で流す方法があります。今回は対象が2台で固定のため、IDを直書きしたうえで「作り直したらここも直す」とコメントを残す方針にしました。

5. 作成できたか確認する

スケジュールの状態と、対象インスタンスの現在の状態をまとめて確認します。

aws scheduler list-schedules --region ap-northeast-1 \
  --query "Schedules[].{Name:Name,State:State}" --output table

実際の出力です。

ec2-start-weekend    ENABLED   cron(0 9 ? * SAT,SUN *) [Asia/Tokyo] -> 2台
ec2-stop-daily       ENABLED   cron(0 18 ? * * *)      [Asia/Tokyo] -> 2台

作成できても、初回の実行時刻が来るまでは本当に動くか分かりません。 最初の1回は時刻を数分後に変えて試すか、実行後にインスタンスの状態を確認しておくと確実です。

6. 止める前に確認しておくこと

停止でコストが下がるのは計算料金だけです。止めても消えない課金があります。

項目停止中の扱い
EC2 の計算料金かからない(これが削減対象)
EBS ボリュームかかり続ける(30GBで月$3弱)
Elastic IPかかる(実行中のインスタンスに紐づいていない扱いになるため)
データ消えない(再起動と同じ扱い)

もうひとつ、忘れると痛いのがパブリックIPです。

EIPを付けていないインスタンスは、停止・起動のたびにパブリックIPが変わります。 毎週アクセス先が変わることになり、ブックマークもセキュリティグループの設定も追随できません。自動停止を入れるなら、EIPはセットで考える必要があります。

今回の2台はどちらもEIPを付けてあったため、この問題は起きませんでした。EIPを後から足す手順はCloudFormationのEC2にElastic IPを後付けするにまとめています。

7. どれくらい安くなるか

t3.large と t3.medium の2台で試算します。東京リージョンのオンデマンド料金で、合計およそ $0.163/時です。

稼働パターン月あたりの稼働時間計算料金(月)
24時間365日730時間約 $119
平日+土日の日中(9〜18時)273時間約 $45
土日の日中のみ78時間約 $13

24時間動かす場合の約9分の1になります。EBSとEIPの固定費(月$8程度)を足しても、削減幅のほうがはるかに大きいです。

検証環境は「使っていない時間」が圧倒的に長いので、止める仕組みを最初に入れておくほど効きます

8. ハマった点:IAMのDescriptionはASCIIのみ

ロールの説明文に日本語を入れたところ、作成に失敗しました。

ValidationError: 1 validation error detected: Value at 'description' failed to
satisfy constraint: Member must satisfy regular expression pattern:
[	

 -~¡-ÿ]*

正規表現を見ると、ASCIIとラテン1補助しか許されていません。 同じ制約は Data Lifecycle Manager のポリシー説明にもあり、こちらは InvalidRequestException で弾かれました。

日本語のコメントを残したいときは、説明フィールドではなくコード側のコメントに書くのが確実です。AWSのAPIには日本語を通す前提のない項目がまだあります。

9. まとめ

EC2の電源操作を自動化するときの要点です。

  • EventBridge Scheduler なら Lambda が要らないaws-sdk ターゲットで EC2 API を直接呼べる
  • ScheduleExpressionTimezone でタイムゾーンを指定できるので、cronのUTC換算が不要
  • IAMロールの Resource対象インスタンスのARNに絞る* にしない
  • 停止側は曜日を絞らず毎日にしておく。手で起動したままの消し忘れを拾える
  • FlexibleTimeWindowOFF。ずれると切り分けがしづらい
  • インスタンスIDの直書きは、作り直すと静かに動かなくなる
  • 停止しても EBSとEIPは課金される。消えるのは計算料金だけ
  • EIPが無いと起動のたびにIPが変わる。自動停止とEIPはセットで考える

一番の効果は金額そのものより、「止め忘れても翌日には止まっている」という状態を作れることだと思います。検証環境は作った直後がいちばん熱心に使われ、その後は放置されがちです。作ったその日にスケジュールも入れておくのが、結局いちばん安く済みます。

AWSを効率的に学習する方法

私が効率的にAWSを学習するために実施した方法は以下の通りです。
 ①最初に書籍(ハンズオンができる)を購入、座学でAWSの基礎を学習
 ②AWS資格試験を取得ための学習

※私の場合は①と②を合わせて2か月でソリューションアーキテクトを取得できました。

①AWS基礎学習

最初に購入した書籍は「Amazon Web Services 基礎からのネットワーク&サーバー構築」です。

Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂4版

この本では、AWSの基本サービスを利用したハンズオンを通じて、AWSの基礎を学習することができます。

また、タイトル通りAWSのネットワークやインフラに関しても網羅しているため、もともとインフラ系の技術者ではない人たちにとっても分かりやすい内容だと思います。

とりあえずAWS上にサーバーを設定して開発を行うための準備までするには最良の一冊です。

Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂4版

②AWS資格取得

AWSの基礎をある程度学習することができたら、次はAWS資格を取得しましょう。まずはAWSソリューションアーキテクトを目指しましょう。

資格勉強のための問題集をひたすら解きながら、AWSの知識を積み重ねて習得していきましょう!

苦行ではありますがこの問題集を3周ほどすればAWS用語や構成に関しても習得できているはずです。

AWS認定ソリューションアーキテクト-アソシエイト問題集

独学だと手が止まってしまう場合は、ササエル のようなインフラエンジニア向けのオンラインスクールを使う手もあります。ネットワークやサーバーの分野に絞ったカリキュラムなので、業務でインフラを触る人が学び直すのにも使えます

あわせて読みたい

コメント

タイトルとURLをコピーしました