Skip to content

Add support for FARGATE #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ The workflow for each part is basically quite the same:
3. Run `terraform plan -var-file=configuration.tfvars` and confirm changes
4. Run `terraform apply -var-file=configuration.tfvars`

## Fargate

By default the ECS cluster is deployed on EC2 ECS instances. Specifying variable
```
launch_type = "FARGATE"
```
will cause the cluster to use FARGATE infrastructure. Currently fargate doesn't support docker private registries. Only Amazon's ECR Registry and Docker Public Registries.

## Additional information

Expand Down
2 changes: 2 additions & 0 deletions asg/alb.tf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ resource "aws_alb_target_group" "webapp_tg" {

deregistration_delay = 180

target_type = "${var.launch_type == "FARGATE" ? "ip" : "instance"}"

health_check {
interval = "60"
path = "/"
Expand Down
5 changes: 4 additions & 1 deletion asg/autoscaling.tf
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
resource "aws_launch_configuration" "webapp_on_demand" {
count = "${var.launch_type == "FARGATE" ? 0 : 1}"
instance_type = "${var.instance_type}"
image_id = "${lookup(var.ecs_image_id, var.aws_region)}"
iam_instance_profile = "${var.ecs_instance_profile}"
Expand All @@ -13,10 +14,11 @@ resource "aws_launch_configuration" "webapp_on_demand" {
}

resource "aws_autoscaling_group" "webapp_on_demand" {
count = "${var.launch_type == "FARGATE" ? 0 : 1}"
name = "${var.name_prefix}_webapp_on_demand"
max_size = 50
min_size = 0
desired_capacity = "${var.desired_capacity_on_demand}"
desired_capacity = "${var.desired_capacity_on_demand}"
health_check_grace_period = 300
health_check_type = "EC2"
force_delete = true
Expand All @@ -35,6 +37,7 @@ resource "aws_autoscaling_group" "webapp_on_demand" {
}

data "template_file" "autoscaling_user_data" {
count = "${var.launch_type == "FARGATE" ? 0 : 1}"
template = "${file("autoscaling_user_data.tpl")}"
vars {
ecs_cluster = "${aws_ecs_cluster.webapp_cluster.name}"
Expand Down
5 changes: 3 additions & 2 deletions asg/configuration.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ desired_capacity_on_demand = 2
ec2_key_name = "key-name"
instance_type = "t2.micro"
minimum_healthy_percent_webapp = 50

launch_type = "EC2" # can also be "FARGATE"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

desired_capacity_on_demand, ec2_key_name, and instance_type are also not needed for Fargate.

And also, I guess it makes more sense to use us-east-1 in the aws_region example here (instead of ap-northeast-1).

##
# This is a sample (public) Docker image from which can be accessed at https://github.com/docker-training/webapp
# This sample image utilizes Flask and it's not RECOMMENDED to run it directly in production (performance degradation)
Expand All @@ -20,7 +20,7 @@ webapp_docker_image_name = "training/webapp"
webapp_docker_image_tag = "latest"

##
# These variables are required, please fill it out with your environment outputs
# These variables are required, please fill it out with your environment outputs or use remote state references
##
sg_webapp_albs_id = "sg-12345678"
sg_webapp_instances_id = "sg-23456789"
Expand All @@ -29,3 +29,4 @@ subnet_ids = "subnet-34567890,subnet-4567890a"

ecs_instance_profile = "arn:aws:iam::123456789012:instance-profile/tutorial-test_ecs_instance_profile"
ecs_service_role = "tutorial-test_ecs_service_role"
ecs_task_execution_role = "arn:aws:iam::123456789012:role/tutorial-test_ecs_task_execution_role" # only used with launch type FARGATE
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ecs_service_role is not needed for FARGATE.

43 changes: 43 additions & 0 deletions asg/ecs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ resource "aws_ecs_cluster" "webapp_cluster" {

/* ECS service definition */
resource "aws_ecs_service" "webapp_service" {
count = "${var.launch_type == "FARGATE" ? 0 : 1}"
name = "${var.name_prefix}_webapp_service"
cluster = "${aws_ecs_cluster.webapp_cluster.id}"
task_definition = "${aws_ecs_task_definition.webapp_definition.arn}"
Expand All @@ -23,9 +24,51 @@ resource "aws_ecs_service" "webapp_service" {
}
}

resource "aws_ecs_service" "webapp_service_fargate" {
count = "${var.launch_type == "FARGATE" ? 1 : 0}"
name = "${var.name_prefix}_webapp_service"
cluster = "${aws_ecs_cluster.webapp_cluster.id}"
task_definition = "${aws_ecs_task_definition.webapp_definition_fargate.arn}"
launch_type = "FARGATE"
desired_count = "${var.count_webapp}"
deployment_minimum_healthy_percent = "${var.minimum_healthy_percent_webapp}"
iam_role = "${var.ecs_service_role}"
Copy link
Owner

@freedomofkeima freedomofkeima May 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With Fargate, we don't need to create ecs_service_role or even pass iam_role here, since Fargate will automatically use service-linked roles for ECS.

You cannot specify an IAM role for services that require a service linked role

See: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using-service-linked-roles.html

It might be good to put this URL in the README.md, in case someone needs to use custom rule service linked roles.

For now, I think it's a good idea to put launch_type variable in static and we can decide whether we need to create ecs_service_role via count = "${var.launch_type == "FARGATE" ? 1 : 0}".


network_configuration {
security_groups = ["${var.sg_webapp_instances_id}"]
subnets = ["${split(",", var.subnet_ids)}"]
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this example, we need to add assign_public_ip = "true" for Fargate (since we don't create NAT). Otherwise, our tasks won't be able to pull Docker image from ECR or public Docker Hub. It will return:

CannotPullContainerError: API error (500): Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)

See samuelkarp answer here.

you either need private IP + NAT or a public IP + IGW. In your example, the task that's failing has neither NAT nor a public IP.

Terraform documentation: https://www.terraform.io/docs/providers/aws/r/ecs_service.html#assign_public_ip


load_balancer {
target_group_arn = "${aws_alb_target_group.webapp_tg.arn}"
container_name = "webapp"
container_port = 5000
}

lifecycle {
create_before_destroy = true
}
}

resource "aws_ecs_task_definition" "webapp_definition" {
count = "${var.launch_type == "FARGATE" ? 0 : 1}"
family = "${var.name_prefix}_webapp"
container_definitions = "${data.template_file.task_webapp.rendered}"

lifecycle {
create_before_destroy = true
}
}

resource "aws_ecs_task_definition" "webapp_definition_fargate" {
count = "${var.launch_type == "FARGATE" ? 1 : 0}"
family = "${var.name_prefix}_webapp"
container_definitions = "${data.template_file.task_webapp.rendered}"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
execution_role_arn = "${var.ecs_task_execution_role}"
cpu = "512"
memory = "1024"

lifecycle {
create_before_destroy = true
Expand Down
12 changes: 10 additions & 2 deletions asg/vars.tf
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ variable "ecs_image_id" {
us-west-1 = "ami-9ad4dcfa"
us-west-2 = "ami-92e06fea"
}
description = "ECS Optimized AMI. Not used with launch_type FARGATE."
}

variable "webapp_docker_image_name" {
Expand All @@ -43,7 +44,12 @@ variable "count_webapp" {

variable "desired_capacity_on_demand" {
default = 2
description = "Number of instance to run"
description = "Number of instances to run. Not used with launch_type FARGATE"
}

variable "launch_type" {
default = "EC2"
description = "launch type. set to FARGATE to use fargate. default is EC2 launch type."
}

variable "ec2_key_name" {
Expand All @@ -53,7 +59,7 @@ variable "ec2_key_name" {

variable "instance_type" {
default = "t2.micro"
description = "EC2 instance type to use"
description = "EC2 instance type to use. Not used with launch_type FARGATE"
}

variable "minimum_healthy_percent_webapp" {
Expand All @@ -70,6 +76,8 @@ variable "subnet_ids" {}
/* Consume static outputs */
variable "ecs_instance_profile" {}
variable "ecs_service_role" {}
variable "ecs_task_execution_role" {}


/* Region settings for AWS provider */
provider "aws" {
Expand Down
48 changes: 48 additions & 0 deletions static/iam/iam.tf
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,51 @@ resource "aws_iam_role_policy" "ecs_service_role_policy" {
}
EOF
}

/**
* Role for ECS using Fargate to use awslogs and access ECR registry
*/

resource "aws_iam_role" "ecs_task_execution_role" {
name = "${var.name_prefix}_ecs_task_execution_role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}

resource "aws_iam_role_policy" "ecs_task_execution_role_policy" {
name = "${var.name_prefix}ecs_task_execution_role_policy"
role = "${aws_iam_role.ecs_task_execution_role.id}"

policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
}
]
}
EOF
}
4 changes: 4 additions & 0 deletions static/iam/output.tf
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ output "ecs_instance_profile" {
output "ecs_service_role" {
value = "${aws_iam_role.ecs_service_role.name}"
}

output "ecs_task_execution_role" {
value = "${aws_iam_role.ecs_task_execution_role.arn}"
}