Skip to content

Commit

Permalink
Add support for deploying spot ecs services (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolaei authored Nov 10, 2023
2 parents bc72169 + 0a9539d commit f5c39d8
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 9 deletions.
3 changes: 1 addition & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ orbs:
# Preferably these would be a CircleCI parameter,
# but parameters doesn't support lists or maps :(
terraform-versions: &terraform-versions
- 1.3.7
- 1.4.4

example-folders: &example-folders
- examples/simple
Expand All @@ -30,4 +30,3 @@ workflows:
parameters:
tag: *terraform-versions
path: *example-folders

49 changes: 44 additions & 5 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,10 @@ locals {
extra_options = try(container.extra_options, {})
}
]
capacity_provider_strategy = {
capacity_provider = "FARGATE_SPOT"
weight = 1
}
}

data "aws_region" "current" {}
Expand Down Expand Up @@ -353,6 +357,16 @@ resource "aws_ecs_task_definition" "task" {
network_mode = var.launch_type == "EXTERNAL" ? "bridge" : "awsvpc"
}

# Service preconditions to ensure that the user doesn't try combinations we want to avoid.
resource "terraform_data" "no_launch_type_and_spot" {
lifecycle {
precondition {
condition = !var.use_spot || var.launch_type == "FARGATE"
error_message = "use_spot and launch_type are mutually exclusive"
}
}
}


# When autoscaling is enabled, we have to ignore changes to the desired count.
# This is because the autoscaling group will manage the desired count.
Expand All @@ -362,13 +376,14 @@ resource "aws_ecs_task_definition" "task" {
# using desired count.

resource "aws_ecs_service" "service" {
count = var.autoscaling == null ? 1 : 0
count = var.autoscaling == null ? 1 : 0
depends_on = [terraform_data.no_launch_type_and_spot]

name = var.application_name
cluster = var.cluster_id
task_definition = aws_ecs_task_definition.task.arn
desired_count = var.desired_count
launch_type = var.launch_type
launch_type = var.use_spot ? null : var.launch_type
deployment_minimum_healthy_percent = var.deployment_minimum_healthy_percent
deployment_maximum_percent = var.deployment_maximum_percent
health_check_grace_period_seconds = var.launch_type == "EXTERNAL" ? null : var.health_check_grace_period_seconds
Expand All @@ -377,7 +392,6 @@ resource "aws_ecs_service" "service" {

# ECS Anywhere doesn't support VPC networking or load balancers.
# Because of this, we need to make these resources dynamic!

dynamic "network_configuration" {
for_each = var.launch_type == "EXTERNAL" ? [] : [0]

Expand All @@ -397,16 +411,28 @@ resource "aws_ecs_service" "service" {
target_group_arn = aws_lb_target_group.service[load_balancer.key].arn
}
}

# We set the service as a spot service through setting up the capacity_provider_strategy.
# Requires a cluster with 'FARGATE_SPOT' capacity provider enabled.
dynamic "capacity_provider_strategy" {
for_each = var.use_spot ? [local.capacity_provider_strategy] : []

content {
capacity_provider = capacity_provider_strategy.value.capacity_provider
weight = capacity_provider_strategy.value.weight
}
}
}

resource "aws_ecs_service" "service_with_autoscaling" {
count = var.autoscaling != null ? 1 : 0
count = var.autoscaling != null ? 1 : 0
depends_on = [terraform_data.no_launch_type_and_spot]

name = var.application_name
cluster = var.cluster_id
task_definition = aws_ecs_task_definition.task.arn
desired_count = var.desired_count
launch_type = var.launch_type
launch_type = var.use_spot ? null : var.launch_type
deployment_minimum_healthy_percent = var.deployment_minimum_healthy_percent
deployment_maximum_percent = var.deployment_maximum_percent
health_check_grace_period_seconds = var.launch_type == "EXTERNAL" ? null : var.health_check_grace_period_seconds
Expand Down Expand Up @@ -437,6 +463,19 @@ resource "aws_ecs_service" "service_with_autoscaling" {
}
}

# We set the service as a spot service through setting up the capacity_provider_strategy.
# Requires a cluster with 'FARGATE_SPOT' capacity provider enabled.
dynamic "capacity_provider_strategy" {
for_each = var.use_spot ? [local.capacity_provider_strategy] : []

content {
capacity_provider = capacity_provider_strategy.value.capacity_provider
weight = capacity_provider_strategy.value.weight
}
}



lifecycle {
ignore_changes = [desired_count]
}
Expand Down
8 changes: 7 additions & 1 deletion variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ variable "sidecar_containers" {
}

variable "launch_type" {
description = "What to launch the instance on."
description = "What to launch the instance on. Mutually exclusive with \"use_spot\"."
type = string
default = "FARGATE"

Expand All @@ -36,6 +36,12 @@ variable "launch_type" {
}
}

variable "use_spot" {
description = "NB! NOT RECOMMENDED FOR PROD. Whether to use spot instances for the service. Requirement: FARGATE_SPOT enabled capacity providers. Mutually exclusive with \"launch_type\"."
type = bool
default = false
}

variable "cpu" {
description = "The amount of cores that are required for the service"
type = number
Expand Down
2 changes: 1 addition & 1 deletion versions.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
terraform {
required_version = ">= 1.3.0"
required_version = ">= 1.4.0"

required_providers {
aws = {
Expand Down

0 comments on commit f5c39d8

Please sign in to comment.