AWS RDS自動停止の方法

#1. はじめに

夜間や土日などの業務時間外に開発・検証用のRDSを起動しっぱなしにすると、無駄な利用料金がかかってしまいます。そのため、夜間や土日などの業務時間外に開発・検証用のRDSを停止しておきたいという取り組みはAWS環境を使うプロジェクトではほとんど求めていると思います。

夜間や土日などの業務時間外に開発・検証用のRDSを停止するために、利用者は退社する前に手動でRDSを停止する必要があります。万が一、退社前に停止作業を忘れる場合、RDSは夜間や土日に起動し続けるため、無駄なコストが発生します。また、RDSは一度停止しても7日間がたったら、自動的に再起動する仕様となっているので、利用者は7日間経過したかどうかという監視もする必要があります。

[AWSドキュメント(RDS)-ユーザガイド](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/USER_StopInstance.html)による抜粋

>DB インスタンスは最大 7 日間停止できます。7 日後に DB インスタンスを手動で起動しなかった場合、DB インスタンスは自動的に起動される

このように、手動でRDS停止したり、7日間経過したかどうかを監視したりなどでRDS利用者は停止するための運用負担が増えてしまいます。RDS利用者に運用負担をかけずに、自動的にRDSを停止する方法を紹介したいと思います。

#2. RDS自動停止の目的と方法

今回RDS自動停止は、夜間や土日の業務時間外などでRDSを使用していないときに、自動的に停止することでAWSの費用削減を目的とします。例えば、あるプロジェクトにおいて、22時以降の勤務は禁止というルールがあります。それに合わせて、毎日22時に起動中のRDSを自動的に停止させます。RDS自動停止の方法として、主に以下2通りあると考えられます。3章以降、それぞれの実装方法について説明します。

①EventBridgeルール+Lambda関数
②EventBridgeルール+Systems Managerオートメーション

#3. EventBridgeルール+Lambda関数

3章でEventBridgeルール+Lambda関数を使ってRDSを自動的に停止する方法を紹介します。例えば、毎日22時に起動中のRDSを自動的に停止する機能を実現することを考える際に、EventBridgeルールとLambda関数のそれぞれの役割は以下となります。
EventBridgeルール：毎日22時にLambda関数を実行します。
Lambda関数：停止したいRDSを停止させます。

###3.1 Lambda関数のロール作成

Lambda関数は該当RDSを停止させる必要があるため、Lambda関数にRDSを停止させる権限を付与する必要があります。また、Lambda関数はCloudWatch Logsに実行ログを出力するのにCloudWatch Logsへのアクセス権限が必要となります。

![role-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/19f30d0b-ecc0-c297-ad6f-0b4dd45c0a6d.png)

![role-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/3a088e8b-b4f7-19ef-d734-3c86905d86a4.png)

![role-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/b361a438-9848-4616-07b7-e588dfe0ecc1.png)

ポリシーフィルタを使って下記2ポリシーをアタッチします。
・AmazonRDSFullAccess
・AWSLambdaBasicExecutionRole
今回テスト用でRDSのFull権限を与えています。必要に応じて権限の縮小をご検討ください。

![role-41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/088acddd-7ecc-6f2f-7f33-32ceb14e753a.png)
任意のロール名を入力してロールを作成します。

###3.2 Lambda関数作成
Lambda関数を作成します。Lambda関数の機能として、Lambda関数の実行リージョン内にあるすべてのRDSに対して、「3.4 停止対象RDSにタグの設定」で説明するタグ設定があるRDSを停止対象と判断して停止を実行します。

![lam-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/93acacb4-2bf0-c9a7-6dcf-bcd984c2554c.png)

![lam-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/cf3a8a1d-7e75-0a19-35ba-381b3db0bad4.png)
任意の関数を入力します。

![lam-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/6dbdbb44-34dc-6ba8-84f3-52149e2dbcfc.png)
ランタイムにPython 3.9を選択します。

![lam-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/0868eeac-e8df-8457-6c35-55fb573bbb6b.png)
既存のロールに「3.1 Lambda関数のロール作成」で作成したロールを選択します。

![lam-5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/507a7640-422c-7ccc-274d-82a04dbbec59.png)
デフォルトで作成されたLambda関数を下記に置き換えます。

