From 46a51de3e1c8df4854aefad4c585c7fc7ac9b538 Mon Sep 17 00:00:00 2001 From: Riccardo Massullo Date: Fri, 17 Oct 2025 14:45:16 +0200 Subject: [PATCH] Added the ability to specify custom metrics for autoscaling and added a couple of examples --- examples/autoscaling/main.tf | 105 +++++++++++++++++++++++++++++++++++ main.tf | 42 +++++++++++++- variables.tf | 22 +++++++- 3 files changed, 166 insertions(+), 3 deletions(-) create mode 100644 examples/autoscaling/main.tf diff --git a/examples/autoscaling/main.tf b/examples/autoscaling/main.tf new file mode 100644 index 0000000..e82cff2 --- /dev/null +++ b/examples/autoscaling/main.tf @@ -0,0 +1,105 @@ +provider "aws" { + region = "us-west-1" +} + +module "rds_cluster_autoscaling_mysql_predefined" { + source = "../../" + name = "autoscaling_predefined_metrics" + engine = "aurora-mysql" + cluster_family = "aurora-mysql8.0" + cluster_size = 2 + namespace = "eg" + stage = "dev" + admin_user = "admin1" + admin_password = "Test123456789" + db_name = "dbname" + db_port = 5432 + instance_type = "db.r4.large" + vpc_id = "vpc-xxxxxxxx" + security_groups = ["sg-xxxxxxxx"] + subnets = ["subnet-xxxxxxxx", "subnet-xxxxxxxx"] + zone_id = "Zxxxxxxxx" + + autoscaling_enabled = true + autoscaling_scale_in_cooldown = 300 + autoscaling_scale_out_cooldown = 300 + autoscaling_min_capacity = 1 + autoscaling_max_capacity = 3 + autoscaling_policy_type = "TargetTrackingScaling" + autoscaling_target_metrics = "RDSReaderAverageCPUUtilization" // or "RDSReaderAverageDatabaseConnections" +} + +module "rds_cluster_autoscaling_mysql_custom_cluster_level" { + source = "../../" + name = "autoscaling_custom_metrics_cluster" + engine = "aurora-mysql" + cluster_family = "aurora-mysql8.0" + cluster_size = 2 + namespace = "eg" + stage = "dev" + admin_user = "admin1" + admin_password = "Test123456789" + db_name = "dbname" + db_port = 5432 + instance_type = "db.r4.large" + vpc_id = "vpc-xxxxxxxx" + security_groups = ["sg-xxxxxxxx"] + subnets = ["subnet-xxxxxxxx", "subnet-xxxxxxxx"] + zone_id = "Zxxxxxxxx" + + autoscaling_enabled = true + autoscaling_scale_in_cooldown = 300 + autoscaling_scale_out_cooldown = 300 + autoscaling_min_capacity = 1 + autoscaling_max_capacity = 3 + autoscaling_policy_type = "TargetTrackingScaling" + autoscaling_target_metrics = "Custom" + autoscaling_target_value = 60 + autoscaling_custom_metric = { + namespace = "AWS/RDS" + metric_name = "AuroraReplicaLagMaximum" # any valid Aurora metric under AWS/RDS + statistic = "Average" + # unit = "Milliseconds" # optional, if you want to pin it + dimensions = [ + { name = "DBClusterIdentifier", value = "my-aurora-cluster" } + ] + } +} + +# WARNING: Edge case for which i could not find a real use yet. +module "rds_cluster_autoscaling_mysql_custom_instances_level" { + source = "../../" + name = "autoscaling_custom_metrics_instances" + engine = "aurora-mysql" + cluster_family = "aurora-mysql8.0" + cluster_size = 2 + namespace = "eg" + stage = "dev" + admin_user = "admin1" + admin_password = "Test123456789" + db_name = "dbname" + db_port = 5432 + instance_type = "db.r4.large" + vpc_id = "vpc-xxxxxxxx" + security_groups = ["sg-xxxxxxxx"] + subnets = ["subnet-xxxxxxxx", "subnet-xxxxxxxx"] + zone_id = "Zxxxxxxxx" + + autoscaling_enabled = true + autoscaling_scale_in_cooldown = 300 + autoscaling_scale_out_cooldown = 300 + autoscaling_min_capacity = 1 + autoscaling_max_capacity = 3 + autoscaling_policy_type = "TargetTrackingScaling" + autoscaling_target_metrics = "Custom" + autoscaling_target_value = 60 + autoscaling_custom_metric = { + namespace = "AWS/RDS" + metric_name = "CPUUtilization" # any valid Aurora metric under AWS/RDS + statistic = "Average" + # unit = "Percent" # optional, if you want to pin it + dimensions = [ + { name = "DBInstanceIdentifier", value = "my-aurora-reader-1" } + ] + } +} \ No newline at end of file diff --git a/main.tf b/main.tf index 7a20e15..995dfdf 100644 --- a/main.tf +++ b/main.tf @@ -15,6 +15,11 @@ locals { ignore_admin_credentials = var.replication_source_identifier != "" || var.snapshot_identifier != null reserved_instance_engine = var.engine use_reserved_instances = var.use_reserved_instances && !local.is_serverless + + use_predefined_metric = contains([ + "RDSReaderAverageCPUUtilization", + "RDSReaderAverageDatabaseConnections", + ], var.autoscaling_target_metrics) } data "aws_partition" "current" { @@ -492,8 +497,9 @@ resource "aws_appautoscaling_target" "replicas" { max_capacity = var.autoscaling_max_capacity } -resource "aws_appautoscaling_policy" "replicas" { - count = local.enabled && var.autoscaling_enabled ? 1 : 0 +# --- Predefined metric policy (only when using the two reader metrics) --- +resource "aws_appautoscaling_policy" "replicas_predefined" { + count = (local.enabled && var.autoscaling_enabled && local.use_predefined_metric) ? 1 : 0 name = module.this.id service_namespace = join("", aws_appautoscaling_target.replicas[*].service_namespace) scalable_dimension = join("", aws_appautoscaling_target.replicas[*].scalable_dimension) @@ -501,14 +507,46 @@ resource "aws_appautoscaling_policy" "replicas" { policy_type = var.autoscaling_policy_type target_tracking_scaling_policy_configuration { + disable_scale_in = false + target_value = var.autoscaling_target_value + scale_in_cooldown = var.autoscaling_scale_in_cooldown + scale_out_cooldown = var.autoscaling_scale_out_cooldown + predefined_metric_specification { predefined_metric_type = var.autoscaling_target_metrics } + } +} +# --- Customized metric policy (everything else) --- +resource "aws_appautoscaling_policy" "replicas_custom" { + count = (local.enabled && var.autoscaling_enabled && !local.use_predefined_metric) ? 1 : 0 + name = module.this.id + service_namespace = join("", aws_appautoscaling_target.replicas[*].service_namespace) + scalable_dimension = join("", aws_appautoscaling_target.replicas[*].scalable_dimension) + resource_id = join("", aws_appautoscaling_target.replicas[*].resource_id) + policy_type = var.autoscaling_policy_type + + target_tracking_scaling_policy_configuration { disable_scale_in = false target_value = var.autoscaling_target_value scale_in_cooldown = var.autoscaling_scale_in_cooldown scale_out_cooldown = var.autoscaling_scale_out_cooldown + + customized_metric_specification { + metric_name = var.autoscaling_custom_metric.metric_name + namespace = var.autoscaling_custom_metric.namespace + statistic = var.autoscaling_custom_metric.statistic + unit = try(var.autoscaling_custom_metric.unit, null) + + dynamic "dimensions" { + for_each = try(var.autoscaling_custom_metric.dimensions, []) + content { + name = dimensions.value.name + value = dimensions.value.value + } + } + } } } diff --git a/variables.tf b/variables.tf index 3fe408e..ad32bec 100644 --- a/variables.tf +++ b/variables.tf @@ -403,7 +403,27 @@ variable "autoscaling_policy_type" { variable "autoscaling_target_metrics" { type = string default = "RDSReaderAverageCPUUtilization" - description = "The metrics type to use. If this value isn't provided the default is CPU utilization" + description = "The metrics type to use. If this value isn't provided the default is CPU utilization. IF the value is not `RDSReaderAverageCPUUtilization` nor `RDSReaderAverageDatabaseConnections` a custom metric for autoscaling must be specified" +} + +variable "autoscaling_custom_metric" { + type = object({ + metric_name = string + namespace = string + statistic = string + unit = optional(string) + dimensions = optional(list(object({ + name = string, + value = string + }))) + }) + default = { + metric_name = "" + namespace = "" + statistic = "Average" + dimensions = [] + } + description = "Specification for a custom Autoscaling metric to use. Will be used if autoscaling_target_metrics os neither `RDSReaderAverageCPUUtilization` nor `RDSReaderAverageDatabaseConnections`" } variable "autoscaling_target_value" {