Closed12

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

LeivyLeivy

はじめに

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

LeivyLeivy

手順概要

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

必要な知識を補填

AWS CDK

CloudFormationのテンプレート作成をPythonTypeScriptなどの言語を使って、より柔軟かつメンテナブルなインフラストラクチャのコードを記述するためのもの
最終的には、CDKがそのコードをCloudFormationテンプレートに変換した後にAWSに送信し、その内容に沿って実際にAWS上にリソースが作成、更新、削除される

AWS クラウド開発キット (AWS CDK) は、コードでクラウドインフラストラクチャを定義し、AWS CloudFormation を通じてプロビジョニングするためのオープンソースのソフトウェア開発フレームワークです。

https://docs.aws.amazon.com/cdk/v2/guide/home.html

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は省略可能です。

https://qiita.com/Brutus/items/6c8d9bfaab7af53d154a

CloudFormation

AWSのリソースをコードで管理できるサービスです。テンプレートと呼ばれるテキストファイル(YAML/JSON)を読み込むと、自動でAWSの環境を作ってくれます。料金は、利用しているリソース分支払えば良いだけで、Cloud Formation自体の利用は無料です。

https://qiita.com/Nightley_dev/items/9ba1cf1744d8ae9fe021

CloudFormationテンプレートのリファレンス

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-reference.html

ElastiCache

ElastiCacheインメモリ型のデータベース
よく使うデータを一時的にElastiCacheに保存しておいて、いつでもすぐに渡せる状態にしておくことができる
こうすることで、アプリケーションは一度ElastiCacheに問い合わせして
ElastiCacheがデータを持っていなければRDSに取りに行くという仕組みができます。

https://zenn.dev/fdnsy/articles/6dd98ba5318bb6

ElastiCacheでは以下のOSSをサポートしています。

  • Memcached
  • Redis
    この二つにはそれぞれ特徴があるが、基本的にはRedisを選択する

Memcachedのユースケースはあまり出てきませんでした。ここでは、野球の試合を分析したり、広告を分析するデータ収集システムに使われていると書かれています。
私の感想にはなってしまいますが、Redisはマルチスレッドに対応しているので、
データの永続化が不要な大量のデータ処理などに使われているのかな?という印象を受けました。

LeivyLeivy

テスト環境の具体的な構築方法

クローンするプロダクトは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"
  }
LeivyLeivy

テスト環境用のサブドメインを作る

まずはRoute 53でサブドメインを作成

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

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 deploycdk 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 が使われる様になっています。

https://engineering.mobalab.net/2023/09/12/rethinking-of-aws-cdk-permissions/





https://speakerdeck.com/kinyok/cdk-deploynibi-yao-naquan-xian-tutenanda?slide=4

LeivyLeivy

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

LeivyLeivy

テスト環境の作成(ネットワーク作成)

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のセキュリティグループを適用
    • アウトバウンドは「すべてのトラフィック」

一旦区切ります

LeivyLeivy

NATゲートウェイってなに?

  • NATゲートウェイは、プライベートサブネット内のリソースがインターネットと通信するために使用されます。
  • プライベートIPアドレスをパブリックIPアドレスに変換する役割を果たします。

Elastic IPの必要性:

  • NATゲートウェイには、パブリックIPアドレスが必要です。
  • Elastic IPを使用することで、このパブリックIPアドレスが固定され、変更されません。
  • これにより、外部サービスやファイアウォールのホワイトリストなどで、一貫したIPアドレスを使用できます。

プライベートサブネットのルーティング:

  • プライベートサブネット内のリソースは、直接インターネットに接続できません。
  • インターネットにアクセスするためには、NATゲートウェイを経由する必要があります。
  • そのため、プライベートサブネットのルートテーブルで、インターネット向けのトラフィック(0.0.0.0/0)をNATゲートウェイにルーティングします。

セキュリティと管理の利点:

NATゲートウェイには、セキュリティ上の利点もあります。例えば、パブリックサブネットに配置されたNATゲートウェイは、インターネットからの未承諾トラフィックを拒否します。つまり、内部からのリクエストに対するレスポンスのみを受け入れるため、外部からの不要なアクセスからプライベートサブネットを保護することができます。