```python
import boto3
import os

#環境変数 
client = boto3.client('rds')
TAGKEY = os.environ['TAGKEY']
TAGVALUE = os.environ['TAGVALUE']

def lambda_handler(event, context):
    #クラスターを停止
    clusters = client.describe_db_clusters()
    for cluster in clusters['DBClusters']:
        if cluster['Status'] == "available":
            tags = client.list_tags_for_resource(ResourceName=cluster['DBClusterArn'])
            for tag in tags['TagList']:
                if tag['Key'] == TAGKEY and tag['Value'] == TAGVALUE:
                    client.stop_db_cluster(DBClusterIdentifier=cluster['DBClusterIdentifier'])
    
    #インスタンスを停止
    instances = client.describe_db_instances()
    for instance in instances['DBInstances']:
        if instance['DBInstanceStatus'] == "available":
            tags = client.list_tags_for_resource(ResourceName=instance['DBInstanceArn'])
            for tag in tags['TagList']:
                if tag['Key'] == TAGKEY and tag['Value'] == TAGVALUE:
                    client.stop_db_instance(DBInstanceIdentifier=instance['DBInstanceIdentifier'])

```
Lambda関数の中で、RDS DBのタグをRDS DBのタグを使用して、停止の対象となるかどうかを判断します。本プログラムでは、下記のタグを使用する場合、RDS DBの停止対象となります。

|キー  |値  |
|---|---|
|AUTO_STOP |true |

また、Boto3ライブラリ関数を使用してRDS DBを停止します。RDS DBはクラスターかインスタンスによって、呼び出すBoto3ライブラリ関数が異なります。Boto3ライブラリ関数の詳細インターフェイスについて、下記リンクをご参照ください。

[stop_db_cluster：Auroraクラスター停止用のBoto3ライブラリ関数](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rds.html#RDS.Client.stop_db_cluster)
[stop_db_instance：RDS DBインスタンス停止用のBoto3ライブラリ関数](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rds.html#RDS.Client.stop_db_instance)

###3.3 環境変数の設定
RDSの停止対象はタグで判断しますので、そのタグを環境変数に設定します。
![03_evi-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/0153c895-9162-1eae-4bc9-0a456d31deec.png)

![03_evi-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/5a352f5f-67c5-ae2d-74aa-ce081dc6cfbf.png)

![03_evi-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/1301c200-b8b8-cb11-64e3-f0b49ba78f93.png)

![03_evi-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/e0bfbbd5-2105-b76c-b571-9dc2426252e4.png)

下記2個の環境変数を設定します。

|キー  |値  |
|---|---|
|TAGKEY  |AUTO_STOP  |
|TAGVALUE  |true  |

###3.4 停止対象RDSにタグの設定
停止対象のRDSに対して、以下のようなタグを設定します。
![03_evi-5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/96f7ae6b-8b40-5c6b-e3ec-fd5a86b868b8.png)

![03_evi-6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/72b48eb9-c11c-0892-446b-aa24d9ced5c1.png)


###3.5 EventBridgeルールの設定
EventBridgeルールを使用して、定期時刻になったらLambda関数を起動しますので、本節ではその設定方法を説明します。
![04_clwe-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/901f6249-5715-9da5-8d95-6085aff07a91.png)

![04_clwe-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/29595455-cef2-ea63-fbfc-fadc1e65be95.png)

![04_clwe-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/db097a74-3cea-288c-067e-881e44f63d6f.png)

![04_clwe-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/5898b8d0-19a8-2e76-03bf-5bf59afb8bb2.png)

この例の設定では、毎日22:00(JST)にLambda関数が起動します。


#4. EventBridgeルール+Systems Managerオートメーション
3章でEventBridgeルール+Lambda関数を使って定期的にRDSを自動的に停止する方法を紹介しました。しかし、Lambda関数は運用者側で一からプログラミングする必要があるデメリットがあります。プログラミングしないという観点に、本章の残りSystems Managerオートメーションという機能を利用して、定期的にRDSを自動的に停止する方法を紹介します。

EventBridgeルール：定期的にSystems Managerオートメーションを実行します。
Systems Managerオートメーション：停止対象のRDSを停止します。

###4.1 ロール作成

EventBridgeルールでSystems Managerオートメーションを実行するのに、Systems Managerオートメーションを呼び出す権限が必要です。また、特定RDSを停止するため、該当RDS停止の権限も必要となります。
![ssm-role-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/6af97ea4-5c31-fedf-afe6-d160fa8e24b1.png)

![ssm-role-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/6ea03747-d82a-4c2b-f763-4eb57f96f996.png)

![ssm-role-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/635867b3-9e9a-9114-cc98-862810f3727a.png)

![ssm-role-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/84208c58-9eb3-93e4-828c-5cc18b850ac9.png)

![ssm-role-5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/9890e217-9f11-25ef-24df-fae8f5f1d44c.png)

![ssm-role-6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/2c25682e-a5f4-c869-b6ba-ee111249024c.png)

デフォルトで生成されたJSON形式のポリシーを下記のものに置き換えます。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "ssm:StartAutomationExecution",
            "Effect": "Allow",
            "Resource": [
                "arn:aws:ssm:{リージョン名}:*:automation-definition/AWS-StartStopAuroraCluster:$DEFAULT",
                "arn:aws:ssm:{リージョン名}::automation-definition/AWS-StopRdsInstance:$DEFAULT"
            ]
        },
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "rds:StopDBCluster",
                "rds:StopDBInstance",
                "rds:DescribeDBClusters"
            ],
            "Resource": [
                "arn:aws:rds:*:{AWSアカウントID}:cluster:*",
                "arn:aws:rds:*:{AWSアカウントID}:db:*"
            ]
        }
    ]
}
```
{リージョン名}：EventBridgeルールの実行リージョン名を入力します。
　　　　　　　　※東京リージョンの場合：ap-northeast-1
{AWSアカウントID}：AWSアカウントIDを入力します。

![ssm-role-7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/d5ba89cd-a0bf-c4c9-b5be-ca5b2c28f38f.png)

![ssm-role-8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/311f7896-3d6a-06ed-1040-c8b9fa0ff0f1.png)

![ssm-role-9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/0f9aa497-ed2d-3097-6471-38464abbbc4e.png)

![ssm-role-10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/0efac929-2def-5dbf-be97-83a5f5eafa00.png)

![ssm-role-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/61ffc3a7-5e2b-3366-c720-c43f3a2d714f.png)
先ほど作成したポリシーを選択します。

![ssm-role-12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/b04b7fc9-2fd0-c5f3-eb39-025dcb2b95cc.png)

![ssm-role-13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/f8a02c09-9dcb-ef59-962c-ad6192aca2d7.png)
ロール名を入力してロールを作成します。

![ssm-role-14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/91bf1a70-bada-360f-5bb8-a67cac48646f.png)

![ssm-role-15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/953d639a-7bea-2701-4f84-57104a540435.png)
ポリシーを下記のものに変更します。

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "events.amazonaws.com",
          "ssm.amazonaws.com",
          "rds.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```

