AWS Aurora Serverlessを使ってみた感想

# Aurora Serverlessの説明
一言でいうと自動でスケールして、アクセスがなければ勝手に停止するAWSのMySQL/PostgreSQLインフラです
2018年に一般公開された比較的新しいサービスで2020年になってもアップデートが続いています

## Aurora Serverlessの利用用途
こちらの記事をご覧になると概要がわかると思います
https://dev.classmethod.jp/articles/lessons-learned-from-up-and-running-aurora-serverless/

通常のAuroraとのコスト比較はこちらの記事にあります
https://dev.classmethod.jp/articles/calculate-amazon-aurora-serverless-costs/

たまにしか接続しない開発環境のDBや、アクセス傾向が不規則なワークロードに適しています
ただ、常時稼働させてしまうと同スペックのDBインスタンスに対して60%割高になるため、安定したアクセスが常時続くのであれば通常のAuroraのほうが適しています
また、停止状態からの稼働に60秒ほどかかるため、本番アクセスでは最低ACU(Aurora Capacity Unit)を１以上にしたいです
DBスナップショットを取ることでAurora Serverless/非Serverlessの移行やリージョン移行なども行えるため、柔軟に対応できます

## コスト
https://aws.amazon.com/jp/rds/aurora/pricing
ACUに対する課金が支配的です
1ACUあたりのコストはバージニア0.06\$/hourに対して東京0.10\$/hourと東京は66%割高です

東京からus-west-2へのレイテンシは100msくらいですので
https://dev.classmethod.jp/articles/hongkong-ec2-s3-rtt/
これが許容できるのであればus-west-2とかに置くのもありです
データ転送コストは0.09USD/GBなので、よほどの量でなければ大した額にならないはずです


# 利用方法
## Aurora Serverlessへのアクセス
mysql通信などの普通のDB通信の他にData APIという接続方法も利用できます
接続情報をSecret Managerに格納した上でIAM認証で接続するため接続周りがすっきりします
また、HTTPSエンドポイントから接続するためLambdaなどからでも利用しやすいです
Data APIを利用するにはコンソールから有効化する必要があります
(CloudFormationからはできなかった)
https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/data-api.html
https://speakerdeck.com/iwatatomoya/aurora-serverlessfalsedata-apinituite

Boto3からの利用としては、下記のようにexecute_statementにクエリを渡して実行します
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rds-data.html#RDSDataService.Client.execute_statement

DataAPI経由であれば異なるAWSアカウント間の通信もPrivateLinkが使えるそうです
https://dev.classmethod.jp/articles/rds-data-api-now-supports-aws-privatelink/

## Aurora Serverlessの管理
AWSコンソールからクエリを叩けるのですが、コンソールのタイムアウトが短く、内部でクエリは実行されるがコンソールから見るとタイムアウトになるというようなことが起こります
mysql通信などの普通のDB通信ができるので、ローカルマシンのDBクライアントから接続するのが良さそうです

私はSequel Proを使っています(後継のSequel Aceで接続できないパターンがある)
Session Manager経由で接続するとハマりどころがあったので書きました
https://qiita.com/elyunim26/items/d987827a7b6189815c3a

# 感想
停止状態からの稼働に60秒かかることが少しネックで、周知が必要だったり、アプリケーションコード内のリトライで対応したり、本番環境で停止しにくかったりします。
しかし特にPoCなどの開発段階では、使わなければ勝手に止まってコストを抑えてくれるのは頼りになります

min, maxのACU設定だけでスケールしてくれるというのは運用負荷が減り、またその気になればスケールできるという安心感があります
(同時に、スケールできてしまうためコストが上る可能性もあるということですが)

また(Serverlessに関わらず)Auroraの特徴としてDB容量は設定せずとも勝手に増えてくれます
この点も運用面で安心感があります
https://aws.amazon.com/jp/premiumsupport/knowledge-center/view-storage-aurora-cluster/

メリット・デメリットを見極めて使えば役に立ちそうです

# Cloud Formation サンプル
下記のようなテンプレートで作成できます

