Skip to content

Commit c6bac9e

Browse files
authored
Add support for FARGATE-SPOT capacity provider and extend options for task definition (#10)
1 parent 1d7fbe8 commit c6bac9e

File tree

4 files changed

+248
-35
lines changed

4 files changed

+248
-35
lines changed

README.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,34 @@
11
# terraform-aws-ecs-fargate
22

3-
Terraform module to create AWS ECS FARGATE services.
3+
Terraform module to create AWS ECS FARGATE services. Module support both FARGATE and FARGATE-SPOT capacity provider settings.
44

55
## Terraform versions
66

77
Terraform 0.12. Pin module version to `~> v1.0`. Submit pull-requests to `master` branch.
88

99
## Usage
1010

11-
### Application Load Balancer
11+
### ECS Fargate Service
1212

1313
```hcl
1414
resource "aws_ecs_cluster" "cluster" {
1515
name = "example-ecs-cluster"
16+
17+
capacity_providers = ["FARGATE_SPOT", "FARGATE"]
18+
19+
default_capacity_provider_strategy {
20+
capacity_provider = "FARGATE_SPOT"
21+
}
22+
23+
setting {
24+
name = "containerInsights"
25+
value = "disabled"
26+
}
1627
}
1728
1829
module "ecs-farage" {
1930
source = "umotif-public/ecs-fargate/aws"
20-
version = "~> 1.0"
31+
version = "~> 1.1"
2132
2233
name_prefix = "ecs-fargate-example"
2334
vpc_id = "vpc-abasdasd132"
@@ -39,6 +50,7 @@ module "ecs-farage" {
3950
}
4051
4152
tags = {
53+
Environment = "test"
4254
Project = "Test"
4355
}
4456
}
@@ -51,6 +63,7 @@ Module is to be used with Terraform > 0.12.
5163
## Examples
5264

5365
* [ECS Fargate](https://github.com/umotif-public/terraform-aws-ecs-fargate/tree/master/examples/core)
66+
* [ECS Fargate Spot](https://github.com/umotif-public/terraform-aws-ecs-fargate/tree/master/examples/fargate-spot)
5467

5568
## Authors
5669

@@ -61,21 +74,25 @@ Module managed by [Marcin Cuber](https://github.com/marcincuber) [LinkedIn](http
6174

6275
| Name | Description | Type | Default | Required |
6376
|------|-------------|:----:|:-----:|:-----:|
77+
| capacity\_provider\_strategy | \(Optional\) The capacity\_provider\_strategy configuration block. This is a list of maps, where each map should contain "capacity\_provider ", "weight" and "base" | list | `[]` | no |
6478
| cluster\_id | The Amazon Resource Name \(ARN\) that identifies the cluster. | string | n/a | yes |
6579
| container\_name | Optional name for the container to be used instead of name\_prefix. | string | `""` | no |
6680
| deployment\_controller\_type | Type of deployment controller. Valid values: CODE\_DEPLOY, ECS. | string | `"ECS"` | no |
6781
| deployment\_maximum\_percent | The upper limit of the number of running tasks that can be running in a service during a deployment | number | `"200"` | no |
6882
| deployment\_minimum\_healthy\_percent | The lower limit of the number of running tasks that must remain running and healthy in a service during a deployment | number | `"50"` | no |
6983
| desired\_count | The number of instances of the task definitions to place and keep running. | number | `"1"` | no |
84+
| docker\_volume\_configuration | \(Optional\) Used to configure a docker volume option "docker\_volume\_configuration". Full set of options can be found at https://www.terraform.io/docs/providers/aws/r/ecs\_task\_definition.html | list | `[]` | no |
7085
| health\_check | A health block containing health check settings for the target group. Overrides the defaults. | map(string) | n/a | yes |
7186
| health\_check\_grace\_period\_seconds | Seconds to ignore failing load balancer health checks on newly instantiated tasks to prevent premature shutdown, up to 7200. Only valid for services configured to use load balancers. | number | `"300"` | no |
7287
| lb\_arn | Arn for the LB for which the service should be attach to. | string | n/a | yes |
7388
| load\_balanced | Whether the task should be loadbalanced. | bool | `"true"` | no |
7489
| log\_retention\_in\_days | Number of days the logs will be retained in CloudWatch. | number | `"30"` | no |
7590
| logs\_kms\_key | The KMS key ARN to use to encrypt container logs. | string | `""` | no |
7691
| name\_prefix | A prefix used for naming resources. | string | n/a | yes |
92+
| placement\_constraints | \(Optional\) A set of placement constraints rules that are taken into consideration during task placement. Maximum number of placement\_constraints is 10. This is a list of maps, where each map should contain "type" and "expression" | list | `[]` | no |
7793
| private\_subnet\_ids | A list of private subnets inside the VPC | list(string) | n/a | yes |
7894
| propogate\_tags | Specifies whether to propagate the tags from the task definition or the service to the tasks. The valid values are SERVICE and TASK\_DEFINITION. | string | `"TASK_DEFINITION"` | no |
95+
| proxy\_configuration | \(Optional\) The proxy configuration details for the App Mesh proxy. This is a list of maps, where each map should contain "container\_name", "properties" and "type" | list | `[]` | no |
7996
| repository\_credentials | name or ARN of a secrets manager secret \(arn:aws:secretsmanager:region:aws\_account\_id:secret:secret\_name\) | string | `""` | no |
8097
| repository\_credentials\_kms\_key | key id, key ARN, alias name or alias ARN of the key that encrypted the repository credentials | string | `"alias/aws/secretsmanager"` | no |
8198
| service\_registry\_arn | ARN of aws\_service\_discovery\_service resource | string | `""` | no |
@@ -90,6 +107,7 @@ Module managed by [Marcin Cuber](https://github.com/marcincuber) [LinkedIn](http
90107
| task\_definition\_cpu | Amount of CPU to reserve for the task. | number | `"256"` | no |
91108
| task\_definition\_memory | The soft limit \(in MiB\) of memory to reserve for the container. | number | `"512"` | no |
92109
| task\_host\_port | The port number on the container instance to reserve for your container. | number | `"0"` | no |
110+
| volume | \(Optional\) A set of volume blocks that containers in your task may use. This is a list of maps, where each map should contain "name", "host\_path" and "docker\_volume\_configuration". Full set of options can be found at https://www.terraform.io/docs/providers/aws/r/ecs\_task\_definition.html | list | `[]` | no |
93111
| vpc\_id | The VPC ID. | string | n/a | yes |
94112

95113
## Outputs

examples/fargate-spot/main.tf

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
provider "aws" {
2+
region = "eu-west-1"
3+
}
4+
5+
#####
6+
# VPC and subnets
7+
#####
8+
module "vpc" {
9+
source = "terraform-aws-modules/vpc/aws"
10+
version = "~> 2.21"
11+
12+
name = "simple-vpc"
13+
14+
cidr = "10.0.0.0/16"
15+
16+
azs = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
17+
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
18+
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
19+
20+
enable_nat_gateway = false
21+
}
22+
23+
#####
24+
# ALB
25+
#####
26+
module "alb" {
27+
source = "umotif-public/alb/aws"
28+
version = "~> 1.0"
29+
30+
name_prefix = "alb-example"
31+
load_balancer_type = "application"
32+
internal = false
33+
vpc_id = module.vpc.vpc_id
34+
subnets = module.vpc.public_subnets
35+
}
36+
37+
resource "aws_lb_listener" "alb_80" {
38+
load_balancer_arn = module.alb.arn
39+
port = "80"
40+
protocol = "HTTP"
41+
42+
default_action {
43+
type = "forward"
44+
target_group_arn = module.fargate.target_group_arn
45+
}
46+
}
47+
48+
#####
49+
# Security Group Config
50+
#####
51+
resource "aws_security_group_rule" "alb_ingress_80" {
52+
security_group_id = module.alb.security_group_id
53+
type = "ingress"
54+
protocol = "tcp"
55+
from_port = 80
56+
to_port = 80
57+
cidr_blocks = ["0.0.0.0/0"]
58+
ipv6_cidr_blocks = ["::/0"]
59+
}
60+
61+
resource "aws_security_group_rule" "task_ingress_80" {
62+
security_group_id = module.fargate.service_sg_id
63+
type = "ingress"
64+
protocol = "tcp"
65+
from_port = 80
66+
to_port = 80
67+
source_security_group_id = module.alb.security_group_id
68+
}
69+
70+
#####
71+
# ECS cluster and fargate
72+
#####
73+
resource "aws_ecs_cluster" "cluster" {
74+
name = "ecs-spot-test"
75+
capacity_providers = ["FARGATE_SPOT", "FARGATE"]
76+
77+
default_capacity_provider_strategy {
78+
capacity_provider = "FARGATE_SPOT"
79+
}
80+
81+
setting {
82+
name = "containerInsights"
83+
value = "disabled"
84+
}
85+
}
86+
87+
module "fargate" {
88+
source = "../../"
89+
90+
name_prefix = "ecs-fargate-example"
91+
vpc_id = module.vpc.vpc_id
92+
private_subnet_ids = module.vpc.public_subnets
93+
lb_arn = module.alb.arn
94+
cluster_id = aws_ecs_cluster.cluster.id
95+
96+
task_container_image = "marcincuber/2048-game:latest"
97+
task_definition_cpu = 256
98+
task_definition_memory = 512
99+
100+
task_container_port = 80
101+
task_container_assign_public_ip = true
102+
103+
health_check = {
104+
port = "traffic-port"
105+
path = "/"
106+
}
107+
108+
capacity_provider_strategy = [
109+
{
110+
capacity_provider = "FARGATE_SPOT",
111+
weight = 100
112+
}
113+
]
114+
}

main.tf

Lines changed: 83 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -126,43 +126,85 @@ resource "aws_ecs_task_definition" "task" {
126126
cpu = var.task_definition_cpu
127127
memory = var.task_definition_memory
128128
task_role_arn = aws_iam_role.task.arn
129+
130+
container_definitions = <<EOF
131+
[{
132+
"name": "${var.container_name != "" ? var.container_name : var.name_prefix}",
133+
"image": "${var.task_container_image}",
134+
%{if var.repository_credentials != ""~}
135+
"repositoryCredentials": {
136+
"credentialsParameter": "${var.repository_credentials}"
137+
},
138+
%{~endif}
139+
"essential": true,
140+
%{if var.task_container_port != 0 || var.task_host_port != 0~}
141+
"portMappings": [
142+
{
143+
%{if var.task_host_port != 0~}
144+
"hostPort": ${var.task_host_port},
145+
%{~endif}
146+
%{if var.task_container_port != 0~}
147+
"containerPort": ${var.task_container_port},
148+
%{~endif}
149+
"protocol":"tcp"
150+
}
151+
],
152+
%{~endif}
153+
"logConfiguration": {
154+
"logDriver": "awslogs",
155+
"options": {
156+
"awslogs-group": "${aws_cloudwatch_log_group.main.name}",
157+
"awslogs-region": "${data.aws_region.current.name}",
158+
"awslogs-stream-prefix": "container"
159+
}
160+
},
161+
"command": ${jsonencode(var.task_container_command)},
162+
"environment": ${jsonencode(local.task_environment)}
163+
}]
164+
EOF
165+
166+
dynamic "placement_constraints" {
167+
for_each = var.placement_constraints
168+
content {
169+
expression = lookup(placement_constraints.value, "expression", null)
170+
type = placement_constraints.value.type
171+
}
172+
}
173+
174+
dynamic "proxy_configuration" {
175+
for_each = var.proxy_configuration
176+
content {
177+
container_name = proxy_configuration.value.container_name
178+
properties = lookup(proxy_configuration.value, "properties", null)
179+
type = lookup(proxy_configuration.value, "type", null)
180+
}
181+
}
182+
183+
dynamic "volume" {
184+
for_each = var.volume
185+
content {
186+
name = volume.value.name
187+
host_path = lookup(volume.value, "host_path", null)
188+
189+
dynamic "docker_volume_configuration" {
190+
for_each = var.docker_volume_configuration
191+
content {
192+
scope = lookup(docker_volume_configuration.value, "scope", null)
193+
autoprovision = lookup(docker_volume_configuration.value, "autoprovision", null)
194+
driver = lookup(docker_volume_configuration.value, "driver", null)
195+
driver_opts = lookup(docker_volume_configuration.value, "driver_opts", null)
196+
labels = lookup(docker_volume_configuration.value, "labels", null)
197+
}
198+
}
199+
}
200+
}
201+
129202
tags = merge(
130203
var.tags,
131204
{
132205
Name = var.container_name != "" ? var.container_name : var.name_prefix
133206
},
134207
)
135-
container_definitions = <<EOF
136-
[{
137-
"name": "${var.container_name != "" ? var.container_name : var.name_prefix}",
138-
"image": "${var.task_container_image}",
139-
%{if var.repository_credentials != ""~}
140-
"repositoryCredentials": {
141-
"credentialsParameter": "${var.repository_credentials}"
142-
},
143-
%{~endif}
144-
"essential": true,
145-
"portMappings": [
146-
{
147-
"containerPort": ${var.task_container_port},
148-
%{if var.task_host_port != 0~}
149-
"hostPort": ${var.task_host_port},
150-
%{~endif}
151-
"protocol":"tcp"
152-
}
153-
],
154-
"logConfiguration": {
155-
"logDriver": "awslogs",
156-
"options": {
157-
"awslogs-group": "${aws_cloudwatch_log_group.main.name}",
158-
"awslogs-region": "${data.aws_region.current.name}",
159-
"awslogs-stream-prefix": "container"
160-
}
161-
},
162-
"command": ${jsonencode(var.task_container_command)},
163-
"environment": ${jsonencode(local.task_environment)}
164-
}]
165-
EOF
166208
}
167209

168210
resource "aws_ecs_service" "service" {
@@ -172,7 +214,7 @@ resource "aws_ecs_service" "service" {
172214
task_definition = aws_ecs_task_definition.task.arn
173215
desired_count = var.desired_count
174216
propagate_tags = var.propogate_tags
175-
launch_type = "FARGATE"
217+
launch_type = length(var.capacity_provider_strategy) == 0 ? "FARGATE" : null
176218
deployment_minimum_healthy_percent = var.deployment_minimum_healthy_percent
177219
deployment_maximum_percent = var.deployment_maximum_percent
178220
health_check_grace_period_seconds = var.load_balanced ? var.health_check_grace_period_seconds : null
@@ -183,6 +225,15 @@ resource "aws_ecs_service" "service" {
183225
assign_public_ip = var.task_container_assign_public_ip
184226
}
185227

228+
dynamic "capacity_provider_strategy" {
229+
for_each = var.capacity_provider_strategy
230+
content {
231+
capacity_provider = capacity_provider_strategy.value.capacity_provider
232+
weight = capacity_provider_strategy.value.weight
233+
base = lookup(capacity_provider_strategy.value, "base", null)
234+
}
235+
}
236+
186237
dynamic "load_balancer" {
187238
for_each = var.load_balanced ? [1] : []
188239
content {

variables.tf

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,33 @@ variable "logs_kms_key" {
170170
description = "The KMS key ARN to use to encrypt container logs."
171171
default = ""
172172
}
173+
174+
variable "capacity_provider_strategy" {
175+
type = list
176+
description = "(Optional) The capacity_provider_strategy configuration block. This is a list of maps, where each map should contain \"capacity_provider \", \"weight\" and \"base\""
177+
default = []
178+
}
179+
180+
variable "placement_constraints" {
181+
type = list
182+
description = "(Optional) A set of placement constraints rules that are taken into consideration during task placement. Maximum number of placement_constraints is 10. This is a list of maps, where each map should contain \"type\" and \"expression\""
183+
default = []
184+
}
185+
186+
variable "proxy_configuration" {
187+
type = list
188+
description = "(Optional) The proxy configuration details for the App Mesh proxy. This is a list of maps, where each map should contain \"container_name\", \"properties\" and \"type\""
189+
default = []
190+
}
191+
192+
variable "volume" {
193+
type = list
194+
description = "(Optional) A set of volume blocks that containers in your task may use. This is a list of maps, where each map should contain \"name\", \"host_path\" and \"docker_volume_configuration\". Full set of options can be found at https://www.terraform.io/docs/providers/aws/r/ecs_task_definition.html"
195+
default = []
196+
}
197+
198+
variable "docker_volume_configuration" {
199+
type = list
200+
description = "(Optional) Used to configure a docker volume option \"docker_volume_configuration\". Full set of options can be found at https://www.terraform.io/docs/providers/aws/r/ecs_task_definition.html"
201+
default = []
202+
}

0 commit comments

Comments
 (0)