はじめに
AWS CDKでは、インフラをコードで管理できます。一方で、CloudFormationスタックを削除・更新する際には、AWSリソースや保存データが意図せず削除される可能性があります。
その挙動を制御するのがRemovalPolicyです。
本記事では、Amazon RDS for PostgreSQLを例に、DESTROY、RETAIN、SNAPSHOTの違いを実際に検証します。削除時のデータ消失リスクや、開発環境・本番環境での使い分けについても説明します。
対象読者
- AWS CDKを使い始めた方
-
RemovalPolicyの違いを整理したい方 - 開発環境と本番環境での削除ポリシーを検討している方
RemovalPolicyとは
RemovalPolicyは、CDKで管理しているAWSリソースがCloudFormationによって削除される際の挙動を指定する設定です。
removalPolicy: cdk.RemovalPolicy.DESTROY
主な値は次の3つです。
| ポリシー | 削除時の挙動 | データ |
|---|---|---|
DESTROY |
リソースを削除 | 原則として消失 |
RETAIN |
リソースを削除せず残す | 保持される |
SNAPSHOT |
スナップショットを作成して削除 | スナップショットから復元可能 |
ただし、SNAPSHOTはすべてのAWSサービスで利用できるわけではありません。対応状況はリソースごとに異なります。
検証環境
今回は、RemovalPolicyの違いを確認しやすいAmazon RDS for PostgreSQLを使用します。
- AWS CDK
- TypeScript
- Amazon RDS for PostgreSQL
- Amazon VPC
- AWS CloudFormation
検証では、データベースにテストデータを登録した後、RemovalPolicyを変更してスタックを削除します。
CDKプロジェクトの作成
mkdir removal-policy-sample
cd removal-policy-sample
cdk init app --language typescript
npm install
VPCとRDSを定義する
lib/removal-policy-sample-stack.tsを次のようにします。
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as rds from 'aws-cdk-lib/aws-rds';
export class RemovalPolicySampleStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const vpc = new ec2.Vpc(this, 'Vpc', {
maxAzs: 2,
natGateways: 0,
});
const database = new rds.DatabaseInstance(this, 'Database', {
engine: rds.DatabaseInstanceEngine.postgres({
version: rds.PostgresEngineVersion.VER_16,
}),
vpc,
// パブリックサブネットへ配置
vpcSubnets: {
subnetType: ec2.SubnetType.PUBLIC,
},
instanceType: ec2.InstanceType.of(
ec2.InstanceClass.T4G,
ec2.InstanceSize.MICRO,
),
allocatedStorage: 20,
databaseName: 'sampledb',
// 認証情報はSecrets Managerに保存
credentials: rds.Credentials.fromGeneratedSecret('postgres'),
removalPolicy: cdk.RemovalPolicy.DESTROY,
deletionProtection: false,
// パブリックアクセスを有効化
publiclyAccessible: true,
});
// 自分のグローバルIPだけ許可
database.connections.allowFrom(
ec2.Peer.ipv4('<自分のグローバルIP>/32'),
ec2.Port.tcp(5432),
'Allow PostgreSQL access from local PC',
);
new cdk.CfnOutput(this, 'Endpoint', {
value: database.dbInstanceEndpointAddress,
});
new cdk.CfnOutput(this, 'SecretArn', {
value: database.secret!.secretArn,
});
}
}
初期状態では、DESTROYを設定しています。
検証用に指定したグローバルIPから接続を許可しています。
<自分のグローバルIP>部分は自分の環境に合わせて変更してください。
なお、検証後は必ず削除してください。
デプロイする
npm run build
cdk bootstrap
cdk deploy
デプロイ後、データベースのエンドポイントを取得します。
aws cloudformation describe-stacks \
--stack-name RemovalPolicySampleStack \
--query "Stacks[0].Outputs"
認証情報はSecrets Managerに保存されています。接続には、次のようにシークレットを取得します。
aws secretsmanager get-secret-value \
--secret-id <シークレット名>
取得した認証情報を使って、データを登録します。
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
INSERT INTO users (name)
VALUES ('Alice'), ('Bob');
SELECT * FROM users;
登録結果の例です。
id | name
----+-------
1 | Alice
2 | Bob
DESTROYを検証する
まず、RemovalPolicy.DESTROYのままスタックを削除します。
cdk destroy
確認プロンプトで削除を実行すると、RDSインスタンス自体が削除されます。
再度AWSコンソールで確認すると、次の状態になります。
- DBインスタンス:削除済み
- DBインスタンスに保存されていたデータ:消失
- 自動バックアップ:保持期間などの設定に依存
- 手動スナップショット:作成されない
つまり、DESTROYはリソースとデータを削除する設定です。
DESTROYの用途
- 一時的な検証環境
- CI/CDで毎回作り直す環境
- データを残す必要がない開発環境
本番データベースで安易に指定すると、重大なデータ消失につながります。
RETAINを検証する
次に、RemovalPolicyを変更します。
removalPolicy: cdk.RemovalPolicy.RETAIN,
再度デプロイします。
cdk deploy
データベースが作成されたら、先ほどと同じようにテストデータを登録します。
その後、スタックを削除します。
cdk destroy
RETAINでは、CloudFormationスタックは削除されますが、RDSインスタンスは残ります。
RETAINの確認ポイント
- CloudFormationスタック:削除済み
- RDSインスタンス:残る
- データ:保持される
- CDKスタックとの管理関係:失われる
重要なのは、残ったリソースがCDKスタックから管理されなくなる点です。
不要になったリソースは、別途AWSコンソールやCLIから削除する必要があります。
aws rds delete-db-instance \
--db-instance-identifier <DB識別子> \
--skip-final-snapshot
RETAINの用途
- 本番データベース
- スタック削除後もデータを保持したい場合
- 誤削除への対策
ただし、リソースが残り続けることで、利用料金も発生し続けます。
SNAPSHOTを検証する
最後に、次のように変更します。
removalPolicy: cdk.RemovalPolicy.SNAPSHOT,
スタックをデプロイし、テストデータを登録した後で削除します。
cdk deploy
cdk destroy
SNAPSHOTでは、RDSインスタンスの削除前に最終スナップショットが作成されます。
SNAPSHOTの確認ポイント
- CloudFormationスタック:削除済み
- DBインスタンス:削除済み
- 最終スナップショット:作成される
- データ:スナップショットとして保持される
スナップショット一覧は、次のコマンドで確認できます。
aws rds describe-db-snapshots \
--snapshot-type manual
スナップショットから復元する場合は、次のようにします。
aws rds restore-db-instance-from-db-snapshot \
--db-instance-identifier restored-db \
--db-snapshot-identifier <スナップショット識別子>
復元後は、エンドポイントを確認して接続します。
aws rds describe-db-instances \
--db-instance-identifier restored-db \
--query "DBInstances[0].Endpoint.Address"
SNAPSHOTの用途
- 削除前にバックアップを残したい場合
- 検証環境を削除するが、データを復元できるようにしたい場合
- データベースの移行や再構築を想定している場合
スナップショットは自動的に削除されません。不要になった後は、手動で削除する必要があります。
aws rds delete-db-snapshot \
--db-snapshot-identifier <スナップショット識別子>
3つのポリシーを比較する
| 観点 | DESTROY | RETAIN | SNAPSHOT |
|---|---|---|---|
| リソース | 削除 | 残る | 削除 |
| データ | 消失 | 保持 | スナップショットに保持 |
| 復元 | 困難 | 残ったリソースを利用 | スナップショットから復元 |
| 後処理 | 少ない | リソース削除が必要 | スナップショット削除が必要 |
| 料金リスク | 小さい | 継続課金に注意 | スナップショット料金に注意 |
| 主な用途 | 開発・一時環境 | 本番データ | バックアップを残す環境 |
データ消失リスクについて
RemovalPolicyは、バックアップ戦略そのものではありません。
例えばSNAPSHOTを指定していても、次のようなリスクがあります。
- スナップショット作成に失敗する
- 対象リソースが
SNAPSHOTに対応していない - スナップショットを誤って削除する
- 別リージョンの障害には対応できない
- 復元手順を検証していない
- データベース以外の関連リソースを削除する
また、RETAINも「安全にバックアップされる」という意味ではありません。単にリソースを削除せず残すだけです。
本番環境では、次の対策も組み合わせる必要があります。
- 自動バックアップ
- 手動スナップショット
- AWS Backup
- クロスリージョンバックアップ
- 削除保護
- 復元手順の定期的な検証
- IAMによる削除権限の制限
deletionProtectionとの違い
RDSにはdeletionProtectionという設定もあります。
deletionProtection: true,
deletionProtectionは、DBインスタンスの削除自体を防止する設定です。
一方、RemovalPolicyは、削除されることになった場合の挙動を指定します。
| 設定 | 役割 |
|---|---|
deletionProtection |
削除操作を防ぐ |
RemovalPolicy |
削除時の動作を決める |
本番環境では、例えば次のような組み合わせが考えられます。
removalPolicy: cdk.RemovalPolicy.RETAIN,
deletionProtection: true,
この場合、誤ったcdk destroyだけでなく、削除保護によってDB削除も防ぎやすくなります。
環境ごとに設定を切り替える
開発環境と本番環境でポリシーを切り替える方法もあります。
const isProduction =
this.node.tryGetContext('environment') === 'prod';
const removalPolicy = isProduction
? cdk.RemovalPolicy.RETAIN
: cdk.RemovalPolicy.DESTROY;
const deletionProtection = isProduction;
cdk.jsonのコンテキストを使ってデプロイします。
cdk deploy -c environment=dev
cdk deploy -c environment=prod
環境別に明示的な設定を持たせることで、誤って本番環境にDESTROYを適用するリスクを下げられます。
CloudFormationテンプレートを確認する
CDKで指定したRemovalPolicyは、最終的にCloudFormationのDeletionPolicyとしてテンプレートへ反映されます。
cdk synth
生成されたテンプレート内で、次のような記述を確認できます。
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
削除時だけでなく、リソースの置換時にもデータを保護したい場合は、UpdateReplacePolicyの挙動も確認することが重要です。
注意点
すべてのリソースがSNAPSHOTに対応しているわけではない
SNAPSHOTは、スナップショット機能を持つリソースでのみ利用できます。
対応していないリソースでは、期待したバックアップが作成されない可能性があります。対象リソースのCDKドキュメントやCloudFormationリファレンスを確認しましょう。
RemovalPolicyの変更は削除を伴う場合がある
既存リソースに対してRemovalPolicyを変更すると、CloudFormationの更新内容によってはリソースの置換や削除が発生することがあります。
変更前に必ず差分を確認します。
cdk diff
RETAINしたリソースは孤立する
スタックを削除した後、RETAINされたリソースはCloudFormationのスタック管理から外れます。
後から不要になったリソースを削除し忘れないよう、棚卸しの仕組みを用意しておくと安全です。
テスト後は必ずリソースを確認する
検証環境では、次のコマンドで不要なリソースが残っていないか確認します。
aws rds describe-db-instances
aws rds describe-db-snapshots --snapshot-type manual
VPCやSecrets Managerのシークレットなど、関連リソースも確認しましょう。
検証後のクリーンアップ
RETAINを使用した場合、RDSインスタンスが残っています。
SNAPSHOTを使用した場合は、手動スナップショットが残っています。
不要なリソースを確認したうえで削除します。
aws rds delete-db-instance \
--db-instance-identifier <DB識別子> \
--skip-final-snapshot
aws rds delete-db-snapshot \
--db-snapshot-identifier <スナップショット識別子>
削除後は、AWSマネジメントコンソールやAWS CLIで、関連リソースが残っていないことを確認してください。
まとめ
今回の検証結果は次のとおりです。
-
DESTROYはリソースとデータを削除する -
RETAINはリソースとデータを残す -
SNAPSHOTはスナップショットを作成してからリソースを削除する -
RETAINやSNAPSHOTでも、料金や削除漏れに注意が必要 -
RemovalPolicyだけでなく、削除保護やバックアップも組み合わせる必要がある
迷った場合の目安は次のとおりです。
| 環境 | 推奨例 |
|---|---|
| 一時的な開発環境 | DESTROY |
| データを絶対に残したい本番環境 | RETAIN |
| 削除後に復元できる状態を残したい環境 | SNAPSHOT |
特に本番環境では、cdk destroyを実行する前に対象リソースとRemovalPolicyを確認する習慣をつけましょう。
この記事が、誰かのお役に立てば幸いです。