From c9c5483fd0f810763c2396d2dcdf512543cf3deb Mon Sep 17 00:00:00 2001 From: Kieran Brown Date: Tue, 23 Jan 2024 12:45:37 +0000 Subject: [PATCH 1/3] Improve spot HA by utilising ASG capacity rebalance --- asg.tf | 37 ++++++++++++++++++++++++++++++++++--- ec2.tf | 16 ++++++++-------- variables.tf | 6 ++++++ 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/asg.tf b/asg.tf index ba0724a..29f24c4 100644 --- a/asg.tf +++ b/asg.tf @@ -8,9 +8,31 @@ resource "aws_autoscaling_group" "main" { health_check_type = "EC2" vpc_zone_identifier = [var.subnet_id] - launch_template { - id = aws_launch_template.main.id - version = "$Latest" + capacity_rebalance = var.use_spot_instances + + mixed_instances_policy { + instances_distribution { + on_demand_percentage_above_base_capacity = var.use_spot_instances ? 0 : 100 + spot_allocation_strategy = "price-capacity-optimized" + } + launch_template { + launch_template_specification { + launch_template_id = aws_launch_template.main.id + version = "$Latest" + } + + override { + instance_type = var.instance_type + } + + dynamic "override" { + for_each = toset(var.ha_additional_instance_types) + + content { + instance_type = override.value + } + } + } } tag { @@ -33,3 +55,12 @@ resource "aws_autoscaling_group" "main" { delete = "15m" } } + +resource "aws_autoscaling_lifecycle_hook" "spot_termination_wait" { + count = var.ha_mode && var.use_spot_instances ? 1 : 0 + + name = "TerminationWait" + autoscaling_group_name = aws_autoscaling_group.main[0].name + heartbeat_timeout = 300 + lifecycle_transition = "autoscaling:EC2_INSTANCE_TERMINATING" +} diff --git a/ec2.tf b/ec2.tf index 24c21cd..95508a0 100644 --- a/ec2.tf +++ b/ec2.tf @@ -58,14 +58,6 @@ resource "aws_launch_template" "main" { security_groups = local.security_groups } - dynamic "instance_market_options" { - for_each = var.use_spot_instances ? ["x"] : [] - - content { - market_type = "spot" - } - } - dynamic "tag_specifications" { for_each = ["instance", "network-interface", "volume"] @@ -96,6 +88,14 @@ resource "aws_instance" "main" { version = "$Latest" } + dynamic "instance_market_options" { + for_each = var.use_spot_instances ? ["x"] : [] + + content { + market_type = "spot" + } + } + tags = var.tags lifecycle { diff --git a/variables.tf b/variables.tf index 8d01029..b607554 100644 --- a/variables.tf +++ b/variables.tf @@ -55,6 +55,12 @@ variable "ha_mode" { default = true } +variable "ha_additional_instance_types" { + description = "Additional instance types used by autoscaling rebalancing when the primary instance is unavailable" + type = list(string) + default = ["t4g.small"] +} + variable "instance_type" { description = "Instance type to use for the NAT instance" type = string From a87c17d4f28ad3f00ee3ba19a858ac97bd90acc0 Mon Sep 17 00:00:00 2001 From: Kieran Brown Date: Tue, 23 Jan 2024 14:14:59 +0000 Subject: [PATCH 2/3] Fix instance not recreating when switching lifecycle --- asg.tf | 2 +- ec2.tf | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/asg.tf b/asg.tf index 29f24c4..3afe913 100644 --- a/asg.tf +++ b/asg.tf @@ -18,7 +18,7 @@ resource "aws_autoscaling_group" "main" { launch_template { launch_template_specification { launch_template_id = aws_launch_template.main.id - version = "$Latest" + version = aws_launch_template.main.latest_version } override { diff --git a/ec2.tf b/ec2.tf index 95508a0..c95ba52 100644 --- a/ec2.tf +++ b/ec2.tf @@ -58,6 +58,14 @@ resource "aws_launch_template" "main" { security_groups = local.security_groups } + dynamic "instance_market_options" { + for_each = var.use_spot_instances && !var.ha_mode ? ["x"] : [] + + content { + market_type = "spot" + } + } + dynamic "tag_specifications" { for_each = ["instance", "network-interface", "volume"] @@ -85,15 +93,7 @@ resource "aws_instance" "main" { launch_template { id = aws_launch_template.main.id - version = "$Latest" - } - - dynamic "instance_market_options" { - for_each = var.use_spot_instances ? ["x"] : [] - - content { - market_type = "spot" - } + version = aws_launch_template.main.latest_version } tags = var.tags From 3f52571496d4ddbdc7c47d5e3e09bf1ce0c39bc8 Mon Sep 17 00:00:00 2001 From: Kieran Brown Date: Mon, 29 Jan 2024 09:16:40 +0000 Subject: [PATCH 3/3] Update docs --- README.md | 4 +++- examples/full/README.md | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 79f03e5..f0ad094 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ module "fck-nat" { | Name | Type | |------|------| | [aws_autoscaling_group.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/autoscaling_group) | resource | +| [aws_autoscaling_lifecycle_hook.spot_termination_wait](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/autoscaling_lifecycle_hook) | resource | | [aws_iam_instance_profile.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource | | [aws_iam_role.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_instance.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource | @@ -77,6 +78,7 @@ module "fck-nat" { | [ebs\_root\_volume\_size](#input\_ebs\_root\_volume\_size) | Size of the EBS root volume in GB | `number` | `2` | no | | [eip\_allocation\_ids](#input\_eip\_allocation\_ids) | EIP allocation IDs to use for the NAT instance. Automatically assign a public IP if none is provided. Note: Currently only supports at most one EIP allocation. | `list(string)` | `[]` | no | | [encryption](#input\_encryption) | Whether or not to encrypt the EBS volume | `bool` | `true` | no | +| [ha\_additional\_instance\_types](#input\_ha\_additional\_instance\_types) | Additional instance types used by autoscaling rebalancing when the primary instance is unavailable | `list(string)` |
[
"t4g.small"
]
| no | | [ha\_mode](#input\_ha\_mode) | Whether or not high-availability mode should be enabled via autoscaling group | `bool` | `true` | no | | [instance\_type](#input\_instance\_type) | Instance type to use for the NAT instance | `string` | `"t4g.micro"` | no | | [kms\_key\_id](#input\_kms\_key\_id) | Will use the provided KMS key ID to encrypt the EBS volume. Uses the default KMS key if none provided | `string` | `null` | no | @@ -113,4 +115,4 @@ module "fck-nat" { | [security\_group\_id](#output\_security\_group\_id) | Deprecated. The ID of the security group used by fck-nat ENIs | | [security\_group\_ids](#output\_security\_group\_ids) | List of security group IDs used by fck-nat ENIs | | [subnet\_id](#output\_subnet\_id) | Subnet ID to which the fck-nat instance is deployed into | -| [vpc\_id](#output\_vpc\_id) | VPC ID to which the fck-nat instance is deployed into | \ No newline at end of file +| [vpc\_id](#output\_vpc\_id) | VPC ID to which the fck-nat instance is deployed into | diff --git a/examples/full/README.md b/examples/full/README.md index 31f7cdf..7be5855 100644 --- a/examples/full/README.md +++ b/examples/full/README.md @@ -29,7 +29,7 @@ $ terraform apply | Name | Source | Version | |------|--------|---------| -| [fck-nat](#module\_fck-nat) | ../ | n/a | +| [fck-nat](#module\_fck-nat) | ../../ | n/a | ## Resources