###4.2 EventBridgeルールの作成
AWSでは、EventBrigdeルールを使用してSystems Managerオートメーションを呼び出す機能があります。本節でその設定方法を説明します。
![ssm-rule-001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/77247908-a330-eb27-1b6f-5507ede7a8d3.png)

![ssm-rule-002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/fcfcdae7-96ce-3db4-9d82-c64b61fa4e83.png)
任意のEventBridgeルール名を入力します。

![ssm-rule-003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/727e5a0a-c9ff-2044-107b-a59a747c7d43.png)
定期的にRDSを停止するスケジュールを設定します。この例の設定では、毎日22:00(JST)にRDSを停止します。

![ssm-rule-004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/e4972d36-a472-6e44-f587-8e3d98eb0096.png)

![ssm-rule-005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/916e2fbe-ff66-2287-78e3-ebf9a9425cfb.png)

|設定項目  |設定値  |
|---|---|
|ターゲット  |Systems Managerオートメーション |
|ドキュメント  |AWS-StartStopAuroraCluster(Auroraクラスターの場合) (※1)|
|ClusterName  |DB識別子(※2) |
|Action  |Stop  |
|既存のロールの使用  |前項で作成したロールを選択  |

(※1) RDSインスタンスの場合、「AWS-StopRdsInstance」を使用します。
(※2) 複数個のクラスターを指定することができません。複数個のクラスターを停止したい場合、ターゲットを増やして対応可能です。ただし、一つのEventBridgeルールに最大5個のターゲットまでしか作成できません。5個以上のターゲットを作成したい場合、EventBridgeルールを増やすか、3章でご紹介したLambda関数の使用をご検討ください。

[AWSドキュメント(Amazon EventBridge)-ユーザガイド](https://docs.aws.amazon.com/ja_jp/eventbridge/latest/userguide/eb-targets.html)による抜粋
>ルールごとに最大 5 つのターゲットを定義できます。

![ssm-rule-006.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2237449/08a0606d-5a3d-a762-c4e1-56f4aa0a1e42.png)
任意のタグ名を入力してルールを作成します。上図はタグなしの設定例となります。

#5. おわりに

本記事では、以下2通りのRDS自動停止の方法を紹介しました。
①EventBridgeルール+Lambda関数
②EventBridgeルール+Systems Managerオートメーション

①は運用者自身がLambda関数を作成する必要がありますが、②のような1ルールあたりに5個のターゲットしか停止できないという制限がありません。また、Lambda関数の中身は運用者が自由にコーディングできるため、他の機能を追加することも可能です。このようなメリットがあるため、①は、多くのRDSを管理する必要があるAWS環境の管理者にとってうれしい停止方法だと思います。

②は1ルールあたりに5個のターゲットしか停止できないという制限があり、5個以上停止したい場合、ルールやターゲットを増加しなければならない手間はかかりますが、Systems ManagerオートメーションはAWSが用意している機能ですので、運用者側は一からコーディングする必要がありません。AWSコンソール画面でメニュー選択やボタンクリックなどで機能を実現することが可能ですので、個人で特定のRDS管理に便利な機能だと思います。

このように、①と②を場合分けて使用することをお勧めいたします。本記事で紹介したRDS自動停止の方法を利用して、AWSコスト削減に役に立ていただければ幸いです。