インスタンスの自動起動停止をする Instance Scheduler を触ってみた

# Instance Scheduler とは

AWS を使っていると、コストを削減するために、定期的に EC2 インスタンスや RDS インスタンスを停止したいときがあります。CloudWatch Event + Lambda を使って独自にプログラムを作っても良いですが、実装するのがめんどくさいです。そういったときに、AWS が提供している Instance Scheduler が便利に使えます。AWS 公式で提供されているソリューションになっていて、多機能で安心感があります。

Instance Scheduler は、AWS のマネージドサービスではなく、いくつかのマネージドサービスを組み合わせて提供されているソリューションです。CloudFormation のテンプレートが提供されており、簡単に Deploy が出来て、自動的に起動停止をしてくれます。

こちらのURL で公開されています。
https://aws.amazon.com/jp/solutions/implementations/instance-scheduler/



アーキテクチャの概要はこんなかんじです。 ([引用](https://d1.awsstatic.com/Solutions/ja_JP/instance-scheduler.pdf))

![1609896963412.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/0bcb6c5e-e991-5c08-9ed4-05c31c818ba3.png)





EventBridge (画像では CloudWatch) が定期的に Lambda を起動して、その Lambda が DynamoDB に格納されている情報を読み込んで、EC2 インスタンスと RDS インスタンスの自動停止・起動を行う構成です。対象のインスタンスは、特定の Tag を付与して起動停止の対象と判断しています。

それでは、Instance Scheduler の Deploy 方法を備忘録としてメモしておきます。



# Instance Scheduler Stack の起動

次の URL にアクセスします。
https://aws.amazon.com/jp/solutions/implementations/instance-scheduler/

画面の中から、AWS コンソールで起動する を選択します。

![1609898100657.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/05bc5bf3-b998-b824-8323-f7cc911d5aaa.png)



すると、AWS のマネージドコンソールで、Instance Scheduler を CloudFormation で Deploy する画面に移ります。対象の Region を確認したあとで、Next を押します。
ちなみに、ここで View in Designer を押すと視覚的にわかりやすく CloudFormation で出来上がるリソースが確認できます。

![1609908769983.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/c4f5eb5a-b13e-8df7-4bc3-1112d0ef91eb.png)



各種パラメータを入れて Next を入れます。パラメータの詳細は[こちら](https://d1.awsstatic.com/Solutions/ja_JP/instance-scheduler.pdf) のドキュメントに乗っています。
この記事の場合は、5分おきに Lambda を起動するパラメータをいれています。

![1609909405411.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/5cd5f8ec-5680-0e02-f382-e35498abaca6.png)



確認画面で IAM リソースの作成を許すチェックを入れて Create stack を押します。

![1609909506503.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/57e001d3-71c7-93dc-6803-2818e6cab423.png)



作成中になります。約5分ほど待つと作成完了になりました。

![1609909592523.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/de5623c3-e70c-a498-f890-61eb35312fe8.png)



CloudFormation によって、作成された Resources はこれらです

- Lambda Function
- DynamoDB Table
- IAM Policy
- KMS Key
- KMS Alias
- SNS Topic
- EventBridge Rule

![1609911338898.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/8f080dde-ffd2-f820-fe12-eaeaf878ffac.png)



# Scheduler CLI Install

Instance Scheduler では、いつどのようにインスタンスを起動停止するかを Dynamo DB でデータを格納しています。DynamoDB データに紐づくインスタンスを、自動的に起動・停止をしてくれます。
DynamoDB には、次の2つの重要なデータが格納されています

- スケジュール(Schedule) : インスタンス起動停止に関係するスケジュールを指定。1つ以上の期間(Period)が含まれている。
- 期間(Period) : いつからいつまでを起動するのか期間を定義

紐づくインスタンスとの関係性はこんな感じです。
インスタンス → Schedule → Period
インスタンスは、Schedule と1対1で紐づき、Schedule は Period と、1対多で紐づきます。

詳細はこちらに書かれています。
https://d1.awsstatic.com/Solutions/ja_JP/instance-scheduler.pdf



Schedule と Period は独自にカスタマイズが出来ます。DynamoDB のデータを直接編集することもできますが、Scheduler CLI を使うのが便利です。次のコマンドで Scheduler CLI をダウンロードします。 

```
wget https://s3.amazonaws.com/solutions-reference/aws-instance-scheduler/latest/scheduler-cli.zip
unzip scheduler-cli.zip
```



Scheduler CLI を Install

```
sudo python3 setup.py install
```



実行例

```
> sudo python3 setup.py install
/usr/lib/python3/dist-packages/setuptools/dist.py:473: UserWarning: Normalizing 'v1.2.0' to '1.2.0'
  warnings.warn(
running install
running bdist_egg
running egg_info
creating scheduler_cli.egg-info

省略

Using /usr/lib/python3/dist-packages
Finished processing dependencies for scheduler-cli==1.2.0
>
```



Scheduler CLI の 動作検証 をしていきます。
DynamoDB の ConfigTable に保存されている Period 一覧を取得するコマンドです。

```
scheduler-cli describe-periods --stack InstanceScheduler
```



デフォルトで格納されている Period を JSON で取得できます。

```
> scheduler-cli describe-periods --stack InstanceScheduler
{
   "Periods": [
      {
         "Months": [
            "jan/3"
         ],
         "Description": "Every first monday of each quarter",
         "Weekdays": [
            "mon#1"
         ],
         "Name": "first-monday-in-quarter",
         "Type": "period"
      },
      {
         "Begintime": "09:00",
         "Description": "Office hours",
         "Endtime": "17:00",
         "Weekdays": [
            "mon-fri"
         ],
         "Name": "office-hours",
         "Type": "period"
      },
      {
         "Description": "Days in weekend",
         "Weekdays": [
            "sat-sun"
         ],
         "Name": "weekends",
         "Type": "period"
      },
      {
         "Description": "Working days",
         "Weekdays": [
            "mon-fri"
         ],
         "Name": "working-days",
         "Type": "period"
      }
   ]
}
```



同様に、Schedule  の一覧を取得します

```
scheduler-cli describe-schedules --stack InstanceScheduler
```



実行例

```
> scheduler-cli describe-schedules --stack InstanceScheduler
{
   "Schedules": [
      {
         "Description": "Instances running",
         "Name": "running",
         "UseMetrics": false,
         "Type": "schedule",
         "OverrideStatus": "running"
      },
      {
         "Timezone": "UTC",
         "Description": "Vertical scaling on weekdays, based on UTC time",
         "Periods": [
            "weekends@t2.nano",
            "working-days@t2.micro"
         ],
         "Name": "scale-up-down",
         "Type": "schedule"
      },
      {
         "Timezone": "US/Pacific",
         "Description": "Office hours in Seattle (Pacific)",
         "Periods": [
            "office-hours"
         ],
         "Name": "seattle-office-hours",
         "Type": "schedule"
      },
      {
         "Description": "Instances stopped",
         "Name": "stopped",
         "UseMetrics": false,
         "Type": "schedule",
         "OverrideStatus": "stopped"
      },
      {
         "Timezone": "Europe/London",
         "Description": "Office hours in UK",
         "Periods": [
            "office-hours"
         ],
         "Name": "uk-office-hours",
         "Type": "schedule"
      }
   ]
}
```



# Period の設定

Scheduler CLI が正常に動作したので、Period を独自に設定していきます。今回は、深夜1時に起動しているインスタンスを自動的に停止したいので、次のコマンドで `LateNightStop` という名前の Period を作成します。

```
scheduler-cli create-period \
--name "LateNightStop" \
--description "Stop Instance at 01:00 every day" \
--endtime "01:00" \
--stack InstanceScheduler
```



実行例

```
> scheduler-cli create-period \
  --name "LateNightStop" \
  --description "Stop Instance at 01:00 every day" \
  --endtime "01:00" \
  --stack InstanceScheduler
{
   "Period": {
      "Description": "Stop Instance at 01:00 every day",
      "Endtime": "01:00",
      "Name": "LateNightStop",
      "Type": "period"
   }
}
```



DynamoDB Table に、作成した `LateNightStop` Item が生成されています。

![1609931770150.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/90a2c913-5981-72f6-ac48-a4343ae29a8e.png)



# Schedule の設定

次に、作成した Period を使った `LateNightStopSchedule` という名前の Schedule を作成します。

```
scheduler-cli create-schedule \
--name "LateNightStopSchedule" \
--description "Stop Instance at 01:00 every day" \
--periods "LateNightStop" \
--timezone "Asia/Tokyo" \
--stack InstanceScheduler
```



実行例

```
> scheduler-cli create-schedule \
  --name "LateNightStopSchedule" \
  --description "Stop Instance at 01:00 every day" \
  --periods "LateNightStop" \
  --timezone "Asia/Tokyo" \
  --stack InstanceScheduler
{
   "Schedule": {
      "Description": "Stop Instance at 01:00 every day",
      "Timezone": "Asia/Tokyo",
      "Name": "LateNightStopSchedule",
      "Periods": [
         "LateNightStop"
      ],
      "StopNewInstances": true,
      "UseMaintenanceWindow": false,
      "RetainRunning": false,
      "Enforced": false,
      "Hibernate": false,
      "UseMetrics": false,
      "Type": "schedule"
   }
}
```



DynamoDB Table に、作成した `LateNightStopSchedule` Item が生成されています。

![1609932344511.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/71806184-ae0f-bfdf-6b8e-65a26fb428df.png)



# インスタンスにタグ付け

EC2 インスタンスに 特定の Tag をつけると自動起動・停止が動作します。TagName=Shedule名の書式で Tag を設定すればよいです。
この記事の場合は、TagName を `Schedule` としているので、`Schedule=LateNightStopSchedule` という形式で Tag を生成します。

TagName は Stack の Parameters を表示すれば確認できます。

![1609932888826.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/aced4bff-c532-9ac1-4dd0-d98279ce6503.png)



ちなみに、この指定は Lambda Function の Environment で使われています。

![1609932949742.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/12a3742a-aeff-7d60-b188-c3015c29bb5a.png)



それでは、EC2 Instance に Tag を付与しました。これで自動的に停止されます。

![1609933060300.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/f847cb89-c628-0fcb-a055-89e1e19fc02b.png)



# CloudWatch Logs

Instance Scheduler 配下の Lambda の Log は、CloudWatch Logs に出力されています。

![1609940172949.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/0998a101-51dc-3ce1-76eb-760b5b7acaee.png)



また、エラーが発生した際には、自動生成された SNS にサブスクライブ先を追加することで、エラーメッセージを受けることも出来ます。必要そうな場合は手動で追加をしておくのもよいです。





# 付録 : CloudFormation で作成されたリソースの確認

## Event Bridge

Rule 

![1609911427501.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/c7ae84f9-3c27-797c-5878-6bb956f2568a.png)



5分おきに、Lambda を Trigger している

![1609911465383.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/11265299-4ed2-9b59-ebdf-95a93cc5e870.png)



## Lambda

Function が作成されている

![1609911589315.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/a0d908b3-271e-fceb-a3c9-aa08f3fddbaa.png)



実際のソースコードや環境変数などが確認可能

![1609911721351.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/4d687084-fbe0-e2c7-7521-099dcc4cff96.png)



## DynamoDB

DynamoDB Table が3つ作成されている

![1609911779826.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/439dc181-f0f6-12cb-2ebd-6c783c5cfc9d.png)



ConfigTable には、スケジュールや期間のテンプレート設定が初めから入っている

![1609911876583.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/c40127e8-e6bd-2c83-5429-05ca63e1a56a.png)



StateTable や MantenainceWindowTable の中身は空っぽ



## SNS

Topic が1個作成されている

![1609912050993.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/ca6f8764-7ccb-597a-d306-90b45ae3c674.png)



Subscription は空なので、手動でメール通知したいアドレスを入れる

![1609912074014.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/566f037a-d509-051b-2cfe-fbf6c8dfb28a.png)



## KMS

Customer managed key が作成されている

![1609912148110.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/a8fdef91-57fb-ec8a-564e-02569531b027.png)



Alias name もこんなかんじ

![1609912488867.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/0326efe1-c7ed-edba-4935-ee26eea899e0.png)





# 参考URL

AWS Instance Scheduler
https://aws.amazon.com/jp/solutions/implementations/instance-scheduler/

AWS 実装ガイド
https://d1.awsstatic.com/Solutions/ja_JP/instance-scheduler.pdf

https://aws.amazon.com/jp/premiumsupport/knowledge-center/stop-start-instance-scheduler/

ソースコード
https://github.com/awslabs/aws-instance-scheduler