AWS RDSで構築されたプロダクトのDBテスト環境作成

はじめに
AWSで運用されているプロダクトのAuroraMySQLを、5.x系から最新の8.x系に更新するタスクが発生しました。
本番DBとは全く別のMySQLで構築されているテスト環境があり、そちらは問題なく更新ができましたが、念のために本番のデータベースと同じものでテストが必要と言う事になりました。
しかし、社内にAWSを完璧に扱える人材がいなかった為、自分でテスト環境を作成することになりました。
AWSの知識は1週間で叩きこんだだけの為、不安なのでメモを残しながら慎重に進めていこうと思います
これは、手順や参考文献、気になった点などをメモするためのスクラップです

手順概要
- テスト用サブドメインの作成
- 本番環境とは独立したECSクラスターを作成する
- 独立したVPCとサブネットを適切に設定する
4 . RDSのリーダーインスタンスからスナップショットを作成し、DBを復元する - 作成したテスト用ECSクラスターとスナップショットから復元したDBを接続してDBテスト環境を作る
-
task-definition.test.json
とdeploy--test-ecs-fargate.yml
を作る - GithubActionsでECSをデプロイ

必要な知識を補填
AWS CDK
CloudFormation
のテンプレート作成をPython
やTypeScript
などの言語を使って、より柔軟かつメンテナブルなインフラストラクチャのコードを記述するためのもの
最終的には、CDK
がそのコードをCloudFormation
テンプレートに変換した後にAWSに送信し、その内容に沿って実際にAWS上にリソースが作成、更新、削除される
AWS クラウド開発キット (AWS CDK) は、コードでクラウドインフラストラクチャを定義し、AWS CloudFormation を通じてプロビジョニングするためのオープンソースのソフトウェア開発フレームワークです。
AWS CDK Constructs
CDKは以下、APP->Stack->Constructの各要素によって構成されています。
-
APP:
コンポーネントの最上位要素
複数スタックの依存関係を定義 -
Stack:
CloudFormationのStack
デプロイ可能な最小単位 -
Construct:
1つ以上からなるAWSリソース
Construct Library
CDKはConstruct Libraryを使用してAWSリソースを定義します。また、Construct Libraryには以下3種類があります。
-
High Level Construct(L2): 直感的にリソースを定義することができ、デフォルト値や便利なメソッドが定義済み
全てのサービスをサポートしている訳ではない - Low Level Construct(L1): プロパティを明示的に設定する必要があるため、パラメーターを詳細に設定したい場合はこの低レベルライブラリを使用。クラス名にCfnプレフィックスがついている
- Patterns: 一般的なアーキテクチャパターンに従う高レベルのConstructを提供
Constructsは初期化時に3つのを引数使用します。
- scope: 現在の構成のスコープ内で構成を定義します。thisで渡すのが一般的です。
- id: 同じスコープ内では一意であるIDが必要です。
- props: キーワード引数になるため特に指定しない場合、適切なデフォルト値が設定されます。また、propsは省略可能です。
CloudFormation
AWSのリソースをコードで管理できるサービスです。テンプレートと呼ばれるテキストファイル(YAML/JSON)を読み込むと、自動でAWSの環境を作ってくれます。料金は、利用しているリソース分支払えば良いだけで、Cloud Formation自体の利用は無料です。
CloudFormationテンプレートのリファレンス
ElastiCache
ElastiCache
はインメモリ
型のデータベース
よく使うデータを一時的にElastiCacheに保存しておいて、いつでもすぐに渡せる状態にしておくことができる
こうすることで、アプリケーションは一度ElastiCacheに問い合わせして
ElastiCacheがデータを持っていなければRDSに取りに行くという仕組みができます。
ElastiCacheでは以下のOSSをサポートしています。
- Memcached
- Redis
この二つにはそれぞれ特徴があるが、基本的にはRedis
を選択する
Memcachedのユースケースはあまり出てきませんでした。ここでは、野球の試合を分析したり、広告を分析するデータ収集システムに使われていると書かれています。
私の感想にはなってしまいますが、Redisはマルチスレッドに対応しているので、
データの永続化が不要な大量のデータ処理などに使われているのかな?という印象を受けました。

