- 1. 解説動画
- 2. 概要
- RDS (Relational Database Service)
- 構成図
- リソース一覧
- 命名規則
- 公式URL
- 3. 解説手順
- 4. CFnテンプレート
- RDS, Secrets Manager
- 5. 予習
- 6. FAQ(随時追加)
AWSでRDB (Relational Database) を使うときはRDS (Relational Database Service) が便利です。RDSについて学んでいきましょう!
ついでにDBのパスワードを保存するためにSecrets Managerも使ってみます!
1. 解説動画
※動画の画質が悪い場合は、歯車から720p以上の画質を選択ください
2. 概要
RDS (Relational Database Service)
【マネージドRDBサービス】
RDSはAWSのマネージドRDBサービスですが、「マネージド」とは何でしょう?
簡単に言えば、AWSに面倒な「管理」をお任せできるということです。例えばオンプレミス(物理的なサーバーの自社運用)の時代にRDBを構築する場合、データセンター(サーバーを置く建物)・インターネット回線・ネットワーク機器・配線・サーバー・OS・RDBについて、それぞれ選定や構築が必須でした。さらにRDBを運用するためにスケーリング(スペックやストレージの増加)・バックアップなどを考慮した、設計や構築も必要でした。
それらをAWSが「管理」してくれることによって、エンジニアは物理的なこと・OSのことを一切忘れて、RDBの設定値のみ考えてボタンをポチッとするだけで良くなりました。
今後も「マネージド」という言葉が出てきたら、「必要な設定値だけ設定した後は面倒なところを全部お任せできる」というイメージを持って頂ければOKです!
【Amazon Aurora】
良く聞くオープンソースのRDBはMySQL(マイエスキューエル)とPostgreSQL(ポスグレースキューエル。通称 ポスグレ)ですね。今回はMySQLの方で勉強していきます。
RDSではMySQLを選択することもできますが、クラウド向けに最適化されたAmazon Auroraを選択することをオススメします。公式ではMySQLと比べて5倍高速と謳われています。また自動的に3つのAvailability Zoneに6個のデータがレプリケーション(レプリカ=複製 を作ること)され、障害にも強く、復旧も迅速です。
Amazon Auroraのすごさは公式にも非公式にも様々な情報がありますので、ググってみてください。
AuroraのDBクラスター概念図は次の通りです。
- 全体がAurora DBクラスター
- Availability Zone(正確にはサブネットグループで指定されたサブネット)内にDBインスタンス
- DBインスタンスのうち、1つがPrimaryインスタンス (Writer)、残りがAuroraレプリカ (Reader) ※ただしAuroraマルチマスタークラスター機能を使うと、全てのインスタンスが読み書きできる(=PrimaryインスタンスとAuroraレプリカの区別がなくなる)
- 本番環境の場合、2個以上のDBインスタンスを作ることが推奨されている。※Primaryインスタンスが障害で停止した時に、Auroraレプリカにフェイルオーバーすることで、短時間(1分程度)でDBを復旧させるため。フェイルオーバーするとAuroraレプリカがPrimaryインスタンスに昇格することになる
- 書き込みをするために使うクラスターエンドポイント、読み取り専用のリーダーエンドポイントがデフォルトで用意される(エンドポイント=DB Hostname=DBの場所を示すドメイン)
- データは3つのAvailability Zoneに、合計で6つコピーされる。 ※インスタンスをどのAvailability Zoneに作るかは関係ない。S3に置かれるが、画面上では一切見えず、ストレージの設定も不要
【Secrets Manager】
Secrets ManagerではDBの認証情報(ユーザー名やパスワード)、APIキー、OAuthトークンなど様々な秘密情報(=シークレット)を安全に保管できるサービスです。AWSのリソース(例えばECS)で使う場合はIAM Roleで権限を付けると、その認証情報を取得できます。
ちなみに手で作る場合はDBパスワードを自ら入力する必要がありますが、CFnの場合は指定した条件を満たす乱数で作ってくれる点が素晴らしいです。またLambda関数を組み合わせればパスワードの定期的なローテーションも可能です。
今回CFnでは
- SecretForRDS: マスターユーザー名(root)・パスワード etc.
- SecretForRDSAwsmaster: 後で作るRailsアプリ用のDB名・ユーザー名・パスワード etc.
以上2つのシークレットを作成します。
構成図
RDS Aurora DB Clusterのうち、DB Instance(Master)をPrivate Subnet Aに作ります(=Private Subnetはインターネットと一切通信ができないため安全です)。Private Subnet CやDにもDB Instance(Read Replica)を作れるテンプレートにしていますが、RDBは料金が結構高いのでデフォルトでは作らないように書いています。
今回作成するRDSの構成図はこちらです。 ※小さくて見づらいので、右クリックして新しいタブで開いてください。
リソース一覧
リソース | 目的 | 備考 |
---|---|---|
SecretsManager: Secret | DBの認証情報「シークレット」の安全な保管 | DBのマスターユーザー(管理者)と、後で作るRailsアプリ用のDB名・ユーザー名、2つの認証情報を保管する |
EC2: SecurityGroup | RDSの通信を許可する「セキュリティグループ」 | ここではセキュリティグループを作ってRDSに付けるだけで、許可ルールはEC2やECSで必要になった時に追加する |
RDS: DBClusterParameterGroup | DBクラスター内の全てのDBインスタンスに適用される「DBクラスターパラメータグループ」 | 最初はデフォルトのままで良いが、タイムアウト値やスロークエリの出力など、DB利用者のニーズにあわせて調整していく。変更を適用するにはパラメータごとに、DBの再起動が必要・不要とあるので注意 |
RDS: DBParameterGroup | 特定のDBインスタンスに適用される「DBパラメータグループ」 | 同上。ただしDBクラスターパラメータグループの値とDBパラメータグループの値が異なる場合、後者が優先されるので注意 |
RDS: DBSubnetGroup | DBインスタンスを作成するサブネットを指定する「DBサブネットグループ」 | 2つ以上のAvailability Zoneと、それぞれに1つ以上のサブネットを選択して作成する。今回はPrivateSubnetA, C, Dを選択する |
RDS: DBCluster | DBインスタンスを取りまとめる「DBクラスター」 | クラスターは今後も出てくるが、「同種の物の集団」という意味。RDSを外側から見るとDBクラスターが実態として見えている |
RDS: DBInstance | DBサーバーにあたる「DBインスタンス」 | インスタンスの言葉もEC2で出てきたが、「実体化されたもの」という意味。物理マシンの上で仮想的に起動されたもの。分かりづらいですよね...💦 「オブジェクト指向プログラミング」を理解すると、イメージがつきやすいので、機会があればぜひ概念だけでも勉強してください! |
SecretsManager: SecretTargetAttachment | 最初に作ったシークレットにDBクラスターを紐付ける | CFnで最初にシークレットを作った段階ではDBクラスターがまだ存在していない。DBクラスター作成後にシークレットと紐付けることで、シークレット内にDBクラスター名・ホスト名・ポート番号などの情報が追加される。 シークレットを手で作る場合は最初に作らず、DBクラスター作成後に「Secret type: Credentials for RDS database」を選んで作ってください |
命名規則
Classmethodさんの命名規則 https://dev.classmethod.jp/articles/aws-name-rule/ に準拠します。
公式URL
RDS
- Docs: Amazon RDS とは - Amazon Relational Database Service Docs: Amazon Aurora とは - Amazon Aurora
- CFn: RDS リソースタイプのリファレンス - AWS CloudFormation
- CLI: rds — AWS CLI v2 Command Reference
Secrets Manager
- Docs: AWS Secrets Manager とは - AWS Secrets Manager
- CFn: AWS Secrets Manager - AWS CloudFormation
- CLI: secretsmanager — AWS CLI v2 Command Reference
3. 解説手順
- CFnでRDS関連のリソースを作成する ※動作確認は単独ではできないので、ELBの後に作るEC2で実施します
- 使わない時はDBインスタンスを停止する。ただし7日間経つと勝手に起動する仕様なのでご注意ください。頻繁に停止起動を繰り返すと起動しなくなるのでご注意ください(数日経つと自然に復旧します) 参考: https://qiita.com/matsumoto_sp/items/4808576159c92f8916f5 RDS管理画面 > Databases > DBクラスターを選択 > (上にある) Actionsボタンをクリック > Stop をクリック > Stop databaseボタンをクリック
- Events メニューを確認する(DBの操作履歴や変更履歴が確認できる)
4. CFnテンプレート
RDS, Secrets Manager
Amazon Aurora(MySQL互換)を作成します。 DBのユーザー名やパスワードはSecrets ManagerのSecretに保存します。
- [SystemName]=
awsmaster
をお好きなシステム名に置換してください。変えなくてもOKです。
CFn Stack Name: [SystemName]-prod-rds S3 File Name: [SystemName]/rds.yml
---
### [Change System Name] awsmaster
## The following CFn stack must be created first in order to be referenced by the ImportValue function.
## 1. iam-role
## 2. ${SystemName}-${Environment}-vpc
AWSTemplateFormatVersion: "2010-09-09"
Description: Create RDS DB Cluster etc.
Mappings:
EnvironmentMap:
prod:
DBInstanceClass: db.t4g.medium
CreateDBInstanceC: false
CreateDBInstanceD: false
EnhancedMonitoring: false
BackupRetentionPeriod: 3
stg:
DBInstanceClass: db.t4g.medium
CreateDBInstanceC: false
CreateDBInstanceD: false
EnhancedMonitoring: false
BackupRetentionPeriod: 1
dev:
DBInstanceClass: db.t4g.medium
CreateDBInstanceC: false
CreateDBInstanceD: false
EnhancedMonitoring: false
BackupRetentionPeriod: 1
SystemNameMap:
awsmaster: ### [Change System Name]
Engine: aurora-mysql
ParameterGroupFamily: aurora-mysql8.0
EngineVersion: 8.0.mysql_aurora.3.05.2
PreferredBackupWindow: 16:15-16:45
PreferredMaintenanceWindowCluster: tue:16:45-tue:17:15
PreferredMaintenanceWindowInstanceA: tue:17:15-tue:17:45
PreferredMaintenanceWindowInstanceC: tue:17:45-tue:18:15
PreferredMaintenanceWindowInstanceD: tue:18:15-tue:18:45
Parameters:
SystemName:
Description: System Name
Type: String
Default: awsmaster ### [Change System Name]
Environment:
Description: Environment
Type: String
Default: prod
AllowedValues:
- prod
- stg
- dev
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: "Environment Configuration"
Parameters:
- SystemName
- Environment
Conditions:
ShouldCreateDBInstanceC: !Equals [ !FindInMap [ EnvironmentMap, !Ref Environment, CreateDBInstanceC ], true ]
ShouldCreateDBInstanceD: !Equals [ !FindInMap [ EnvironmentMap, !Ref Environment, CreateDBInstanceD ], true ]
EnabledEnhancedMonitoring: !Equals [ !FindInMap [ EnvironmentMap, !Ref Environment, EnhancedMonitoring ], true ]
Resources:
## Secrets Manager: Secret (root)
SecretForRDS:
Type: AWS::SecretsManager::Secret
Properties:
Description: !Sub Secret for RDS (Master user (root) of ${AWS::StackName})
GenerateSecretString:
SecretStringTemplate: '{"username": "root"}'
GenerateStringKey: password
ExcludePunctuation: true
PasswordLength: 32
## Secrets Manager: Secret (${SystemName})
SecretForRDSAwsmaster:
Type: AWS::SecretsManager::Secret
Properties:
Description: !Sub Secret for RDS (User ${SystemName} of ${AWS::StackName})
GenerateSecretString:
SecretStringTemplate: !Sub '{"database": "${SystemName}", "username": "${SystemName}"}'
GenerateStringKey: password
ExcludePunctuation: true
PasswordLength: 32
## EC2(VPC): Security Group
EC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub ${SystemName}-${Environment}-rds-sg
GroupDescription: !Sub ${SystemName}-${Environment}-rds-sg
VpcId:
Fn::ImportValue: !Sub ${SystemName}-${Environment}-vpc-VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Environment}-rds-sg
## RDS: DB Cluster Parameter Group
DBClusterParameterGroup:
Type: AWS::RDS::DBClusterParameterGroup
Properties:
Description: !Sub ${SystemName}-${Environment}-rds-cluster-pg
Family: !FindInMap [ SystemNameMap, !Ref SystemName, ParameterGroupFamily ]
Parameters:
require_secure_transport: "ON" ## SSL required
## RDS: DB Parameter Group
DBParameterGroup:
Type: AWS::RDS::DBParameterGroup
Properties:
Description: !Sub ${SystemName}-${Environment}-rds-pg
Family: !FindInMap [ SystemNameMap, !Ref SystemName, ParameterGroupFamily ]
## RDS: DB Subnet Group
DBSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupDescription: !Sub ${SystemName}-${Environment}-rds-subgrp
SubnetIds:
- Fn::ImportValue: !Sub ${SystemName}-${Environment}-vpc-SubnetPrivateA
- Fn::ImportValue: !Sub ${SystemName}-${Environment}-vpc-SubnetPrivateC
- Fn::ImportValue: !Sub ${SystemName}-${Environment}-vpc-SubnetPrivateD
## RDS: DB Cluster
DBCluster:
Type: AWS::RDS::DBCluster
Properties:
DBClusterIdentifier: !Sub ${SystemName}-${Environment}-rds
Engine: !FindInMap [ SystemNameMap, !Ref SystemName, Engine ]
EngineMode: provisioned
EngineVersion: !FindInMap [ SystemNameMap, !Ref SystemName, EngineVersion ]
MasterUsername: !Sub "{{resolve:secretsmanager:${SecretForRDS}:SecretString:username}}"
MasterUserPassword: !Sub "{{resolve:secretsmanager:${SecretForRDS}:SecretString:password}}"
DBSubnetGroupName: !Ref DBSubnetGroup
AvailabilityZones:
- !Sub ${AWS::Region}a
- !Sub ${AWS::Region}c
- !Sub ${AWS::Region}d
VpcSecurityGroupIds:
- !Ref EC2SecurityGroup
Port: 3306 ## Aurora MySQL = 3306 / Aurora PostgreSQL = 5432
DBClusterParameterGroupName: !Ref DBClusterParameterGroup
PreferredMaintenanceWindow: !FindInMap [ SystemNameMap, !Ref SystemName, PreferredMaintenanceWindowCluster ]
PreferredBackupWindow: !FindInMap [ SystemNameMap, !Ref SystemName, PreferredBackupWindow ]
BackupRetentionPeriod: !FindInMap [ EnvironmentMap, !Ref Environment, BackupRetentionPeriod ]
CopyTagsToSnapshot: true
StorageEncrypted: true
EnableCloudwatchLogsExports:
- audit
- error
- general
- slowquery
DeletionProtection: true
## RDS: DB Instance @ Private-A
DBInstanceA:
Type: AWS::RDS::DBInstance
Properties:
PromotionTier: 1
AvailabilityZone: !Sub ${AWS::Region}a
PubliclyAccessible: false
DBClusterIdentifier: !Ref DBCluster
Engine: !FindInMap [ SystemNameMap, !Ref SystemName, Engine ]
DBInstanceClass: !FindInMap [ EnvironmentMap, !Ref Environment, DBInstanceClass ]
CACertificateIdentifier: rds-ca-rsa2048-g1
DBParameterGroupName: !Ref DBParameterGroup
PreferredMaintenanceWindow: !FindInMap [ SystemNameMap, !Ref SystemName, PreferredMaintenanceWindowInstanceA ]
AutoMinorVersionUpgrade: true
EnablePerformanceInsights: false ## Not suported on Aurora MySQL db.t3/t4g instance classes.
MonitoringInterval: !If [ EnabledEnhancedMonitoring, 60, 0 ]
MonitoringRoleArn: !If [ EnabledEnhancedMonitoring, !ImportValue iam-role-AmazonRDSEnhancedMonitoringRoleArn, !Ref AWS::NoValue ]
## RDS: DB Instance @ Private-C
DBInstanceC:
Condition: ShouldCreateDBInstanceC
Type: AWS::RDS::DBInstance
Properties:
PromotionTier: 2
AvailabilityZone: !Sub ${AWS::Region}c
PubliclyAccessible: false
DBClusterIdentifier: !Ref DBCluster
Engine: !FindInMap [ SystemNameMap, !Ref SystemName, Engine ]
DBInstanceClass: !FindInMap [ EnvironmentMap, !Ref Environment, DBInstanceClass ]
CACertificateIdentifier: rds-ca-rsa2048-g1
DBParameterGroupName: !Ref DBParameterGroup
PreferredMaintenanceWindow: !FindInMap [ SystemNameMap, !Ref SystemName, PreferredMaintenanceWindowInstanceC ]
AutoMinorVersionUpgrade: true
EnablePerformanceInsights: false ## Not suported on Aurora MySQL db.t3/t4g instance classes.
MonitoringInterval: !If [ EnabledEnhancedMonitoring, 60, 0 ]
MonitoringRoleArn: !If [ EnabledEnhancedMonitoring, !ImportValue iam-role-AmazonRDSEnhancedMonitoringRoleArn, !Ref AWS::NoValue ]
## RDS: DB Instance @ Private-D
DBInstanceD:
Condition: ShouldCreateDBInstanceD
Type: AWS::RDS::DBInstance
Properties:
PromotionTier: 3
AvailabilityZone: !Sub ${AWS::Region}d
PubliclyAccessible: false
DBClusterIdentifier: !Ref DBCluster
Engine: !FindInMap [ SystemNameMap, !Ref SystemName, Engine ]
DBInstanceClass: !FindInMap [ EnvironmentMap, !Ref Environment, DBInstanceClass ]
CACertificateIdentifier: rds-ca-rsa2048-g1
DBParameterGroupName: !Ref DBParameterGroup
PreferredMaintenanceWindow: !FindInMap [ SystemNameMap, !Ref SystemName, PreferredMaintenanceWindowInstanceD ]
AutoMinorVersionUpgrade: true
EnablePerformanceInsights: false ## Not suported on Aurora MySQL db.t3/t4g instance classes.
MonitoringInterval: !If [ EnabledEnhancedMonitoring, 60, 0 ]
MonitoringRoleArn: !If [ EnabledEnhancedMonitoring, !ImportValue iam-role-AmazonRDSEnhancedMonitoringRoleArn, !Ref AWS::NoValue ]
## Secrets Manager: Secret Target Attachment (root)
SecretTargetAttachment:
Type: AWS::SecretsManager::SecretTargetAttachment
Properties:
SecretId: !Ref SecretForRDS
TargetId: !Ref DBCluster
TargetType: AWS::RDS::DBCluster
## Secrets Manager: Secret Target Attachment (${SystemName})
SecretTargetAttachmentAwsmaster:
Type: AWS::SecretsManager::SecretTargetAttachment
Properties:
SecretId: !Ref SecretForRDSAwsmaster
TargetId: !Ref DBCluster
TargetType: AWS::RDS::DBCluster
Outputs:
## Secrets Manager: Secret (root)
SecretForRDS:
Value: !Ref SecretForRDS
Export:
Name: !Sub ${AWS::StackName}-SecretForRDS
## Secrets Manager: Secret (${SystemName})
SecretForRDSAwsmaster:
Value: !Ref SecretForRDSAwsmaster
Export:
Name: !Sub ${AWS::StackName}-SecretForRDSAwsmaster
## EC2(VPC): Security Group
EC2SecurityGroup:
Value: !Ref EC2SecurityGroup
Export:
Name: !Sub ${AWS::StackName}-EC2SecurityGroup
EC2SecurityGroupVpcId:
Value: !GetAtt EC2SecurityGroup.VpcId
Export:
Name: !Sub ${AWS::StackName}-EC2SecurityGroupVpcId
## RDS: DB Cluster Parameter Group
DBClusterParameterGroup:
Value: !Ref DBClusterParameterGroup
Export:
Name: !Sub ${AWS::StackName}-DBClusterParameterGroup
## RDS: DB Parameter Group
DBParameterGroup:
Value: !Ref DBParameterGroup
Export:
Name: !Sub ${AWS::StackName}-DBParameterGroup
## RDS: DB Subnet Group
DBSubnetGroup:
Value: !Ref DBSubnetGroup
Export:
Name: !Sub ${AWS::StackName}-DBSubnetGroup
## RDS: DB Cluster
DBCluster:
Value: !Ref DBCluster
Export:
Name: !Sub ${AWS::StackName}-DBCluster
DBClusterEndpointAddress:
Value: !GetAtt DBCluster.Endpoint.Address
Export:
Name: !Sub ${AWS::StackName}-DBClusterEndpointAddress
DBClusterEndpointPort:
Value: !GetAtt DBCluster.Endpoint.Port
Export:
Name: !Sub ${AWS::StackName}-DBClusterEndpointPort
DBClusterReadEndpointAddress:
Value: !GetAtt DBCluster.ReadEndpoint.Address
Export:
Name: !Sub ${AWS::StackName}-DBClusterReadEndpointAddress
## RDS: DB Instance @ Private-A
DBInstanceA:
Value: !Ref DBInstanceA
Export:
Name: !Sub ${AWS::StackName}-DBInstanceA
DBInstanceAEndpointAddress:
Value: !GetAtt DBInstanceA.Endpoint.Address
Export:
Name: !Sub ${AWS::StackName}-DBInstanceAEndpointAddress
DBInstanceAEndpointPort:
Value: !GetAtt DBInstanceA.Endpoint.Port
Export:
Name: !Sub ${AWS::StackName}-DBInstanceAEndpointPort
## RDS: DB Instance @ Private-C
DBInstanceC:
Condition: ShouldCreateDBInstanceC
Value: !Ref DBInstanceC
Export:
Name: !Sub ${AWS::StackName}-DBInstanceC
DBInstanceCEndpointAddress:
Condition: ShouldCreateDBInstanceC
Value: !GetAtt DBInstanceC.Endpoint.Address
Export:
Name: !Sub ${AWS::StackName}-DBInstanceCEndpointAddress
DBInstanceCEndpointPort:
Condition: ShouldCreateDBInstanceC
Value: !GetAtt DBInstanceC.Endpoint.Port
Export:
Name: !Sub ${AWS::StackName}-DBInstanceCEndpointPort
## RDS: DB Instance @ Private-D
DBInstanceD:
Condition: ShouldCreateDBInstanceD
Value: !Ref DBInstanceD
Export:
Name: !Sub ${AWS::StackName}-DBInstanceD
DBInstanceDEndpointAddress:
Condition: ShouldCreateDBInstanceD
Value: !GetAtt DBInstanceD.Endpoint.Address
Export:
Name: !Sub ${AWS::StackName}-DBInstanceDEndpointAddress
DBInstanceDEndpointPort:
Condition: ShouldCreateDBInstanceD
Value: !GetAtt DBInstanceD.Endpoint.Port
Export:
Name: !Sub ${AWS::StackName}-DBInstanceDEndpointPort
## Secrets Manager: Secret Target Attachment (root)
SecretTargetAttachment:
Value: !Ref SecretTargetAttachment
Export:
Name: !Sub ${AWS::StackName}-SecretTargetAttachment
## Secrets Manager: Secret Target Attachment (${SystemName})
SecretTargetAttachmentAwsmaster:
Value: !Ref SecretTargetAttachmentAwsmaster
Export:
Name: !Sub ${AWS::StackName}-SecretTargetAttachmentAwsmaster
【変更履歴】
- 2021-07-26:EngineVersionを
5.7.mysql_aurora.2.09.1
から5.7.mysql_aurora.2.09.2
に変更 - 2022-04-27:EngineVersionを
5.7.mysql_aurora.2.09.2
から5.7.mysql_aurora.2.10.2
に変更 - 2022-06-16:DBクラスターとインスタンスのプロパティ値の見直し
- 2022-11-30:EngineVersionを
5.7.mysql_aurora.2.10.2
から5.7.mysql_aurora.2.11.0
に変更 - 2023-03-02:EngineVersionを
5.7.mysql_aurora.2.11.0
から5.7.mysql_aurora.2.11.1
に変更 - 2023-04-06:冒頭でImportValueについてのコメント補足追加
- 2023-04-14:コメント文を全般的に修正
- 2023-07-04:
- EngineVersionを
5.7.mysql_aurora.2.11.1
から5.7.mysql_aurora.2.11.3
に変更 - クラスターパラメータグループのパラメータに
require_secure_transport: "ON"
を追加(log_output: FILE
は不要なので削除) - DBインスタンスに
CACertificateIdentifier: rds-ca-rsa2048-g1
を追加 - 2024-01-20:
- DBInstanceClassを
db.t3.small
からdb.t4g.medium
に変更 - Engineを
aurora-mysql5.7
からaurora-mysql8.0
に変更 - EngineVersionを
5.7.mysql_aurora.2.11.3
から8.0.mysql_aurora.3.05.1
に変更 - ※Aurora MySQL 2 から 3 へのメジャーバージョンアップ手順はFAQでご覧ください
- 2024-03-12:EngineVersionを
5.7.mysql_aurora.3.05.1
から8.0.mysql_aurora.3.05.2
に変更(重大な欠陥が発見されたため)
5. 予習
- CFnでDBクラスター・インスタンスを作成してください ※CFnでエラーになった場合は、EventsタブのスクリーンショットをSlackでください (AWSの画面が変わっている場合は同じ設定項目を探してください)
- CFnで作成したリソースを全て、手でも作ってください。作成中に分からなかった設定項目については調べたり、質問のためにメモしておいてください ※順番も大事なので、CFnの記載順を参考にしてください。ただしSecretは最初に作らず、DBクラスター作成後に「Secret type: Credentials for RDS database」を選んで作ってください
- 【宿題】下記の項目について調べて、次回のZoomで説明してください (公式の情報が優先で、分からない場合はブログ等を参照ください)
- Secrets Managerのシークレットの料金体系
- Amazon Aurora (MySQL) のDBインスタンス、ストレージ、バックアップの料金体系。今回作ったDBインスタンスを1ヶ月連続で動かした場合、インスタンス料金、ストレージ料金、バックアップ料金はいくらかかるか?(計算上、1ヶ月=30日×24時間とする)
- Amazon Auroraのインスタンス(Read Replica)を1台追加した場合、インスタンス、ストレージの料金はどうなるか?
- 今回は「オンデマンド」インスタンスを使っていますが、それ以外に「リザーブド」インスタンスという種類もあります。db.t4g.mediumの場合、「オンデマンド」と比較して割引率はどれくらいか?(リザーブドは複数パターンありますのでご注意ください)
- RDSをPrivate Subnetに作った理由は?
- Amazon Aurora MySQLの特徴について、公式 Amazon Aurora MySQL の特徴 | MySQL PostgreSQL リレーショナルデータベース | アマゾン ウェブ サービス に目を通しておいてください。興味がある点は深入りして調べてみてください
- RDSにはスペックが異なる「DBインスタンスクラス」が存在します(※EC2の時はインスタンスタイプでした。名前の違いに気づいていないエンジニアは多く、RDSでもEC2と同じくインスタンスタイプと言っても差し支えありません)公式の日本語ドキュメント DB インスタンスクラス - Amazon Aurora、最新の英語ドキュメント DB instance classes - Amazon Aurora を熟読しておいてください
- 【応用課題】余裕のある方は実際に挑戦して頂くと勉強になると思います! ※時間の都合でZoomで解説はしませんので、答え合わせをされたい方はSlackでDMください
- CFnテンプレートの「CreateDBInstanceC: false」をtrueにしてスタックを更新し、DBインスタンスを2台作ってください。一方がWriter、もう一方がReaderになることを確認してください
- 障害発生や意図的な操作で「フェイルオーバー」を行うと、WriterとReaderが入れ替わります。試してみてください。Eventsメニューで一連の動作が確認できるはずです
- テスト終了後はコスト削減のため、1.と逆の操作でDBインスタンスは1台に戻すことをオススメします
- Secretに「AWSPREVIOUS」と「AWSCURRENT」の2つのバージョンステージが作られています。それぞれSecretの値がどうなっているのか、AWS CLIを使って確認してください
6. FAQ(随時追加)
- AWS::RDS::DBClusterParameterGroup - AWS CloudFormation
- AWS::RDS::DBParameterGroup - AWS CloudFormation
の通り、DBParameterGroupの2種類についてはCFnで名前を指定することができないため、CFnが自動的に命名した乱数入りの名前を使っています。
の通り、DBSubnetGroupについてはCFnで名前を指定することができますが、DBParameterGroupに合わせて乱数入りの名前を使っています。名前を指定してもしなくてもどちらでもOKです。
の通り、Secretについても名前を指定することができますが、敢えて名前を指定しないで乱数入りの名前を使っています。というのも、CFnや手で削除した場合、最短でも7日間は待機(=アーカイブ)状態になり、削除ができません。その結果、CFnでスタックを削除→再作成した場合、Secretの名前を指定しておくと名前が重複してエラーになるため、敢えて乱数入りの名前を使っています。
実は今まで作ったリソースもこれから作るリソースも、ほとんどでNameは必須要素ではなく、指定しないとCFnが自動的に命名してくれます。命名規則はリソースによってバラバラですが…。
名前を指定した方が分かりやすいことが多いのでたいていは指定していますが、上記のSecretのように指定しない方が良い場合もあります。
セッション資料・動画一覧 - AWS Summit 2019 | AWS より『サービス責任者が語る Amazon Aurora MySQL/PostgreSQL の詳細と内部構造』の資料と動画が参考になります。ただDBの専門用語が多いので難易度は高めです。Auroraについて詳しく知りたい方はご覧ください。 ※2019年6月時点の情報なので、情報が古い点はご注意ください。
- 資料:https://pages.awscloud.com/rs/112-TZM-766/images/D2-05.pdf
- 動画:(最後の方はスキップされたので、資料を読むしかないようです)
バージョンを X.Y.Z
(例えば 1.2.3)と表記した場合、
X
=メジャーバージョン (Major Version) 下位メジャーバージョンと互換性がない大きな改変Y
=マイナーバージョン (Minor Version) 下位マイナーバージョンと互換性がある機能追加Z
=パッチバージョン (Patch Version) 下位パッチバージョンと互換性があるバグ修正
と呼びます。以下、参考まで。
- 詳細なルールは セマンティック バージョニング 2.0.0 | Semantic Versioning をご覧ください
- バージョン番号の付け方の練習は 正しいバージョンの付け方を身につけよう | 株式会社LYZON でできます
- Aurora MySQLのバージョン番号については Aurora MySQL のバージョン番号と特殊バージョン - Amazon Aurora が公式ドキュメントです
手順は ①アップデート、②バックアップ(=スナップショット)からの復元で再作成、③ダンプ(=エクスポート)しておいて再作成後にインポート など様々な方法がありますが、私が実際に行った③の手順を記しておきます。
▼以下、⑧AMIの手順を正常に終えていることを前提とします▼
- メジャーバージョンアップによって影響がないか調査する。下記によれば文字コードの違いを気にする程度だった
- RDSのDBクラスターと、ELB配下のEC2インスタンスを起動する
- RDSのDBクラスターでスナップショットを作成する(念のためのバックアップ)
- EC2インスタンスにSession Managerで接続し、次のコマンドを実行する
- RDSのDBクラスターの変更画面で、Deletion Protectionを無効にする
- RDSのCFnテンプレートを次のように変更する
- DBClusterParameterGroup / DBParameterGroup / DBCluster / DBInstanceA / DBInstanceC / DBInstanceD / SecretTargetAttachment / SecretTargetAttachmentAwsmaster のResourcesをコメントアウトする
- DBClusterEndpointPortのOutputsを次のように変更する
- 上記DBClusterEndpointPort以外、DBClusterParameterGroup / DBParameterGroup / DBCluster / DBInstanceA / DBInstanceC / DBInstanceD / SecretTargetAttachment / SecretTargetAttachmentAwsmaster 関連のOutputsをコメントアウトする
- RDSのCFnスタックを更新する
- RDSのCFnテンプレートを次のように変更する
- 上記6.で変更した内容を全て元に戻す
- Mappingsの中で次のように変更する
- RDSのCFnスタックを更新する
- EC2インスタンスにSession Managerで接続し、次のコマンドを実行する
- ブラウザで
https://elb.[ご自身のドメイン名]/
にアクセスして動作確認を行う - RDSのDBクラスターと、ELB配下のEC2インスタンスを停止する
Aurora MySQL バージョン 2 と Aurora MySQL バージョン 3 の比較 - Amazon Aurora https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/Aurora.AuroraMySQL.Compare-v2-v3.html
bash
cd
docker exec -it app /bin/bash
## 事前にSecrets Managerでrootのパスワードを確認しておく
mysqldump --ssl -h ${MYSQL_HOST} -u root -p \
--databases ${MYSQL_DATABASE} --default-character-set=utf8mb4 \
${MYSQL_DATABASE} > output.dump
DBClusterEndpointPort:
Value: !GetAtt DBCluster.Endpoint.Port
↓
DBClusterEndpointPort:
# Value: !GetAtt DBCluster.Endpoint.Port
Value: 3306
DBInstanceClass: db.t3.small
↓3箇所
DBInstanceClass: db.t4g.medium
ParameterGroupFamily: aurora-mysql5.7
EngineVersion: 5.7.mysql_aurora.2.11.3
↓1箇所
ParameterGroupFamily: aurora-mysql8.0
EngineVersion: 8.0.mysql_aurora.3.05.2
bash
cd
docker exec -it app /bin/bash
## 事前にSecrets Managerでrootのパスワードを確認しておく
mysql --ssl -h ${MYSQL_HOST} -u root -p -e \
"CREATE USER IF NOT EXISTS '${MYSQL_USER}'@'%' IDENTIFIED BY '${MYSQL_PASSWORD}'; \
GRANT ALL ON \`%\`.* TO '${MYSQL_USER}'@'%'; \
SHOW GRANTS FOR '${MYSQL_USER}'@'%';"
mysql --ssl -h ${MYSQL_HOST} -u root -p < output.dump
EC2SecurityGroup
にインバウンドルールが1つも追加されていません。これではどこからも通信できず、使えない状態ですが、なぜこのような設計なのですか?今回RDSを作った時点ではまだどこから使うかは想定できないので、インバウンドルールも設定できません。
この先 ⑧AMIで(ELB配下の)EC2からRDSを使うことになりますので、その時点でインバウンドルールを追加します。
実は教材の都合に留まらず、CFnを設計する上でもその方が望ましいです。
Q. EC2からRDSに接続したい場合、RDSのインバウンドルールをどこで追加するか? A-1. RDS側のテンプレで追加する A-2. EC2側のテンプレで追加する
どちらでも動きますが、
A-1.は分かりやすいものの、EC2の追加・削除時にRDS側のテンプレも変更する必要があります。そうするとインバウンドルールの追加はともかく、削除作業が漏れやすくなります。
A-2.はRDSのテンプレは変更せず、EC2側のテンプレにRDSのインバウンドルールも含めます。そうするとEC2の追加・削除とインバウンドルールの追加・削除が連動しますので、作業漏れがなくなります。分かりづらいのは欠点ですが...。
CFnテンプレを作る際は、そのテンプレで作ったリソースが削除される時に「一緒に削除されて欲しい」リソースなのか「一緒に削除されたら困る/他でも使う」リソースなのかを、「同じテンプレに含める」または「別のテンプレにする」という判断基準にされると良いです。
EC2を乗っ取られない限りは問題ないですが、乗っ取られた場合にRDSが危うくなるのは確かです。そのためインターネットから直接EC2にアクセスできないように、EC2はPublic SubnetではなくProtected Subnetに置いた方が望ましいです。
ただPublic Subnetに置いてもSession Managerを使うことでSSHポートを開けないようにしたり、HTTP/HTTPSなど必要なポートしか開けないようにすることでセキュリティレベルを高めることはできます。
またPublicに置いてもProtectedに置いても、EC2にログインすることなく、Webアプリケーションの脆弱性を突かれた場合(SQLインジェクション攻撃など)は同じですので、WAF(Web Application Firewall)など追加手段で防御する必要もあります。
セキュリティは気にし始めるとキリがない(当然コストもかかる)ので、実務上どこまで厳しくするかという線引きは必要ですね。というのもサービスや会社の規模、扱っている情報や業種の違いなどによっても判断基準は変わります。
ちなみにPublic SubnetのEC2とPrivate SubnetのRDS間の通信自体はVPC内部の話ですので、特に懸念する必要はありません。AWS自身に悪意があればどうしようもないのですが...。
t2, t3, t4gのインスタンスクラスでは有効にできないのが残念ですが、DBの負荷をSQLクエリごとに、リアルタイムで確認できる機能です。クエリの詳細、接続元ホスト、接続ユーザーなどを調べられます。
実際の現場でもとても重宝しており、DBの負荷が異常に高くなった場合に原因を調べたり、対策を検討する場合に活躍します。
Classmethodさんの記事: SQL レベルのメトリクス をサポートしたパフォーマンスインサイトで高負荷状態のDB稼働を確認してみた | DevelopersIO
でイメージが沸くと思います。
ちなみに記事の中の図で「データベースのロード」とありますが、ロード(load)=負荷という意味の英語です。つまり縦軸はロード(load)を表しており、分かりやすく言うなら「SQL処理で使っている プロセス数 または vCPU数」と思って頂いてもかまいません。
「最大vCPUを表示」にチェックを付けて表示された破線が、そのインスタンスクラスのvCPU数を表しているので、そこまでは同時に実行できる「プロセス数」に余裕があります。
逆にその破線を越えてしまうとCPUの待ち(=SQLクエリの処理待ち)が発生するので、一時的にDBからの応答が遅くなってしまいます。たまに超えるならまだしも頻繁に発生するようになると、DBインスタンスのスペックを増やすか、SQLクエリ側をチューニングするかの対策が必要になります。
公式ブログ: 初めてのPerformance Insights入門 | Amazon Web Services ブログ 初めてのPerformance Insights入門 – その2 | Amazon Web Services ブログ 初めてのPerformance Insights入門 – その3 | Amazon Web Services ブログ
にも詳しく書かれています。
公式ドキュメント: Amazon Aurora での Performance Insights を使用したDBロードのモニタリング - Amazon Aurora
もご参考まで。
【↓⑪ECSのFAQより抜粋。詳しくは⑪ECSで解説します】
Parameterを作成する際にSecureStringを選択すると暗号化して保存されます。Secretとほぼ同じ機能を持っているため、現場でも使い分けはかなり悩みます。
結論から言えば、DBのパスワードを保存したり、CFnで乱数生成したい場合はSecretが向いていますが、それ以外はParameter (SecureString)を手で作成する方が気軽にできると思います。
全てを網羅した記事がネット上になかったので、下記にまとめておきます。とにかくややこしいです。変化も激しいので、下記の内容(2022年4月時点)が変わっている可能性も大いにあります。
名前 | Secret | Parameter(SecureString) |
---|---|---|
料金 | 有料(ごく少額ですが) | 無料 |
CFnで作成 | 可能。さらに乱数生成も可能 | 不可能 |
JSONで保存(=DBの情報など、複数のパラメータをまとめる) | 可能。さらに個別にKeyを指定して特定のValueのみを参照することも可能。そのためユーザー名・パスワードなど、DBの情報を1つのSecretで保存できる | 可能。ただし個別にKeyを指定して特定のValueのみを参照することは不可能。そのためユーザー名・パスワードなど、DBの情報を保存する際は個別に保存した方が良い |
パスワードローテーション機能 | あり(画面上にローテーション機能の表示あり。内部的にはLambda Functionで動くようになっている。昔は手でLambdaを作っていたが、今はその必要がなくなった) | なし(もちろんLambda Functionと組み合わせて自分で作り込むことはできるが、画面上にローテーションの機能の表示はない) |
パラメータのバージョン管理 ※正直かなり複雑です... | ステージングラベルを使えばバージョン管理できるが、自動的には管理されない。AWS CLIやCFnテンプレートでは複数バージョンを参照できるが、画面上では参照できない。 ※パスワードローテーションをするためには変更前・変更後で2つのパスワードを保存する必要があるため、実装されたのだろう | 過去分も自動でバージョン管理されている。画面上やCFnテンプレートでは過去分を参照できるが、AWS CLIでは参照できない。 |
CFnテンプレートでの参照 | 可能。構文: | 可能。 構文: |
ECSタスク定義での参照 | 前述の通り1つのSecretでもJSONの中のKeyを指定することで個別にパラメータ参照できる。ステージングラベルを指定して複数バージョンの参照もできる(あまりしないが) | 単に最新のパラメータを参照するだけ(JSONの中のKeyを指定して参照することや、過去バージョンを指定して参照することはできない) |
対応するサービス | 次第に増えているが、Parameter(SecureString)よりは少ない | 多い。例えばFargateでも最初の頃はParameter(SecretString)のみ対応していた |
もう片方を参照できるか? ※ややこしすぎます... | (恐らくSecrets ManagerのAPIからParameterは参照できない。後発サービスなので実装する必要性が感じられない) | CLIなどで |
参考(2018年8月の古い記事。Secrets Managerが登場した頃):AWSのParameter StoreとSecrets Manager、結局どちらを使えばいいのか?比較 - Qiita
参考(Secretのパスワードローテーション機能):AWS Secrets Managerで RDSのパスワードローテーションしてみる in 2022 | DevelopersIO
参考(Secretのステージングラベルの使い方):新サービス AWS Secrets Manager のシークレットを CLI から更新し、その動作を理解する | DevelopersIO
参考(CFnテンプレートでの動的な参照):動的な参照を使用してテンプレート値を指定する - AWS CloudFormation
参考(Parameter Storeの GetParameter
APIを使ってSecretを参照):AWS Secrets Manager パラメータからの Parameter Store シークレットの参照 - AWS Systems Manager
→ このドキュメントを読んで初めて知りましたが「AWS Systems Manager の一機能である Parameter Store は Secrets Manager と統合された」んですね...。ややこしすぎます。
以上、お疲れさまでした!