CFn管理のRDSをエンドポイントを変えず & 非リプレイス(旧RDSを削除しない)でリストア(PITR)する方法
はじめに
IaC管理のRDSのリストアをどうするかは、「IaCの永遠の課題である」とかなんとかって、どっかのお偉いさんが言ってるように(嘘です)、CFnでもそれは例外ではありません。
RDSのエンドポイントを変えてよい & リプレイス(旧RDSは削除)で良いのであれば、CFnのSnapshotIdentifierとRestoreToTimeのパラメータを使えば特に問題はないのですが、アプリケーションへの影響を鑑みて「エンドポイントを変えずにリストアしたい!」とか、「旧RDSは残したい!」とか要求されることがあると思います。
こうなってくると一気に難易度が跳ね上がる(バックトラックが要件的に使えない前提)んですが、CFnには「管理しているリソースの物理IDが変わらなければ、そのリソースが別物になっていたとしても、特に関知しない」といった仕様があるようで(私がRDSで検証した限り)、この仕様の活用とシェル芸を頑張れば、CFn管理のRDSであってもこれが可能です。
本記事では、この方法を紹介します。
手順
概要
リストア元の旧RDSから設定値をファイルにダンプし、旧RDSの識別子を変更する。
ダンプしたファイルを元に同じ設定値を持つ新RDSを元々の旧RDSの識別子で作成する。
その後、CFnでドリフトが発生していないかを確認する。
前提
- Auroraはインスタンス数が2つで、エンジンがMySQLの前提
- 非AuroraはマルチAZで、エンジンがSQLServerの前提
- リストア元の旧RDSは削除しない
- 作業はコンソールとCloudShellで行う
- 各コマンドは、旧RDSのパラメータ、JSONフォーマット次第で変わるので、あくまでも例
リストア手順 for Aurora
旧RDSクラスター、旧RDSインスタンス1、旧RDSインスタンス2のDB識別子をそれぞれ控える
以下環境変数をexportする
環境変数名 | 値 | 備考 |
---|---|---|
DB_CLUSTER_NAME | ${旧RDSクラスターのDB識別子} | |
DB_INSTANCE1_NAME | ${旧RDSインスタンス1のDB識別子} | |
DB_INSTANCE2_NAME | ${旧RDSインスタンス2のDB識別子} | |
RESTORE_TO_TIME | ${復元する時間} | 復元する時間をUTCで「YYYY-MM-DDThh:mm:ssZ」の形式で指定 |
コマンド例
export DB_CLUSTER_NAME=hoge-rds-cluster
export DB_INSTANCE1_NAME=hoge-rds-instance1
export DB_INSTANCE2_NAME=hoge-rds-instance2
export RESTORE_TO_TIME=2024-03-05T07:26:00Z
旧RDSクラスターの設定値をJSONファイルにダンプする
コマンド例
aws rds describe-db-clusters --filters "Name=db-cluster-id,Values=$DB_CLUSTER_NAME" --output "json" > currentclustersconfig.json
旧RDSインスタンス1と旧RDSインスタンス2の設定値をJSONファイルにダンプする
コマンド例
aws rds describe-db-instances --filters "Name=db-instance-id,Values=$DB_INSTANCE1_NAME" --output "json" > currentinstanceconfig1.json
aws rds describe-db-instances --filters "Name=db-instance-id,Values=$DB_INSTANCE2_NAME" --output "json" > currentinstanceconfig2.json
旧RDSクラスター、旧RDSインスタンス1と2のDB識別子を変更する
コマンド例
aws rds modify-db-cluster --db-cluster-identifier $DB_CLUSTER_NAME --new-db-cluster-identifier "$DB_CLUSTER_NAME-old" --apply-immediately --no-cli-pager
aws rds modify-db-instance --db-instance-identifier $DB_INSTANCE1_NAME --new-db-instance-identifier "$DB_INSTANCE1_NAME-old" --apply-immediately --no-cli-pager
aws rds modify-db-instance --db-instance-identifier $DB_INSTANCE2_NAME --new-db-instance-identifier "$DB_INSTANCE2_NAME-old" --apply-immediately --no-cli-pager
旧RDSクラスター、旧RDSインスタンス1と2のDB識別子がサフィックスに-oldが付与されたものに変更され、ステータスが[利用可能]になることを確認する。
旧RDSクラスターを停止する
旧RDSクラスターを停止すると同時に、旧RDSインスタンスも自動で停止する。
安全なリストアを目的に念の為に停止する。
コマンド例
aws rds stop-db-cluster --db-cluster-identifier "$DB_CLUSTER_NAME-old" --no-cli-pager
旧RDSクラスター、旧RDSインスタンス1と2のステータスが[一時的に停止済み]になることを確認する
JSONファイルの設定値を元に、新RDSクラスターを作成(リストア)する
コマンド例
aws rds restore-db-cluster-to-point-in-time --db-cluster-identifier $DB_CLUSTER_NAME --source-db-cluster-identifier "$DB_CLUSTER_NAME-old" --restore-to-time $RESTORE_TO_TIME --port "`cat currentclustersconfig.json | jq -r '.DBClusters[0].Port'`" --db-subnet-group-name "`cat currentclustersconfig.json | jq -r '.DBClusters[0].DBSubnetGroup'`" --vpc-security-group-ids "`cat currentclustersconfig.json | jq -r '.DBClusters[0].VpcSecurityGroups[0].VpcSecurityGroupId'`" --tags "`cat currentclustersconfig.json | jq -r '.DBClusters[0].TagList' | jq -r 'map(select(.Key | startswith("aws:") | not))'`" --kms-key-id "`cat currentclustersconfig.json | jq -r '.DBClusters[0].KmsKeyId'`" --enable-cloudwatch-logs-exports "`cat currentclustersconfig.json | jq -r '.DBClusters[0].EnabledCloudwatchLogsExports'`" --db-cluster-parameter-group-name "`cat currentclustersconfig.json | jq -r '.DBClusters[0].DBClusterParameterGroup'`" --deletion-protection --engine-mode "`cat currentclustersconfig.json | jq -r '.DBClusters[0].EngineMode'`" --serverless-v2-scaling-configuration "`cat currentclustersconfig.json | jq -r '.DBClusters[0].ServerlessV2ScalingConfiguration'`" --network-type "`cat currentclustersconfig.json | jq -r '.DBClusters[0].NetworkType'`" --no-cli-pager
新RDSクラスター(-oldのサフィックスが付与されてないもの)のステータスが[利用可能]になることを確認する
JSONファイルの設定値を元に、新RDSインスタンス1を作成する
コマンド例
aws rds create-db-instance --db-instance-identifier $DB_INSTANCE1_NAME --db-instance-class "`cat currentinstanceconfig1.json | jq -r '.DBInstances[0].DBInstanceClass'`" --engine "`cat currentinstanceconfig1.json | jq -r '.DBInstances[0].Engine'`" --availability-zone "`cat currentinstanceconfig1.json | jq -r '.DBInstances[0].AvailabilityZone'`" --preferred-maintenance-window "`cat currentinstanceconfig1.json | jq -r '.DBInstances[0].PreferredMaintenanceWindow'`" --db-parameter-group-name "`cat currentinstanceconfig1.json | jq -r '.DBInstances[0].DBParameterGroups[0].DBParameterGroupName'`" --tags "`cat currentinstanceconfig1.json | jq -r '.DBInstances[0].TagList' | jq -r 'map(select(.Key | startswith("aws:") | not))'`" --db-cluster-identifier $DB_CLUSTER_NAME --monitoring-interval "`cat currentinstanceconfig1.json | jq -r '.DBInstances[0].MonitoringInterval'`" --monitoring-role-arn "`cat currentinstanceconfig1.json | jq -r '.DBInstances[0].MonitoringRoleArn'`" --promotion-tier "`cat currentinstanceconfig1.json | jq -r '.DBInstances[0].PromotionTier'`" --enable-performance-insights --performance-insights-kms-key-id "`cat currentinstanceconfig1.json | jq -r '.DBInstances[0].PerformanceInsightsKMSKeyId'`" --performance-insights-retention-period "`cat currentinstanceconfig1.json | jq -r '.DBInstances[0].PerformanceInsightsRetentionPeriod'`" --no-cli-pager
JSONファイルの設定値を元に、新RDSインスタンス2を作成する
コマンド例
aws rds create-db-instance --db-instance-identifier $DB_INSTANCE2_NAME --db-instance-class "`cat currentinstanceconfig2.json | jq -r '.DBInstances[0].DBInstanceClass'`" --engine "`cat currentinstanceconfig2.json | jq -r '.DBInstances[0].Engine'`" --availability-zone "`cat currentinstanceconfig2.json | jq -r '.DBInstances[0].AvailabilityZone'`" --preferred-maintenance-window "`cat currentinstanceconfig2.json | jq -r '.DBInstances[0].PreferredMaintenanceWindow'`" --db-parameter-group-name "`cat currentinstanceconfig2.json | jq -r '.DBInstances[0].DBParameterGroups[0].DBParameterGroupName'`" --tags "`cat currentinstanceconfig2.json | jq -r '.DBInstances[0].TagList' | jq -r 'map(select(.Key | startswith("aws:") | not))'`" --db-cluster-identifier $DB_CLUSTER_NAME --monitoring-interval "`cat currentinstanceconfig2.json | jq -r '.DBInstances[0].MonitoringInterval'`" --monitoring-role-arn "`cat currentinstanceconfig2.json | jq -r '.DBInstances[0].MonitoringRoleArn'`" --promotion-tier "`cat currentinstanceconfig2.json | jq -r '.DBInstances[0].PromotionTier'`" --enable-performance-insights --performance-insights-kms-key-id "`cat currentinstanceconfig2.json | jq -r '.DBInstances[0].PerformanceInsightsKMSKeyId'`" --performance-insights-retention-period "`cat currentinstanceconfig2.json | jq -r '.DBInstances[0].PerformanceInsightsRetentionPeriod'`" --no-cli-pager
新RDSインスタンス1と2(-oldのサフィックスが付与されてないもの)のステータスが[利用可能]になることを確認する
JSONファイルの設定値を元に、新RDSクラスターの設定を変更する
新RDSクラスターのリストア時にできなかったパラメータの設定を実施。
コマンド例
aws rds modify-db-cluster --db-cluster-identifier "$DB_CLUSTER_NAME" --preferred-maintenance-window "`cat currentclustersconfig.json | jq -r '.DBClusters[0].PreferredMaintenanceWindow'`" --no-auto-minor-version-upgrade --apply-immediately --no-cli-pager
CFnでドリフトが検出されないことを(IN_SYNC)確認する
ドリフトが検出された場合は、ドリフトの解消を行う。
リストア手順 for 非Aurora
旧RDSインスタンスのDB識別子を控える
以下環境変数をexportする
環境変数名 | 値 | 備考 |
---|---|---|
DB_INSTANCE_NAME | ${旧RDSインスタンスのDB識別子} | |
RESTORE_TO_TIME | ${復元する時間} | 復元する時間をUTCで「YYYY-MM-DDThh:mm:ssZ」の形式で指定 |
コマンド例
export DB_INSTANCE_NAME=hoge-rds-instance
export RESTORE_TO_TIME=2024-03-06T06:50:01Z
旧RDSインスタンスの設定値をJSONファイルにダンプする
コマンド例
aws rds describe-db-instances --filters "Name=db-instance-id,Values=$DB_INSTANCE_NAME" --output "json" > currentinstanceconfig.json
旧RDSインスタンスのDB識別子を変更する
コマンド例
aws rds modify-db-instance --db-instance-identifier $DB_INSTANCE_NAME --new-db-instance-identifier "$DB_INSTANCE_NAME-old" --apply-immediately --no-cli-pager
旧RDSインスタンスの識別子がサフィックスに-oldが付与されたものに変更され、ステータスが[利用可能]になることを確認する。
旧RDSインスタンスを停止する
マルチAZのSQLServerは停止ができないので、スキップ。
JSONファイルの設定値を元に、新RDSインスタンスを作成(リストア)する
コマンド例
aws rds restore-db-instance-to-point-in-time --target-db-instance-identifier $DB_INSTANCE_NAME --source-db-instance-identifier "$DB_INSTANCE_NAME_old" --restore-time $RESTORE_TO_TIME --db-subnet-group-name "`cat currentinstanceconfig.json | jq -r '.DBInstances[0].DBSubnetGroup.DBSubnetGroupName'`" --multi-az --no-auto-minor-version-upgrade --iops "`cat currentinstanceconfig.json | jq -r '.DBInstances[0].Iops'`" --option-group-name "`cat currentinstanceconfig.json | jq -r '.DBInstances[0].OptionGroupMemberships[0].OptionGroupName'`" --copy-tags-to-snapshot --tags "`cat currentinstanceconfig.json | jq -r '.DBInstances[0].TagList'`" --storage-type "`cat currentinstanceconfig.json | jq -r '.DBInstances[0].StorageType'`" --vpc-security-group-ids "`cat currentinstanceconfig.json | jq -r '.DBInstances[0].VpcSecurityGroups[0].VpcSecurityGroupId'`" --enable-cloudwatch-logs-exports "`cat currentinstanceconfig.json | jq -r '.DBInstances[0].EnabledCloudwatchLogsExports'`" --db-parameter-group-name "`cat currentinstanceconfig.json | jq -r '.DBInstances[0].DBParameterGroups[0].DBParameterGroupName'`" --deletion-protection --source-dbi-resource-id "`cat currentinstanceconfig.json | jq -r '.DBInstances[0].DbiResourceId'`" --max-allocated-storage "`cat currentinstanceconfig.json | jq -r '.DBInstances[0].MaxAllocatedStorage'`" --network-type "`cat currentinstanceconfig.json | jq -r '.DBInstances[0].NetworkType'`" --storage-throughput "`cat currentinstanceconfig.json | jq -r '.DBInstances[0].StorageThroughput'`" --no-cli-pager
新RDSインスタンス(-oldのサフィックスが付与されてないもの)のステータスが[利用可能]になることを確認する
JSONファイルの設定値を元に、新RDSインスタンスの設定を変更する
新RDSインスタンスのリストア時にできなかったパラメータの設定を実施。
コマンド例
aws rds modify-db-instance --db-instance-identifier $DB_INSTANCE_NAME --monitoring-interval "`cat currentinstanceconfig.json | jq -r '.DBInstances[0].MonitoringInterval'`" --monitoring-role-arn "`cat currentinstanceconfig.json | jq -r '.DBInstances[0].MonitoringRoleArn'`" --enable-performance-insights --performance-insights-kms-key-id "`cat currentinstanceconfig.json | jq -r '.DBInstances[0].PerformanceInsightsKMSKeyId'`" --performance-insights-retention-period "` cat currentinstanceconfig.json | jq -r '.DBInstances[0].PerformanceInsightsRetentionPeriod'`" --manage-master-user-password --master-user-secret-kms-key-id "`cat currentinstanceconfig.json | jq -r '.DBInstances[0].MasterUserSecret.KmsKeyId'`" --apply-immediately --no-cli-pager
CFnでドリフトが検出されないことを(IN_SYNC)確認する
ドリフトが検出された場合は、ドリフトの解消を行う。
さいごに
シェル芸でただただ泥臭いことやってるだけですが、「CFn管理のRDSでエンドポイントを変えず & 非リプレイス(旧RDSを削除しない)でリストア(PITR)は無理ゲーやろおおおお」と、私は当初頭を抱えたので、同じような境遇に遭遇した方の手助けになれたらと。
Discussion