初めに
Amazon RDSはスナップショットの復元は別アカウントからのデータ移行や、バックアップからの復元、様々なケースで利用されます。
個人的にはバックアップからの復元や既存ののデータを利用した一時検証環境などを使うケースで利用することが多く、その関係IaCで管理する環境に含めることが少なく今まで手動で立ち上げしかやったことのない対応でした。
直近別環境から既存RDSのスナップショットを利用して新規環境を立ち上げる対応でスナップショットから立ち上げる対応をCloudFormatinoで書く機会があったので備忘録として残しておきます。
別アカウントからのスナップショット復元
本筋からは少し外れますが別のアカウントからのスナップショットを利用する場合は、そのアカウントからスナップショットを共有してもらう必要があります。
なお自動バックアップでは共有できないようなので共有が必要な場合は自動バックアップとは別に手動でスナップショットを取得し、そちらを共有する必要があります。
その他詳細は上記AWSドキュメントページをご参照ください。
スナップショットからの立ち上げ方
今回は非Aurora環境から試しますがこの場合はAWS::RDS::DBInstanceのDBSnapshotIdentifier
に対してスナップショットのARNを指定することでそのスナップショットからのインスタンスの立ち上げが可能です。
Auroraを利用する場合はAWS::RDS::DBClusterのSnapshotIdentifier
となりそうですが、DBInstanceの方にもDBClusterSnapshotIdentifier
というパラメータがあるのでこの辺りの指定は改めて機会がありましたら確認してみます。
DBSnapshotIdentifierの指定変更に関する注意事項
AWS::RDS::DBInstanceに記載がありますが一度指定した後の変更には注意が必要です。
After you restore a DB instance with a DBSnapshotIdentifier property, you can delete the DBSnapshotIdentifier property. When you specify this property for an update, the DB instance is not restored from the DB snapshot again, and the data in the database is not changed. However, if you don't specify the DBSnapshotIdentifier property, an empty DB instance is created, and the original DB instance is deleted. If you specify a property that is different from the previous snapshot restore property, a new DB instance is restored from the specified DBSnapshotIdentifier property, and the original DB instance is deleted.
つまりのところDBSnapshotIdentifier
でしているする復元元のスナップショットの識別子(ARN)の値が指定された場合は(そのスナップショットで再作成が必要なため)置換処理がかかるようです。
ただし指定がある状態から指定を削除した場合は特に何も処理が行われない形になるとのことです(現状維持)。
削除保護をつけておくことで防止はできますが誤って指定を変えてしまった場合に元のDBを吹き飛ばす可能性があるので一応注意しましょう。
テンプレート
CloudFormationテンプレートは以下のようになります。
今回はDBSnapshotArn
を空とすることでDBSnapshotIdentifier
がAWS::NoValue
(すなわちパラメータ無し)として扱われるようになっております。
今回の対応は以下の流れで対応を行います。
CloudFormationで新規立ち上げ->DB内容変更->スナップショット作成->DB内容変更->スナップショット指定追加(スナップショット復元)->スナップショット指定削除(空のDB作成)
VPC等一部のリソースは毎回作成するのは煩雑でベースになるものを事前にあるのでそちらを利用しています。バージョンのベタ書き等がありますがその点は目を瞑っていただければと思います。
AWSTemplateFormatVersion: 2010-09-09
Parameters:
Env:
Type: String
Prefix:
Type: String
DeleteProtection:
Type: String
AllowedValues:
- True
- False
EnableMultiAZ:
Type: String
AllowedValues:
- True
- False
VPCId:
Type: String
RDSSGId:
Type: String
SubnetGroupId:
Type: String
DBSnapshotId:
Type: String
DBInstanceClass:
Type: String
RDSStorageSize:
Type: Number
Conditions:
SpecifySnapshot: !Not [ !Equals [ !Ref DBSnapshotId, ""]]
Resources:
#----------------------------------
#----- RDS Instance
#----------------------------------
DBInstance:
Type: AWS::RDS::DBInstance
Properties:
Engine: postgres
MasterUsername: postgres
AllocatedStorage: !Ref RDSStorageSize
StorageType: gp3
# 後述しますがこの設定が原因で後々失敗します
DBInstanceIdentifier: !Sub ${Env}-${Prefix}
MasterUserPassword: !Sub "{{resolve:secretsmanager:${RDSMasterUserPassword}:SecretString:password::}}"
AvailabilityZone: !Sub ${AWS::Region}a
DBInstanceClass: !Ref DBInstanceClass
AutoMinorVersionUpgrade: False
EnablePerformanceInsights: True
MultiAZ: !Ref EnableMultiAZ
CACertificateIdentifier: "rds-ca-rsa4096-g1"
CopyTagsToSnapshot: True
DBSubnetGroupName: !Ref SubnetGroupId
OptionGroupName: !Ref RDSOptionGroup
DBSnapshotIdentifier:
Fn::If:
- SpecifySnapshot
- !Ref DBSnapshotId
- !Ref AWS::NoValue
VPCSecurityGroups:
- !Ref RDSSGId
DBParameterGroupName: !Ref InstanceParameterGroup
DeletionProtection: !Ref DeleteProtection
Tags:
- Key: Environment
Value: !Ref Env
RDSMasterUserPassword:
Type: AWS::SecretsManager::Secret
Properties:
Name: !Sub ${Env}-${Prefix}-master-password
GenerateSecretString:
GenerateStringKey: password
PasswordLength: 20
ExcludePunctuation: True
SecretStringTemplate: '{}'
#----------------------------------
#----- Parameter Group
#----------------------------------
InstanceParameterGroup:
Type: AWS::RDS::DBParameterGroup
Properties:
DBParameterGroupName: !Sub ${Env}-${Prefix}-instance-pg
Description: "-"
Family: postgres15
Tags:
- Key: Environment
Value: !Ref Env
#----------------------------------
#----- Option Group
#----------------------------------
RDSOptionGroup:
Type: AWS::RDS::OptionGroup
Properties:
EngineName: postgres
MajorEngineVersion: 15
OptionGroupName: !Sub ${Env}-${Prefix}-og
OptionGroupDescription: "-"
Tags:
- Key: Environment
Value: !Ref Env
新規立ち上げ〜データ投入〜スナップショット作成
まずはスナップショットなしで立ち上げを行います。
先ほどのテンプレートでスタックを立ち上げる際にDBSnapshotId
を空、後々再作成を行う関係でDeleteProtection
をfalseにして削除保護をつけないようにして起動します。
起動後接続を行い適当なテストデータを作っておきます。
postgres=> select version();
version
---------------------------------------------------------------------------------------------------------
PostgreSQL 15.4 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 7.3.1 20180712 (Red Hat 7.3.1-12), 64-bit
(1 row)
postgres=> CREATE TABLE history(id serial, text text, create_date timestamp DEFAULT now());
CREATE TABLE
postgres=> INSERT INTO history(text) SELECT md5(generate_series::text) FROM generate_series(1,20);
INSERT 0 20
postgres=> SELECT * FROM history;
id | text | create_date
----+----------------------------------+----------------------------
1 | c4ca4238a0b923820dcc509a6f75849b | 2023-12-19 08:41:54.345255
2 | c81e728d9d4c2f636f067f89cc14862c | 2023-12-19 08:41:54.345255
3 | eccbc87e4b5ce2fe28308fd9f2a7baf3 | 2023-12-19 08:41:54.345255
4 | a87ff679a2f3e71d9181a67b7542122c | 2023-12-19 08:41:54.345255
5 | e4da3b7fbbce2345d7772b0674a318d5 | 2023-12-19 08:41:54.345255
6 | 1679091c5a880faf6fb5e6087eb1b2dc | 2023-12-19 08:41:54.345255
7 | 8f14e45fceea167a5a36dedd4bea2543 | 2023-12-19 08:41:54.345255
8 | c9f0f895fb98ab9159f51fd0297e236d | 2023-12-19 08:41:54.345255
9 | 45c48cce2e2d7fbdea1afc51c7c6ad26 | 2023-12-19 08:41:54.345255
10 | d3d9446802a44259755d38e6d163e820 | 2023-12-19 08:41:54.345255
11 | 6512bd43d9caa6e02c990b0a82652dca | 2023-12-19 08:41:54.345255
12 | c20ad4d76fe97759aa27a0c99bff6710 | 2023-12-19 08:41:54.345255
13 | c51ce410c124a10e0db5e4b97fc2af39 | 2023-12-19 08:41:54.345255
14 | aab3238922bcc25a6f606eb525ffdc56 | 2023-12-19 08:41:54.345255
15 | 9bf31c7ff062936a96d3c8bd1f8f2ff3 | 2023-12-19 08:41:54.345255
16 | c74d97b01eae257e44aa9d5bade97baf | 2023-12-19 08:41:54.345255
17 | 70efdf2ec9b086079795c442636b55fb | 2023-12-19 08:41:54.345255
18 | 6f4922f45568161a8cdf4ad2299f6d23 | 2023-12-19 08:41:54.345255
19 | 1f0e3dad99908345f7439f8ffabdffc4 | 2023-12-19 08:41:54.345255
20 | 98f13708210194c475687be6106a3b84 | 2023-12-19 08:41:54.345255
(20 rows)
マネジメントコンソールから手動でスナップショットを取得します。
後ほどCloudFormationのパラメータのDBSnapshotId
(と言いつつARN)を入れるのでスナップショットのARNを控えておきます。
今回は同一アカウントのため前述のような別アカウント共有は行いません。
データ変更〜スナップショット復元
上記のスナップショットで戻したことが確認できるようにさらにデータを追加しておきます。
postgres=> INSERT INTO history(text) SELECT md5(generate_series::text) FROM generate_series(1,20);
INSERT 0 20
postgres=> SELECT * FROM history offset 20;
id | text | create_date
----+----------------------------------+----------------------------
21 | c4ca4238a0b923820dcc509a6f75849b | 2023-12-19 08:51:18.906937
22 | c81e728d9d4c2f636f067f89cc14862c | 2023-12-19 08:51:18.906937
23 | eccbc87e4b5ce2fe28308fd9f2a7baf3 | 2023-12-19 08:51:18.906937
24 | a87ff679a2f3e71d9181a67b7542122c | 2023-12-19 08:51:18.906937
25 | e4da3b7fbbce2345d7772b0674a318d5 | 2023-12-19 08:51:18.906937
26 | 1679091c5a880faf6fb5e6087eb1b2dc | 2023-12-19 08:51:18.906937
27 | 8f14e45fceea167a5a36dedd4bea2543 | 2023-12-19 08:51:18.906937
28 | c9f0f895fb98ab9159f51fd0297e236d | 2023-12-19 08:51:18.906937
29 | 45c48cce2e2d7fbdea1afc51c7c6ad26 | 2023-12-19 08:51:18.906937
30 | d3d9446802a44259755d38e6d163e820 | 2023-12-19 08:51:18.906937
31 | 6512bd43d9caa6e02c990b0a82652dca | 2023-12-19 08:51:18.906937
32 | c20ad4d76fe97759aa27a0c99bff6710 | 2023-12-19 08:51:18.906937
33 | c51ce410c124a10e0db5e4b97fc2af39 | 2023-12-19 08:51:18.906937
34 | aab3238922bcc25a6f606eb525ffdc56 | 2023-12-19 08:51:18.906937
35 | 9bf31c7ff062936a96d3c8bd1f8f2ff3 | 2023-12-19 08:51:18.906937
36 | c74d97b01eae257e44aa9d5bade97baf | 2023-12-19 08:51:18.906937
37 | 70efdf2ec9b086079795c442636b55fb | 2023-12-19 08:51:18.906937
38 | 6f4922f45568161a8cdf4ad2299f6d23 | 2023-12-19 08:51:18.906937
39 | 1f0e3dad99908345f7439f8ffabdffc4 | 2023-12-19 08:51:18.906937
40 | 98f13708210194c475687be6106a3b84 | 2023-12-19 08:51:18.906937
(20 rows)
postgres=> SELECT COUNT(*) FROM history;
count
-------
40
(1 row)
先ほど作成したスナップショットのARNをDBSnapshotId
に指定し同一のスタックをアップデートします。
置換条件がConditionになっているのでこの時点では発生するかは断定できません。
アップデートすると失敗してしまいました。
完全に失念していましたがこれはDBInstanceIdentifier
を指定していることで置換処理ができないためのエラーとなります。
(RDSの仕組み上同一アカウントリージョン上に同名のDBが作れない為)
識別子のために固定の名前をつけていることも多いかと思いますのでCloudFormationでDBSnapshotIdentifier
を差し替えるような型値前提としている場合はDBInstanceIdentifier
を未指定にするようにして対応しましょう。
DBInstanceIdentifier
の指定を削除したテンプレートで再作成し(DBSnapshotIdの値は空)あらためて先ほどのDBSnapshotId
を指定する形で更新を行います。
(後で冷静に考えるとわざわざ一度消さずにDBInstanceIdentifier
の指定を削除してアップデートで十分だったかもしれません)
スタックを更新してみると新しいリソースを作成するメッセージが含まれており置換処理が発生することがわかります。
作成中にDBインスタンスを見に行くと置換前後のインスタンス両方が確認できます。
SELECT文を実行して最初に取ったスナップショットの状態に戻っていることを確認します。
sh-4.2$ psql -h db-snapshot-dbinstance-yflw3q0pvlzb.xxxxxx.ap-northeast-1.rds.amazonaws.com -U postgres
Password for user postgres:
psql (14.8, server 15.4)
WARNING: psql major version 14, server major version 15.
Some psql features might not work.
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.
postgres=> select * from history;
id | text | create_date
----+----------------------------------+----------------------------
1 | c4ca4238a0b923820dcc509a6f75849b | 2023-12-19 08:41:54.345255
2 | c81e728d9d4c2f636f067f89cc14862c | 2023-12-19 08:41:54.345255
3 | eccbc87e4b5ce2fe28308fd9f2a7baf3 | 2023-12-19 08:41:54.345255
4 | a87ff679a2f3e71d9181a67b7542122c | 2023-12-19 08:41:54.345255
5 | e4da3b7fbbce2345d7772b0674a318d5 | 2023-12-19 08:41:54.345255
6 | 1679091c5a880faf6fb5e6087eb1b2dc | 2023-12-19 08:41:54.345255
7 | 8f14e45fceea167a5a36dedd4bea2543 | 2023-12-19 08:41:54.345255
8 | c9f0f895fb98ab9159f51fd0297e236d | 2023-12-19 08:41:54.345255
9 | 45c48cce2e2d7fbdea1afc51c7c6ad26 | 2023-12-19 08:41:54.345255
10 | d3d9446802a44259755d38e6d163e820 | 2023-12-19 08:41:54.345255
11 | 6512bd43d9caa6e02c990b0a82652dca | 2023-12-19 08:41:54.345255
12 | c20ad4d76fe97759aa27a0c99bff6710 | 2023-12-19 08:41:54.345255
13 | c51ce410c124a10e0db5e4b97fc2af39 | 2023-12-19 08:41:54.345255
14 | aab3238922bcc25a6f606eb525ffdc56 | 2023-12-19 08:41:54.345255
15 | 9bf31c7ff062936a96d3c8bd1f8f2ff3 | 2023-12-19 08:41:54.345255
16 | c74d97b01eae257e44aa9d5bade97baf | 2023-12-19 08:41:54.345255
17 | 70efdf2ec9b086079795c442636b55fb | 2023-12-19 08:41:54.345255
18 | 6f4922f45568161a8cdf4ad2299f6d23 | 2023-12-19 08:41:54.345255
19 | 1f0e3dad99908345f7439f8ffabdffc4 | 2023-12-19 08:41:54.345255
20 | 98f13708210194c475687be6106a3b84 | 2023-12-19 08:41:54.345255
(20 rows)
なお今回初めて知ったのですがリストア復旧の場合はモニタリングのセッティングの後にResetting-master-credentials-message
と言う処理が挟まりこの段階でマスターユーザの情報が変更されるようです。
スナップショット指定削除
DBSnapshotId
の指定を削除しDBSnapshotIdentifier
の指定が有りから無しになった際の挙動を見ます。
この場合は特に置換が発生しないことが確認できます。
少しわかりづらいですが更新処理を行った19:32~3頃にはRDS側のイベントは特に発生しておらず、また先のスナップショットからの復元を行った際のログも残っております。
終わりに
終わりに今回はCloudFormationによるスナップショットの復元を試しつつ指定が変動した際の挙動を確認してみました。
一発目の立ち上げを行う場合には今回のような置換処理等が発生せずにスムーズに行えるため便利ではありそうですが、変更するような運用を取る際にはパラメータの指定には少し注意が必要そうです。
CloudFormationでスナップショットを利用したRDSインスタンスの立ち上げは可能ですが置換等を行う場合は条件によっては利用できないことがございますので利用シーンや差し替えのフローを整理の上、場合によっては手動での対応も視野に入れていただければと思います。