【Aurora】【Terraform】Secret Manager でマスターユーザーのパスワードを管理する環境で、Aurora の Blue/Green デプロイを実施する

記事タイトルとURLをコピーする

こんにちは😺 カスタマーサクセス部の山本です。

AWS Summit Tokyo の「Amazon Aurora の技術とイノベーションDeep dive」というセッションで、Aurora の Blue/Green デプロイメントについて学びました。

参考リンク:Amazon Aurora の技術とイノベーションDeep dive

実際に試してみたところ、以下の制約に引っかかってしまいました。

ブルー/グリーンデプロイでは、AWS Secrets Manager を使用したマスターユーザーのパスワード管理はサポートされていません。

参考:Aurora 用 Amazon RDS ブルー/グリーンデプロイの概要

この制約は、ブルー環境で Secrets Manager を使用して管理しているマスターユーザーのパスワードを、グリーン環境には引き継げないという意味だと考えました。 そこで、一度自身で作成したパスワードで マスターユーザーのパスワードを管理するようにクラスターを変更し、Blue/Green デプロイを実施することにしました。デプロイ完了後に再び AWS Secrets Manager を使用したパスワード管理に戻しました。
もし、デプロイ後にマスターユーザーのパスワードが変わることでアプリケーションに問題が発生する場合、この手順は参考にならないかもしれません。 ただし、アプリケーションでマスターユーザーを使用することは推奨されていないため、そのようなケースは稀だと思います。

アプリケーションではマスターユーザーを直接使用しないことを強くお勧めします。代わりに、アプリケーションに必要な最小の特権で作成されたデータベースユーザーを使用するというベストプラクティスに従ってください。

参考:マスターユーザーアカウント権限 - Amazon Aurora

目次です。

検証シナリオ

Terraform を使用して Aurora Postgres Serverless v2 を作成しています。
切り替え後に、ブルー環境のクラスターを削除して Terraform の管理から外し、グリーン環境のクラスターを Terraform にインポートします。
ブルー環境は Aurora Postgres v15 で作成しています。
グリーン環境作成時に v16 にアップグレードします。

ブルー環境の Aurora クラスター作成

  • Terraform バージョン

    • Terraform v1.9.3 on darwin_arm64
  • AWS Provider

    • 5.60.0

  • provider.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.60.0"
    }
  }
}

provider "aws" {
  region = "ap-northeast-1"
}
  • rds.tf
    • vpc_security_group_idssubnet_ids には検証環境に作成済みの実際のリソース ID を入れて実行しました。
    • パラメータグループは、v15 と v16 のもの、両方を作成しています。
    • Blue/Green デプロイをするため、クラスターパラメータグループでは、rds.logical_replication を 1 に設定しています。
resource "aws_rds_cluster" "test" {
  cluster_identifier              = "provisioning-rds"
  db_subnet_group_name            = aws_db_subnet_group.test.name
  db_cluster_parameter_group_name = aws_rds_cluster_parameter_group.test15.name
  engine_version                  = "15.4"
  engine                          = "aurora-postgresql"
  engine_mode                     = "provisioned"
  enable_http_endpoint            = true
  iam_roles                       = []
  master_username                 = "postgres"
  manage_master_user_password     = true
  enable_global_write_forwarding  = false
  network_type = "IPV4"
  serverlessv2_scaling_configuration {
    max_capacity = 16
    min_capacity = 2
  }
  vpc_security_group_ids = ["sg-xxxxx"]
  skip_final_snapshot = true
}
resource "aws_rds_cluster_instance" "test" {
  cluster_identifier      = aws_rds_cluster.test.cluster_identifier
  db_subnet_group_name    = aws_rds_cluster.test.db_subnet_group_name
  db_parameter_group_name = aws_db_parameter_group.test15.name
  engine                  = aws_rds_cluster.test.engine
  engine_version          = aws_rds_cluster.test.engine_version
  identifier              = "rds-1"
  instance_class          = "db.serverless"
}
resource "aws_db_subnet_group" "test" {
  name       = "test_db_group"
  subnet_ids = ["subnet-xxxxx","subnet-xxxxx"]
}
resource "aws_rds_cluster_parameter_group" "test15" {
  name        = "test-aurora-postgresql15-cluster"
  family      = "aurora-postgresql15"
  description = "for aurora-postgresql15"
  parameter {
    name  = "rds.logical_replication"
    value = "1"
    apply_method = "pending-reboot"
  }
}
resource "aws_db_parameter_group" "test15" {
  name        = "test-aurora-postgresql15-instance"
  family      = "aurora-postgresql15"
  description = "for aurora-postgresql15"
}
resource "aws_rds_cluster_parameter_group" "test16" {
  name        = "test-aurora-postgresql16-cluster"
  family      = "aurora-postgresql16"
  description = "for aurora-postgresql16"
  parameter {
    name  = "rds.logical_replication"
    value = "1"
    apply_method = "pending-reboot"
  }
}
resource "aws_db_parameter_group" "test16" {
  name        = "test-aurora-postgresql16-instance"
  family      = "aurora-postgresql16"
  description = "for aurora-postgresql16"
}
resource "aws_ssm_parameter" "db_endpoint" {
  name = "jdbc_url"
  type = "String"
  value = aws_rds_cluster.test.endpoint
}
resource "aws_ssm_parameter" "reference_test" {
  name  = "reference"
  type  = "String"
  value = aws_ssm_parameter.db_endpoint.value
}

