RDS(Aurora)を夜中と休日は止めて、利用料を節約する

例えば、開発環境などは夜中や休日は使っていないことが多いと思います。
その時間DBを常に動かしているとお金の無駄になります。
Amazon Auroraでは、最大7日間DBインスタンスを停止することができます。

## RDS(Aurora)を止めるとどのくらい節約できるのか

> https://blog.manabusakai.com/2017/06/stopping-rds/
>> 1 週間のうち、土日と平日深夜から早朝（0 〜 8 時）に停止させると仮定すると、
>> 削減時間は (24 時間 * 2 日) + (8 時間 * 5 日) = 88 時間
>> 削減率は 88 時間 / (24 時間 * 7 日) = 0.5238 (52 %)

この記事によれば、平日深夜と休日に止めるだけで料金の半分を節約することができると書いてあります。

## どうやって止めるのか

今回は`Boto3(Python)/CloudWatch/Lambda`を使ってRDSを止めていきます。
止める対象のDBは１リージョンにあるDBすべてとなります。
実装はCloudFormationを使って作ります。

## 実装

### 前準備

事前に`lambda-function-stop-start-rds-<リージョン>-<アカウントID>`という名前でS3バケットを作成します。
※ ここで指定したリージョンのRDSをすべて止めることになります。

```Bash
aws s3 mb \
    s3://lambda-function-stop-start-rds-<リージョン>-<アカウントID> \
    --profile <プロファイル> --region <リージョン>
```

次に、以下のコードをZIPに圧縮して`stop-start-rds.zip`という名前で上記のS3へ置きます。

```Python:lambda_function.py
import boto3
import json

client = boto3.client('rds')

def lambda_handler(event, context):
    print(event)
    
    cloudWatchEventsArn = event['resources'][0]
    
    if 'start-rds-events' in cloudWatchEventsArn:
        start_db_cluster()
    elif 'stop-rds-events' in cloudWatchEventsArn:
        stop_db_cluster()

def stop_db_cluster():
    for DBClusterIdentifier in describe_db_clusters() :
        response = client.stop_db_cluster(
            DBClusterIdentifier = DBClusterIdentifier
        )

def start_db_cluster():
    for DBClusterIdentifier in describe_db_clusters() :
        response = client.start_db_cluster(
            DBClusterIdentifier = DBClusterIdentifier
        )

def describe_db_clusters():
    clusters = []
    response = client.describe_db_clusters()

    for DBCluster in response['DBClusters']:
        clusters.append(DBCluster['DBClusterIdentifier'])

    return clusters
```

### CloudFormationの実行

以下のymlファイルを作成します。

```Yaml:stop-start-rds.yml
---
AWSTemplateFormatVersion: '2010-09-09'
Description: 'CloudWatch Events -> Lambda -> RDS(Aurora)'
Parameters:
  FunctionName:
    Type: String
    Description: "Lambda Fuction Name"
    Default: "lambda-function-stop-start-rds"
  SourceFileName:
    Type: String
    Description: "Lambda Source FileName"
    Default: "stop-start-rds.zip"
Resources:
  IAMRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonRDSFullAccess
        - arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Path: /
      RoleName:
        Fn::Join:
          - '-'
          - - !Ref FunctionName
            - !Ref 'AWS::Region'

  LambdaFunction:
    DependsOn:
      - IAMRole
    Type: AWS::Lambda::Function
    Properties:
      Code:
        S3Bucket:
          Fn::Join:
            - '-'
            - - !Ref FunctionName
              - !Ref 'AWS::Region'
              - !Ref 'AWS::AccountId'
        S3Key: !Ref SourceFileName
      FunctionName: !Ref FunctionName
      Handler: lambda_function.lambda_handler
      MemorySize: 1024
      Role: !GetAtt IAMRole.Arn
      Runtime: python3.7
      Timeout: 60

  StopRdsEventRule:
    DependsOn:
      - LambdaFunction
    Type: "AWS::Events::Rule"
    Properties: 
      Name: "stop-rds-events"
      Description: !Ref FunctionName
      ScheduleExpression: "cron(0 16 ? * MON-FRI *)"
      State: "ENABLED"
      Targets: 
        - Arn: !GetAtt LambdaFunction.Arn
          Id: LambdaFunction
  StartRdsEventRule:
    DependsOn:
      - LambdaFunction
    Type: "AWS::Events::Rule"
    Properties: 
      Name: "start-rds-events"
      Description: !Ref FunctionName
      ScheduleExpression: "cron(0 22 ? * SUN-THU *)"
      State: "ENABLED"
      Targets: 
        - Arn: !GetAtt LambdaFunction.Arn
          Id: LambdaFunction
```

CloudFormationでスタックを作成します。
※ 最初に作成したS3と同じリージョンに作成してください。
※ stop-start-rds.ymlがあるディレクトリで実行してください。

```Bash
aws --profile "<プロファイル>" --region "<リージョン>" \
    cloudformation create-stack \
    --stack-name stop-start-rds \
    --template-body file://stop-start-rds.yml \
    --capabilities CAPABILITY_NAMED_IAM
```

CloudFormationのコンソールでスタックが作成されれば完了です。

## 所感

実際にいくら安くなっているのかは一ヶ月経たないとわかりませんが、
RDSのインスタンス自体はCloudWatchメトリクスを見ると止まっているのが確認できました。
意外と簡単に作れるのでおすすめです。

### どうでもいいこと

最近のおすすめの曲：https://www.youtube.com/watch?v=BuP1R6MKyns

皆さん、良いお年を〜