Amazon Aurora Serverless v2
Amazon Auroraはコンピューティングとストレージを分離した構成をとるデータベースです。 そして先月に、そのServerless版の新しいバージョンである、Amazon Aurora Serverless v2がGAになりました。
ここで言う「Serverless」の意味合いですが、特段AWS Lambda等と関係しているわけではなく、 アプリケーションニーズに応じて自動的に起動や停止、スケーリングをしてくれるAuroraになります。
そのユースケースはAurora Serverless v2 の使用にまとめられています。 代表的なものは以下と言えるでしょうか。
- ワークロードが変動するアプリケーションに対するスケーリングの即応
- テナントごとにクラスターを作成することによる、マルチテナント対応の容易化 (テナントごとのデータ量の変動や、繁忙期の違いに対応しやすくなる)
- スケールの予測ができない新しいアプリケーションへの対応
- 開発やテストにおける、断続的なトラフィックへの対応
この中の2.なんていうのは、VMに乗っていたアプリケーションが細分化してコンテナに乗る流れの相似形に見え、 スケールアップが主となっていたデータベースもスケールアウトの流れに向かうのかと楽しみになります。
とにもかくにも構築してみる
まずはざっくり構築してみました。 Terraformのコードは末尾に示しますが、わかってみればそれほど悩むところはないかもしれません。 Serverlessとはいえ、RDSと多くの設定は同一か類似しています。
ハマったところ: Multi-AZ構成
Multi-AZ構成をとるという定義を、「複数AZにAuroraクラスタの中のインスタンスを配置する」とした場合、それを実現するのに苦労しました。
rds_cluster_instance
にはavailability_zone
という
パラメータがあります。これが未指定の場合、AuroraがAZをランダムに選んでくれるというのがマニュアルの記載なのですが。
Aurora automatically chooses an appropriate Availability Zone if you don't specify one.
Default: A random, system-chosen Availability Zone in the endpoint's AWS Region.
実際に複数インスタンスをavailability_zone
未指定で投入した場合、すべてがap-northeast-1a
に配置されてしまい、なかなかMulti-AZになりませんでした。
「ランダム」と言っているからには、単にぼくの運が悪いという可能性もありますが。
このため、以下のようにAZを明示的に指定するようにしています。
availability_zone = var.availability_zones[count.index % length(var.availability_zones)]
これを行うと、以下の画像のようにインスタンスが複数AZに配置される形になりました。
ハマったところ: インターネットからのアクセス
rds_cluster_instance
のpublicly_accessible
にtrue
を指定すると、Aurora Serverlessへのインターネットごしの直接アクセスが可能になります。
ただ、これは当たり前ですがAuroraが接続可能なDBサブネットをVPCの「パブリックサブネット」で構成することが条件です。
ぼくはずっと「プライベートサブネット」を組み合わせたDBサブネットとAuroraを接続していていたので、インターネットアクセスができないできないと長い間悩みました。
クエリエディタはServerless v2では接続できない
きちんと構築できているのかを簡単に試そうとして、クエリエディタを利用しようとしていました。ただ、これはServerless v2には対応していないようですね。 自分が作ったはずのクラスタが接続先の選択肢に出てこないので、それなりに焦りました。
Terraformコード
resource "aws_rds_cluster" "this" { cluster_identifier = var.name availability_zones = var.availability_zones engine_mode = "provisioned" # Serverless v2 engine = "aurora-postgresql" engine_version = var.engine_version database_name = var.database_name port = var.port master_username = var.master_username master_password = var.master_password apply_immediately = var.apply_immediately db_cluster_parameter_group_name = aws_rds_cluster_parameter_group.this.name db_subnet_group_name = aws_db_subnet_group.this.name # Major Version Up は自動的に行わない allow_major_version_upgrade = false preferred_backup_window = var.preferred_backup_window_in_utc preferred_maintenance_window = var.preffered_maintenance_window_in_utc backup_retention_period = var.backup_retention_period_in_days final_snapshot_identifier = join("", [var.name, "-", formatdate("YYYY-MM-DD-HH-mm-ss", timestamp())]) # undersocre は利用不可 copy_tags_to_snapshot = true # DB を削除可能とするか deletion_protection = var.deletion_protection # まずはログに出せるものは全て出力。問題があれば減らしていく。 # 実質的に設定できる値は "postgresql" のみ enabled_cloudwatch_logs_exports = ["postgresql"] enable_http_endpoint = true iam_database_authentication_enabled = false vpc_security_group_ids = [aws_security_group.allow_db_client.id] serverlessv2_scaling_configuration { min_capacity = var.minimum_capacity max_capacity = var.maximum_capacity } tags = { Name = var.name } lifecycle { ignore_changes = [ master_password, # 2 つの AZ しか設定しない場合であっても、AWS が 3 つを指定したことにするため、 # 必ず差分が発生してしまう # see: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rds_cluster#availability_zones availability_zones ] } } resource "aws_rds_cluster_instance" "example" { count = var.replica_number + 1 # Master インスタンスで +1 availability_zone = var.availability_zones[count.index % length(var.availability_zones)] identifier = "${var.name}-instance-${count.index}" cluster_identifier = aws_rds_cluster.this.id db_subnet_group_name = aws_rds_cluster.this.db_subnet_group_name engine = aws_rds_cluster.this.engine engine_version = aws_rds_cluster.this.engine_version apply_immediately = var.apply_immediately publicly_accessible = var.publicly_accessible # メモリはみたいので、拡張モニタリングを有効化 monitoring_role_arn = aws_iam_role.monitoring.arn monitoring_interval = 60 # CloudWatch の課金額を小さくするため最大間隔を指定 instance_class = "db.serverless" # Aurora Serverless v2 tags = { Name = "${var.name}-instance-${count.index}" } } resource "aws_db_subnet_group" "this" { subnet_ids = var.subnet_ids tags = { Name = "${var.name}-db-subnet-group" } } resource "aws_security_group" "allow_db_client" { name = "allow db client" description = "Allow From Postgresql DB Client" vpc_id = var.vpc_id tags = { Name = "${var.name} DB Client Access Rule" } } resource "aws_security_group_rule" "allow_db_client" { security_group_id = aws_security_group.allow_db_client.id type = "ingress" description = "Allow From Postgresql DB Client" protocol = "tcp" from_port = 5432 to_port = 5432 cidr_blocks = var.allow_db_access_cidr_blocks } resource "aws_rds_cluster_parameter_group" "this" { name = "aurora-parameter-group" family = "aurora-postgresql13" description = "Cluster Parameter Group" # デフォルト 1 秒だが、1 秒ごとにロック獲得待ちの tx に対してチェックするのは重い # see: https://soudai.hatenablog.com/entry/2017/12/26/080000 parameter { name = "deadlock_timeout" value = "10000" } # いわゆるスロークエリログの出力。デフォルトは -1 (off) # 1 秒以上のクエリをログ出力するよう設定 parameter { name = "log_min_duration_statement" value = "1000" } tags = { Name = "Aurora Parameter Group" } } # 拡張モニタリング用ロール # see: https://dev.classmethod.jp/articles/rds-enhanced-monitoring-manual/ resource "aws_iam_role" "monitoring" { name = "EnhancedMonitoringRole" description = "Role to enable enhanced monitoring" assume_role_policy = data.aws_iam_policy_document.assume_role_policy.json managed_policy_arns = [data.aws_iam_policy.enhanced_monitoring.arn] tags = { Name = "EnhancedMonitoringRole" } } data "aws_iam_policy" "enhanced_monitoring" { name = "AmazonRDSEnhancedMonitoringRole" } # マネジメントコンソールから作成された信頼ポリシーをそのまま記述 data "aws_iam_policy_document" "assume_role_policy" { statement { actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = ["monitoring.rds.amazonaws.com"] } } }