また、NATゲートウェイを利用する際は、VPC(仮想プライベートクラウド)内のトラフィックが適切にインターネットゲートウェイにルーティングされるように、ルートテーブルの設定を行う必要があります。これにより、プライベートサブネット内のリソースが、NATゲートウェイを通してインターネットと安全に通信できるようになります。

https://zenn.dev/youtuber/articles/6d656fac5013d4

プライベートサブネットのインスタンス → NATゲートウェイ(Elastic IP付き)→ インターネットゲートウェイ → インターネット

となる

つまり、NATゲートウェイにElastic IPを紐づけ、プライベートサブネットのルートに設定することで、プライベートサブネット内のリソースが安全かつ制御された方法でインターネットにアクセスできるようになります。

LeivyLeivy

テスト環境の作成(RDSのスナップショット復元)

Cloud Map の設定

  1. AWS マネジメントコンソールで Cloud Map サービスに移動します。
  2. ECSのクラスター作成時に自動生成された名前空間をクリックします。
  3. 「API 呼び出しと VPC の DNS クエリ」を選択
  4. 以下の設定を行います:
    • 名前空間名
    • インスタンスの検出: 「API 呼び出しと VPC の DNS クエリ」
    • VPC: test-vpc を選択
  5. 「名前空間の作成」をクリックして完了します。

ECSクラスターの作成

  • 今回はクローン元のプロジェクトがAWS Fargateなのでそれを選択
  • 作成した名前空間を選択
  • CloudWatch Container Insightsは有効にしておきます

サブネットグループの作成

  1. 「Amazon ElastiCache」の「設定」から「サブネットグループ」を選択して作成画面へ
  2. test用VPCを選択し、「プライベートサブネットだけを選択」して作成

RDSのスナップショット作成

  1. クラスターを選択し、アクションから「スナップショットの取得」
  2. DBクラスターを選択
  3. スナップショット名は「****-prod-snapshot-yyyymmdd」などとする
  4. スナップショットの取得をクリック

RDS のスナップショット復元

  1. 作成されたスナップショットを選択しアクションから復元を選択
  2. 基本的には本番環境と同じ設定にする
  3. 「DB クラスター識別子」はインスタンスをまとめるクラスターの名前。元の名前にtestなどを加えたものに設定
  4. 「DB インスタンス識別子」はインスタンスの名前。元の名前にtestなどを加えたものに設定
  5. 「インスタンスの設定」ではdb.t3.smallを今回選択
  6. 「可用性と耐久性」は「レプリカを作成しない」にチェック
  7. 「接続」設定
    • テスト用VPCを選択
    • 「パブリックアクセス」はなし
    • セキュリティグループでVPC用のもの
    • アベイラビリティーゾーンの指定はなし
  8. データベース認証は「パスワード認証」
LeivyLeivy

DB接続時にエラーが発生

RDS SQLSTATE[HY000] [1045] Access denied for user '****'

ユーザー名とパスワードは特に変更していないので本番のものと同じ設定をしているが、このようなエラーに遭遇してしまいました

調べてみると、DBのHOSTの値や様々な値が若干違うことに気付き修正しました
しかし依然エラーが解消されず・・・

解決方法

結論から言うと、AuroraProxySecretManagerにアクセスする際の認証で弾かれていました

具体的には認証に本番用と同じIAMロールを設定していたのですが、そのロールのポリシーで明示的に対象のSecretManagerのARNを指定していたので、テスト用に新しくロールとポリシーを作成することに

ポリシーの作成時に対象のSecretManagerのARNを明示的に指定することで解決しました

LeivyLeivy

まとめ

インフラの設定項目はセキュリティ的に公開するのも怖かったので、このスクラップには途中からエラー時のメモなどだけ残すようにしました。
中途半端になってしまいましたが、なんやかんやあり無事に目的は達成できました。

今回初めてAWSについて知り、実際の業務で扱ってみましたが、好みだったので資格の取得も検討しようとおもいます。

このスクラップは2024/08/29にクローズされました