Blue/Green デプロイでは、AWS Secrets Manager を使用したマスターユーザーのパスワード管理はサポートされていない

Terraform で作成した Aurora クラスターで Blue/Green デプロイを実行してみましょう。 実際に、Secrets Manager を使用してマスターユーザーを管理している状態では、Blue/Green デプロイを実行できません。

ブルー環境のクラスターで、マスターユーザーのパスワードを自身で作成したパスワードに変更する

クラスターを変更します。
セルフマネージドにチェックを入れます。

任意のパスワードを設定します。既存の Secrets Manager から取得したものを設定し、現在のパスワードと同じにしました。

「すぐに適用」にチェックを入れ、更新します。

変更中になります。

変更が完了しました。

Blue/Green デプロイを実施する

Blue/Green デプロイを実施します。

同意します。

Terraform で作成したパラメータグループを指定します。

「ステージング環境の作成」を押します。

プロビジョニングしています。

v15 から v16 にアップデートしています。

「ステージング環境の作成」を押してから 30 分ほどで、切り替え可能な状態になりました。

切り替え前にグリーン環境を使用して、アプリケーションのテストを十分に実施します。
ブルー環境からグリーン環境へのレプリケーションとの競合が発生しないよう、グリーン環境では可能な限り読み取り操作のみテストします。
その後、切り替えます。

切り替えが完了しました。

EC2 から psql で接続していたセッションでは、一回コネクションが終了し、再接続しました。

新しいクラスターでマスターユーザーのパスワードを Secret Manager で管理するように変更する

新しいクラスターでマスターユーザーのパスワードを Secret Manager で管理するように変更します。 ここで、マスターユーザーのパスワードが変わります。

変更が完了しました。

ブルー環境のクラスターを削除する

まず、Blue/Green デプロイを削除します。

次に、古くなり未使用のブルー環境のインスタンスを削除します。

古くなり未使用のブルー環境のクラスターを削除します。

切り替えたグリーン環境のクラスターを Terraform にインポートする

state ファイルから旧クラスター・インスタンスと terraform のリソースの関連付けを削除

state ファイルでは旧クラスター・インスタンスと terraform のリソースが関連づいているため、削除します。

terraform state rm aws_rds_cluster.test

terraform state rm aws_rds_cluster_instance.test

rds.tf の編集

新しいクラスターの設定内容に合わせ、以下の編集をしました。

  • engine_version
    • 15.4 から 16.2 に変更
  • db_cluster_parameter_group_name
    • 15 を 16 に変更
  • db_parameter_group_name
    • 15 を 16 に変更
  • v15 用のクラスター・DBパラメータグループのリソースを削除