テスト環境の具体的な構築方法
クローンするプロダクトはAWS CDK
で管理されていました。
管理できる人がおらず、一部の設定がマネジメントコンソール上で行われてしまい差異があるようです。
CDKの知識が浅いため、テスト環境だけマネジメントコンソール上で作ろうかとも思いました。
しかし管理が余計に複雑になる上、可能な限り本番環境と同じ環境が望ましい為、CDKでテスト環境用のスタックを別で作り、テスト用スタックだけをデプロイする方針にしようと思います。
この方法であれば、本番環境に一切影響を与えないはずです
const app = new cdk.App();
// 本番環境用のスタック
new InfraStack(app, 'InfraStackProd', {
env: {
account: '123456789012',
region: 'ap-northeast-1',
},
tags: {
environment: 'production',
},
});
// テスト環境用のスタック
new InfraStack(app, 'InfraStackTest', {
env: {
account: '123456789012',
region: 'ap-northeast-1',
},
tags: {
environment: 'test',
},
});
)
// package.json
// 事故防止に以下のスクリプトを使って検証、デプロイするようにする
{
"scripts": {
"cdk:diff:test": "cdk diff InfraStackTest",
"cdk:deploy:test": "cdk deploy InfraStackTest"
}

テスト環境用のサブドメインを作る
まずはRoute 53
でサブドメインを作成
- 管理画面からホストゾーンへ移動し、サブドメインを作りたいドメインを選択
- レコードを作成をクリックし、それぞれ必要そうなものを作成しておく
- test.
- dbtest.
- testmail.
- 値はとりあえず空にしておく

CDKデプロイ用のIAMロール・ユーザーの作成
参考文献
CDK を使った業務に必要な権限は一番最初に実行する cdk bootstrap コマンドによって作られます。
# npx cdk bootstrap
上記の様に引数なしで実行すると、東京リージョンの場合次の様な IAM Role が作られます
cdk-hnb659fds-cfn-exec-role-00000000000-ap-northeast-1
cdk-hnb659fds-deploy-role-00000000000-ap-northeast-1
cdk-hnb659fds-file-publishing-role-00000000000-ap-northeast-1
cdk-hnb659fds-image-publishing-role-00000000000-ap-northeast-1
-
cdk-hnb659fds-lookup-role-00000000000-ap-northeast-1
hnb659fds
と言う値はデフォルトで使われる物となっている
これらの role は、
cdk deploy
やcdk diff
などのAWS API
を扱うコマンドを実行する時に適宜使われます。
例えば
cdk deploy
を実行すると、synthesise
されたCloudFormation
テンプレートをS3
バケットにPut
する際はcdk-hnb659fds-file-publishing-role-00000000000-ap-northeast-1
を、その後CloudFormation
にスタックを作成したりする際はcdk-hnb659fds-deploy-role-00000000000-ap-northeast-1
が使われます。他にもcdk diff
の際は読み込み権限のみを持つcdk-hnb659fds-lookup-role-00000000000-ap-northeast-1
が使用される等、必要に応じて最適なrole
が使われる様になっています。

CDK
の現状をさらに調査したところ、デプロイ環境のモジュールのバージョンがv1
のままであったりと他にも問題が多かったので、最終的にはTerraform
に移行することになりました。
Terraform
でテスト環境を作成したいところですが、今回はもう学習する時間がない為マネジメントコンソール上で行うことにします。

テスト環境の作成(ネットワーク作成)
AWS CDK
で定義されている設定を参考にしつつ、マネジメントコンソール上で設定を行っていきます
VPCの作成
- 名前は「****-test-vpc」とする
-
IPv4 CIDR
は本番環境と被らない値を設定するよう注意
サブネットの作成
プライベートとパブリックをそれぞれ作成
インターネットゲートウェイの作成
作成してVPCにアタッチ
ルートテーブルの作成
それぞれのサブネット分作成し、パブリックサブネットだけ「ルートの編集」から送信先: 0.0.0.0/0
とターゲットをigw
にする設定を行う
Elastic IPの作成
NATゲートウェイ用のElastic IP
を、パブリックサブネットの分作成します
NAT ゲートウェイの作成
NATゲートウェイの作成時、先ほど作ったElastic IPを紐づけ、プライベートサブネットのルートに設定します
ルートテーブルの設定の続き
NAT ゲートウェイが作成されたら、各プライベートルートテーブルに以下のルートを追加します
- 送信先:
0.0.0.0/0
- ターゲット:
NAT ゲートウェイ
これでプライベートサブネットはNATゲートウェイを通じてインターネットと通信可能になります
セキュリティグループの作成
以下作成していきます
- VPC 用のセキュリティグループ
- 「タイプ」は「すべてのトラフィック」
- インバウンド・アウトバウンドルールでそれぞれVPCのCIDRブロックを指定
- ALB 用のセキュリティグループ(最終的にはIP制限をかける)
- インバウンドには
HTTP
用とHTTPS
用それぞれ設定 - アウトバウンドは「すべてのトラフィック」
- インバウンドには
- ECS タスク用のセキュリティグループ
- インバウンドには「タイプ」は
HTTP
で、ソースにALB用のセキュリティグループを指定 - アウトバウンドは「すべてのトラフィック」
- インバウンドには「タイプ」は
- RDS 用のセキュリティグループ
- インバウンドには「MYSQL/Aurora」でECSのセキュリティグループを適用
- アウトバウンドは「すべてのトラフィック」
- Redis 用のセキュリティグループ
- インバウンドには「カスタム TCP」「ポート番号は****」ソースにECSのセキュリティグループを適用
- アウトバウンドは「すべてのトラフィック」
一旦区切ります

NATゲートウェイってなに?
- NATゲートウェイは、プライベートサブネット内のリソースがインターネットと通信するために使用されます。
- プライベートIPアドレスをパブリックIPアドレスに変換する役割を果たします。
Elastic IPの必要性:
- NATゲートウェイには、パブリックIPアドレスが必要です。
- Elastic IPを使用することで、このパブリックIPアドレスが固定され、変更されません。
- これにより、外部サービスやファイアウォールのホワイトリストなどで、一貫したIPアドレスを使用できます。
プライベートサブネットのルーティング:
- プライベートサブネット内のリソースは、直接インターネットに接続できません。
- インターネットにアクセスするためには、NATゲートウェイを経由する必要があります。
- そのため、プライベートサブネットのルートテーブルで、インターネット向けのトラフィック(0.0.0.0/0)をNATゲートウェイにルーティングします。
セキュリティと管理の利点:
NATゲートウェイには、セキュリティ上の利点もあります。例えば、パブリックサブネットに配置されたNATゲートウェイは、インターネットからの未承諾トラフィックを拒否します。つまり、内部からのリクエストに対するレスポンスのみを受け入れるため、外部からの不要なアクセスからプライベートサブネットを保護することができます。
また、NATゲートウェイを利用する際は、VPC(仮想プライベートクラウド)内のトラフィックが適切にインターネットゲートウェイにルーティングされるように、ルートテーブルの設定を行う必要があります。これにより、プライベートサブネット内のリソースが、NATゲートウェイを通してインターネットと安全に通信できるようになります。
プライベートサブネットのインスタンス → NATゲートウェイ(Elastic IP付き)→ インターネットゲートウェイ → インターネット
となる
つまり、NATゲートウェイにElastic IPを紐づけ、プライベートサブネットのルートに設定することで、プライベートサブネット内のリソースが安全かつ制御された方法でインターネットにアクセスできるようになります。

テスト環境の作成(RDSのスナップショット復元)
Cloud Map の設定
- AWS マネジメントコンソールで Cloud Map サービスに移動します。
-
ECS
のクラスター作成時に自動生成された名前空間をクリックします。 - 「API 呼び出しと VPC の DNS クエリ」を選択
- 以下の設定を行います:
- 名前空間名
- インスタンスの検出: 「API 呼び出しと VPC の DNS クエリ」
- VPC: test-vpc を選択
- 「名前空間の作成」をクリックして完了します。
ECSクラスターの作成
- 今回はクローン元のプロジェクトが
AWS Fargate
なのでそれを選択 - 作成した名前空間を選択
-
CloudWatch Container Insights
は有効にしておきます
サブネットグループの作成
- 「Amazon ElastiCache」の「設定」から「サブネットグループ」を選択して作成画面へ
- test用VPCを選択し、「プライベートサブネットだけを選択」して作成
RDSのスナップショット作成
- クラスターを選択し、アクションから「スナップショットの取得」
- DBクラスターを選択
- スナップショット名は「****-prod-snapshot-yyyymmdd」などとする
- スナップショットの取得をクリック
RDS のスナップショット復元
- 作成されたスナップショットを選択しアクションから復元を選択
- 基本的には本番環境と同じ設定にする
- 「DB クラスター識別子」はインスタンスをまとめるクラスターの名前。元の名前にtestなどを加えたものに設定
- 「DB インスタンス識別子」はインスタンスの名前。元の名前にtestなどを加えたものに設定
- 「インスタンスの設定」では
db.t3.small
を今回選択 - 「可用性と耐久性」は「レプリカを作成しない」にチェック
- 「接続」設定
- テスト用VPCを選択
- 「パブリックアクセス」はなし
- セキュリティグループで
VPC
用のもの - アベイラビリティーゾーンの指定はなし
- データベース認証は「パスワード認証」

DB接続時にエラーが発生
RDS SQLSTATE[HY000] [1045] Access denied for user '****'
ユーザー名とパスワードは特に変更していないので本番のものと同じ設定をしているが、このようなエラーに遭遇してしまいました
調べてみると、DBのHOSTの値や様々な値が若干違うことに気付き修正しました
しかし依然エラーが解消されず・・・
解決方法
結論から言うと、AuroraProxy
がSecretManager
にアクセスする際の認証で弾かれていました
具体的には認証に本番用と同じIAMロールを設定していたのですが、そのロールのポリシーで明示的に対象のSecretManagerのARNを指定していたので、テスト用に新しくロールとポリシーを作成することに
ポリシーの作成時に対象のSecretManagerのARNを明示的に指定することで解決しました

まとめ
インフラの設定項目はセキュリティ的に公開するのも怖かったので、このスクラップには途中からエラー時のメモなどだけ残すようにしました。
中途半端になってしまいましたが、なんやかんやあり無事に目的は達成できました。
今回初めてAWSについて知り、実際の業務で扱ってみましたが、好みだったので資格の取得も検討しようとおもいます。