From 876ba5dabfced2a8fdf61a233254e6282b0ae171 Mon Sep 17 00:00:00 2001 From: dnx-solutions-ms <156617918+dnx-solutions-ms@users.noreply.github.com> Date: Wed, 27 Mar 2024 13:05:15 +1000 Subject: [PATCH 1/3] adding auto scalling feature --- _variables.tf | 74 ++++++++++++++++++++++++++++++- appautoscaling.tf | 108 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 appautoscaling.tf diff --git a/_variables.tf b/_variables.tf index e868c65..1ad3267 100644 --- a/_variables.tf +++ b/_variables.tf @@ -143,4 +143,76 @@ variable "task_role_policies_managed" { variable "task_role_policies" { default = [] description = "Custom policies to be added on the task role." -} \ No newline at end of file +} + +variable "autoscaling_cpu" { + default = false + description = "Enables autoscaling based on average CPU tracking" +} + +variable "autoscaling_memory" { + default = false + description = "Enables autoscaling based on average Memory tracking" +} + +variable "autoscaling_max" { + default = 4 + description = "Max number of containers to scale with autoscaling" +} + +variable "autoscaling_min" { + default = 1 + description = "Min number of containers to scale with autoscaling" +} + +variable "autoscaling_target_cpu" { + default = 50 + description = "Target average CPU percentage to track for autoscaling" +} + +variable "autoscaling_target_memory" { + default = 90 + description = "Target average Memory percentage to track for autoscaling" +} + +variable "autoscaling_scale_in_cooldown" { + default = 300 + description = "Cooldown in seconds to wait between scale in events" +} + +variable "autoscaling_scale_out_cooldown" { + default = 300 + description = "Cooldown in seconds to wait between scale out events" +} + +variable "enable_schedule" { + default = false + description = "Enables schedule to shut down and start up instances outside business hours." +} + +variable "autoscaling_custom" { + type = list(object({ + name = string + scale_in_cooldown = number + scale_out_cooldown = number + target_value = number + metric_name = string + namespace = string + statistic = string + })) + default = [] + description = "Set one or more app autoscaling by customized metric" +} + +variable "schedule_cron_start" { + type = string + default = "" + description = "Cron expression to define when to trigger a start of the auto-scaling group. E.g. 'cron(00 21 ? * SUN-THU *)' to start at 8am UTC time." +} + +variable "schedule_cron_stop" { + type = string + default = "" + description = "Cron expression to define when to trigger a stop of the auto-scaling group. E.g. 'cron(00 09 ? * MON-FRI *)' to start at 8am UTC time" +} + diff --git a/appautoscaling.tf b/appautoscaling.tf new file mode 100644 index 0000000..15a630d --- /dev/null +++ b/appautoscaling.tf @@ -0,0 +1,108 @@ +resource "aws_appautoscaling_target" "ecs" { + count = var.autoscaling_cpu || var.autoscaling_memory || length(var.autoscaling_custom) > 0 ? 1 : 0 + max_capacity = var.autoscaling_max + min_capacity = var.autoscaling_min + resource_id = "service/${var.cluster_name}/${aws_ecs_service.default.name}" + scalable_dimension = "ecs:service:DesiredCount" + service_namespace = "ecs" +} + +resource "aws_appautoscaling_policy" "scale_cpu" { + count = var.autoscaling_cpu ? 1 : 0 + name = "scale-cpu" + policy_type = "TargetTrackingScaling" + resource_id = aws_appautoscaling_target.ecs[0].resource_id + scalable_dimension = aws_appautoscaling_target.ecs[0].scalable_dimension + service_namespace = aws_appautoscaling_target.ecs[0].service_namespace + + target_tracking_scaling_policy_configuration { + target_value = var.autoscaling_target_cpu + disable_scale_in = false + scale_in_cooldown = var.autoscaling_scale_in_cooldown + scale_out_cooldown = var.autoscaling_scale_out_cooldown + + predefined_metric_specification { + predefined_metric_type = "ECSServiceAverageCPUUtilization" + } + } +} + +resource "aws_appautoscaling_policy" "scale_memory" { + count = var.autoscaling_memory ? 1 : 0 + name = "scale-memory" + policy_type = "TargetTrackingScaling" + resource_id = aws_appautoscaling_target.ecs[0].resource_id + scalable_dimension = aws_appautoscaling_target.ecs[0].scalable_dimension + service_namespace = aws_appautoscaling_target.ecs[0].service_namespace + + target_tracking_scaling_policy_configuration { + target_value = var.autoscaling_target_memory + disable_scale_in = false + scale_in_cooldown = var.autoscaling_scale_in_cooldown + scale_out_cooldown = var.autoscaling_scale_out_cooldown + + predefined_metric_specification { + predefined_metric_type = "ECSServiceAverageMemoryUtilization" + } + } +} + +resource "aws_appautoscaling_policy" "scale_custom" { + for_each = { for custom in var.autoscaling_custom : custom.name => custom } + + name = each.value.name + policy_type = "TargetTrackingScaling" + resource_id = aws_appautoscaling_target.ecs[0].resource_id + scalable_dimension = aws_appautoscaling_target.ecs[0].scalable_dimension + service_namespace = aws_appautoscaling_target.ecs[0].service_namespace + + target_tracking_scaling_policy_configuration { + scale_in_cooldown = each.value.scale_in_cooldown + scale_out_cooldown = each.value.scale_out_cooldown + target_value = each.value.target_value + + customized_metric_specification { + metric_name = each.value.metric_name + namespace = each.value.namespace + statistic = each.value.statistic + dimensions { + name = "ClusterName" + value = var.cluster_name + } + dimensions { + name = "ServiceName" + value = var.name + } + } + } +} + +resource "aws_appautoscaling_scheduled_action" "scale_service_out" { + count = var.enable_schedule ? 1 : 0 + name = "${var.name}-scale-out" + service_namespace = aws_appautoscaling_target.ecs[0].service_namespace + resource_id = aws_appautoscaling_target.ecs[0].resource_id + scalable_dimension = aws_appautoscaling_target.ecs[0].scalable_dimension + schedule = var.schedule_cron_stop + timezone = "UTC" + + scalable_target_action { + min_capacity = 0 + max_capacity = 0 + } +} + +resource "aws_appautoscaling_scheduled_action" "scale_service_in" { + count = var.enable_schedule ? 1 : 0 + name = "${var.name}-scale-in" + service_namespace = aws_appautoscaling_target.ecs[0].service_namespace + resource_id = aws_appautoscaling_target.ecs[0].resource_id + scalable_dimension = aws_appautoscaling_target.ecs[0].scalable_dimension + schedule = var.schedule_cron_start + timezone = "UTC" + + scalable_target_action { + min_capacity = var.autoscaling_min + max_capacity = var.autoscaling_max + } +} \ No newline at end of file From 0c927fbbae8af7692637f2dd4cb9518825e21cb1 Mon Sep 17 00:00:00 2001 From: dnx-solutions-ms <156617918+dnx-solutions-ms@users.noreply.github.com> Date: Wed, 27 Mar 2024 13:11:17 +1000 Subject: [PATCH 2/3] README update --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 90cd5e8..7400c06 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ In addition you have the option to create or not : | deployment\_maximum\_percent | Deployment maximum percentage | `string` | `"100"` | no | | deployment\_minimum\_healthy\_percent | Deployment minumum health percentage | `string` | `"0"` | no | | desired\_count | Number of containers (tasks) to run | `number` | `1` | no | +| enable\_schedule | Enables schedule to shut down and start up instances outside business hours. | `bool` | `false` | no | | fargate\_spot | Set true to use FARGATE\_SPOT capacity provider by default (only when launch\_type=FARGATE) | `bool` | `false` | no | | image | Docker image to deploy (can be a placeholder) | `string` | `"dnxsolutions/nginx-hello:latest"` | no | | launch\_type | The launch type on which to run your service. The valid values are EC2 and FARGATE. Defaults to EC2. | `string` | `"EC2"` | no | @@ -70,6 +71,8 @@ In addition you have the option to create or not : | network\_mode | The Docker networking mode to use for the containers in the task. The valid values are none, bridge, awsvpc, and host. (REQUIRED IF 'LAUCH\_TYPE' IS FARGATE) | `any` | `null` | no | | ordered\_placement\_strategy | Service level strategy rules that are taken into consideration during task placement. List from top to bottom in order of precedence. The maximum number of ordered\_placement\_strategy blocks is 5. |
list(object({| `[]` | no | | placement\_constraints | Rules that are taken into consideration during task placement. Maximum number of placement\_constraints is 10. |
field = string
expression = string
}))
list(object({| `[]` | no | +| schedule\_cron\_start | Cron expression to define when to trigger a start of the auto-scaling group. E.g. '0 20 \* \* \*' to start at 8pm GMT time. | `string` | `""` | no | +| schedule\_cron\_stop | Cron expression to define when to trigger a stop of the auto-scaling group. E.g. '0 10 \* \* \*' to stop at 10am GMT time. | `string` | `""` | no | | security\_groups | The security groups associated with the task or service | `any` | `null` | no | | subnets | The subnets associated with the task or service. (REQUIRED IF 'LAUCH\_TYPE' IS FARGATE) | `any` | `null` | no | | task\_role\_policies | Custom policies to be added on the task role. | `list` | `[]` | no | From 5d184c4f3e04dc661efa432b5620ded936db0f24 Mon Sep 17 00:00:00 2001 From: olialvesrobson
type = string
expression = string
}))
list(object({| `[]` | no | +| autoscaling\_max | Max number of containers to scale with autoscaling | `number` | `4` | no | +| autoscaling\_memory | Enables autoscaling based on average Memory tracking | `bool` | `false` | no | +| autoscaling\_min | Min number of containers to scale with autoscaling | `number` | `1` | no | +| autoscaling\_scale\_in\_cooldown | Cooldown in seconds to wait between scale in events | `number` | `300` | no | +| autoscaling\_scale\_out\_cooldown | Cooldown in seconds to wait between scale out events | `number` | `300` | no | +| autoscaling\_target\_cpu | Target average CPU percentage to track for autoscaling | `number` | `50` | no | +| autoscaling\_target\_memory | Target average Memory percentage to track for autoscaling | `number` | `90` | no | | cloudwatch\_logs\_export | Whether to mark the log group to export to an S3 bucket (needs terraform-aws-log-exporter to be deployed in the account/region) | `bool` | `false` | no | | cloudwatch\_logs\_retention | Specifies the number of days you want to retain log events in the specified log group. Possible values are: 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, and 3653. | `number` | `120` | no | | cluster\_name | n/a | `string` | `"Name of existing ECS Cluster to deploy this app to"` | no | @@ -71,8 +80,8 @@ In addition you have the option to create or not : | network\_mode | The Docker networking mode to use for the containers in the task. The valid values are none, bridge, awsvpc, and host. (REQUIRED IF 'LAUCH\_TYPE' IS FARGATE) | `any` | `null` | no | | ordered\_placement\_strategy | Service level strategy rules that are taken into consideration during task placement. List from top to bottom in order of precedence. The maximum number of ordered\_placement\_strategy blocks is 5. |
name = string
scale_in_cooldown = number
scale_out_cooldown = number
target_value = number
metric_name = string
namespace = string
statistic = string
}))
list(object({| `[]` | no | | placement\_constraints | Rules that are taken into consideration during task placement. Maximum number of placement\_constraints is 10. |
field = string
expression = string
}))
list(object({| `[]` | no | -| schedule\_cron\_start | Cron expression to define when to trigger a start of the auto-scaling group. E.g. '0 20 \* \* \*' to start at 8pm GMT time. | `string` | `""` | no | -| schedule\_cron\_stop | Cron expression to define when to trigger a stop of the auto-scaling group. E.g. '0 10 \* \* \*' to stop at 10am GMT time. | `string` | `""` | no | +| schedule\_cron\_start | Cron expression to define when to trigger a start of the auto-scaling group. E.g. 'cron(00 21 ? \* SUN-THU \*)' to start at 8am UTC time. | `string` | `""` | no | +| schedule\_cron\_stop | Cron expression to define when to trigger a stop of the auto-scaling group. E.g. 'cron(00 09 ? \* MON-FRI \*)' to start at 8am UTC time | `string` | `""` | no | | security\_groups | The security groups associated with the task or service | `any` | `null` | no | | subnets | The subnets associated with the task or service. (REQUIRED IF 'LAUCH\_TYPE' IS FARGATE) | `any` | `null` | no | | task\_role\_policies | Custom policies to be added on the task role. | `list` | `[]` | no |
type = string
expression = string
}))