resource "aws_rds_cluster" "test" {
  cluster_identifier              = "provisioning-rds"
  db_subnet_group_name            = aws_db_subnet_group.test.name
  db_cluster_parameter_group_name = aws_rds_cluster_parameter_group.test16.name
  engine_version                  = "16.2"
  engine                          = "aurora-postgresql"
  engine_mode                     = "provisioned"
  enable_http_endpoint            = true
  iam_roles                       = []
  master_username                 = "postgres"
  manage_master_user_password     = true
  enable_global_write_forwarding  = false
  network_type = "IPV4"
  serverlessv2_scaling_configuration {
    max_capacity = 16
    min_capacity = 2
  }
  vpc_security_group_ids = ["sg-xxxxxx"]
  skip_final_snapshot = true
}
resource "aws_rds_cluster_instance" "test" {
  cluster_identifier      = aws_rds_cluster.test.cluster_identifier
  db_subnet_group_name    = aws_rds_cluster.test.db_subnet_group_name
  db_parameter_group_name = aws_db_parameter_group.test16.name
  engine                  = aws_rds_cluster.test.engine
  engine_version          = aws_rds_cluster.test.engine_version
  identifier              = "rds-1"
  instance_class          = "db.serverless"
}
resource "aws_db_subnet_group" "test" {
  name       = "test_db_group"
  subnet_ids = ["subnet-xxxxx","subnet-xxxxx"]
}
resource "aws_rds_cluster_parameter_group" "test16" {
  name        = "test-aurora-postgresql16-cluster"
  family      = "aurora-postgresql16"
  description = "for aurora-postgresql16"
  parameter {
    name  = "rds.logical_replication"
    value = "1"
    apply_method = "pending-reboot"
  }
}
resource "aws_db_parameter_group" "test16" {
  name        = "test-aurora-postgresql16-instance"
  family      = "aurora-postgresql16"
  description = "for aurora-postgresql16"
}
resource "aws_ssm_parameter" "db_endpoint" {
  name = "jdbc_url"
  type = "String"
  value = aws_rds_cluster.test.endpoint
}
resource "aws_ssm_parameter" "reference_test" {
  name  = "reference"
  type  = "String"
  value = aws_ssm_parameter.db_endpoint.value
}

import.tf の作成

新しいクラスターをインポートする tf を作成します。

import {
  to = aws_rds_cluster.test
  id = "provisioning-rds"
}
import {
  to = aws_rds_cluster_instance.test
  id = "rds-1"
}

terraform applyし切り替えたグリーン環境のクラスターを Terraform にインポートする

terraform apply し、インポートします。

v15 用のパラメータグループ2つが destroy となり、新クラスター・インスタンスがインポートされます。
1 件の change は enable_global_write_forwarding に関する既知の事象でした。詳細は以下の記事を確認ください。
【Terraform】Aurora クラスターをリストアする - サーバーワークスエンジニアブログ

無事、完了しました。
最後に、今後の apply では import.tf は不要になるため、削除しました。

まとめ

Secret Manager でマスターユーザーのパスワードを管理する環境で、Aurora の Blue/Green デプロイを実施しました。
ひと手間かかるものの、Blue/Green デプロイの恩恵を受けることができます。

最後に、Blue/Green デプロイには様々な制約がございますので、以下のドキュメントは十分にご参照ください。
Aurora 用 Amazon RDS ブルー/グリーンデプロイの概要 - Amazon Aurora

余談

一度はやってみたかった、八ヶ岳の南北縦走をしてきました。
山梨県の小淵沢から、長野県の蓼科山までの 42 km 、ひたすら山を走りました。
標高 1000m の道の駅から、最高峰である標高 2899m の赤岳まで登り上げ、その後も最後までアップダウンが続く厳しいコースでした。 岩場が多く、かなり神経を使いました。 16 時間も走ったので、その後のお風呂も気持ちよく、夜もよく眠れていい休日でした。 なかなか達成感もあります。

赤岳

南アルプス

登山を楽しむ人達

天狗岳

蓼科山

山本 哲也 (記事一覧)

カスタマーサクセス部のエンジニア。2024 Japan AWS Top Engineers に選んでもらいました。

今年の目標は Advanced Networking – Specialty と Machine Learning - Specialty を取得することです。

山を走るのが趣味です。今年の目標は 100 km と 100 mile を完走することです。 100 km は Gran Trail みなかみで完走しました。残すは OSJ koumi 100 で 100 mile 走ります。実際には 175 km らしいです。「草 100 km / mile」 もたまに企画します。

基本的にのんびりした性格です。座右の銘は「いつか着く」