```
AWSTemplateFormatVersion: 2010-09-09
Description: Build AWS Batch environment

Parameters:
  VpcStackName:
    Type: String
  MiddleStackName:
    Type: String
  EnvType:
    Description: Environment type.
    Default: test
    Type: String
    AllowedValues:
      - prod
      - test
    ConstraintDescription: must specify prod or test.
  RdsDBClusterIdentifier:
    Description: DBClusterIdentifier
    Type: String
    AllowedPattern: ^[a-zA-Z0-9\-]*$
    MinLength: 1
    MaxLength: 60
  RdsDbMasterUserName:
    Description: RdsDbMasterUserName
    Type: String
  RdsDbMasterUserPassword:
    Description: RdsDbMasterUserPassword
    Type: String
    NoEcho: true
  MachineUserName:
    Type: String
  BucketName:
    Type: String

Conditions:
  IsProd: !Equals [ !Ref EnvType, prod ]

Resources:
  #####
  # IAM
  #####
  RDSDataApiUsePolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: RDSDataApiUsePolicy
      PolicyDocument:
        # https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/data-api.html
        Version: '2012-10-17'
        Statement:
          - Sid: SecretsManagerDbCredentialsAccess
            Effect: Allow
            Action:
              - secretsmanager:GetSecretValue
              - secretsmanager:PutResourcePolicy
              - secretsmanager:PutSecretValue
              - secretsmanager:DeleteSecret
              - secretsmanager:DescribeSecret
              - secretsmanager:TagResource
            Resource: arn:aws:secretsmanager:*:*:secret:rds-db-credentials/*
          - Sid: RDSDataServiceAccess
            Effect: Allow
            Action:
              - secretsmanager:CreateSecret
              - secretsmanager:ListSecrets
              - secretsmanager:GetRandomPassword
              - tag:GetResources
              - rds-data:BatchExecuteStatement
              - rds-data:BeginTransaction
              - rds-data:CommitTransaction
              - rds-data:ExecuteStatement
              - rds-data:RollbackTransaction
            Resource: "*"
      Users:
        - !Ref MachineUserName

  RdsS3WritableRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - rds.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        -
          PolicyName: "S3WritableForRds"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action: "s3:*"
                Resource: !Sub arn:aws:s3:::{$BucketName}

  #####
  # Subnet Group
  #####
  RdsDbSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription: Database subnets for RDS
      SubnetIds:
        Fn::Split:
          - ','
          # Suppose access from data api.
          # It can connect through iam auth from internet.
          - Fn::ImportValue: !Sub ${VpcStackName}-SubnetsPrivate

  #####
  # aurora
  #####
  RdsDbClusterParameterGroup:
    Type: AWS::RDS::DBClusterParameterGroup
    Properties:
      Description: CloudFormation Aurora Cluster Parameter Group
      Family: aurora5.6
      Parameters:
        server_audit_logging: 0
        time_zone: Asia/Tokyo
        # https://qiita.com/msykd/items/f6ef3f7c137c7778fa70
        character_set_client: utf8mb4
        character_set_connection: utf8mb4
        character_set_database: utf8mb4
        character_set_results: utf8mb4
        character_set_server: utf8mb4
        innodb_large_prefix: 1
        innodb_file_format: Barracuda
        innodb_file_per_table: 1
        # https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.Integrating.SaveIntoS3.html
        aurora_select_into_s3_role: !GetAtt RdsS3WritableRole.Arn
        aws_default_s3_role: !GetAtt RdsS3WritableRole.Arn

  RdsDbCluster:
    Type: AWS::RDS::DBCluster
    Properties:
      BackupRetentionPeriod: 7
      DBClusterIdentifier: !Ref 'RdsDBClusterIdentifier'
      DBClusterParameterGroupName: !Ref 'RdsDbClusterParameterGroup'
      DBSubnetGroupName: !Ref 'RdsDbSubnetGroup'
      DeletionProtection: false
      Engine: aurora
      EngineMode: serverless
      EngineVersion: 5.6.10a
      MasterUsername: !Ref 'RdsDbMasterUserName'
      MasterUserPassword: !Ref 'RdsDbMasterUserPassword'
      Port: 3306
      PreferredBackupWindow: 15:25-15:55
      PreferredMaintenanceWindow: Tue:16:05-Tue:16:35
      ScalingConfiguration:
        MinCapacity: 1
        AutoPause: true
        MaxCapacity: 4
        SecondsUntilAutoPause: 1800
      StorageEncrypted: true
      VpcSecurityGroupIds:
        - !Ref RdsSecurityGroup
  RdsSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId:
        Fn::ImportValue: !Sub ${VpcStackName}-VPC
      GroupDescription: Allow MySQL (TCP3306)
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-mysql

Outputs:
  RdsDbClusterEndpoint:
    Value: !GetAtt 'RdsDbCluster.Endpoint.Address'

```