Mounting AWS EFS to ECS EC2 Container - amazon-web-services

Hello I'm using Terraform to try and spin up Zookeeper for a development environment and I keep running into the following issue when I spin up the terraform.
Stopped reason Error response from daemon: create
ecs-clearstreet-basis-dev-Zookeeper-46-clearstreet-confluent-c2cf998e98d1afd45900:
VolumeDriver.Create: mounting volume failed: Specified port [2999] is
unavailable. Try selecting a different port.
I don't have this issue when attaching an EFS to a Fargate container.
Here is the terraform for reference.
resource "aws_ecs_task_definition" "ecs-zookeeper" {
family = "${var.project}-Zookeeper"
container_definitions = templatefile("./task-definitions/zookeeper.tftpl",
{
zookeeper_image = "${var.zookeeper_image}:${var.zookeeper_image_version}"
zookeeper_port = "${var.zookeeper_port}"
zookeeper_port_communication = "${var.zookeeper_port_communication}"
zookeeper_port_election = "${var.zookeeper_port_election}"
zookeeper-servers = "server.1=${var.project}1.${var.dns_zone}:2888:3888;2181"
zookeeper-elect-port-retry = "${var.zookeeper-elect-port-retry}"
zookeeper_4lw_commands_whitelist = "${var.zookeeper_4lw_commands_whitelist}"
aws_region = "${var.aws_region}"
}
)
task_role_arn = var.ecs-task-role-arn
network_mode = "awsvpc"
volume {
name = "resolv"
host_path = "/etc/docker_resolv.conf"
}
volume {
name = "Client-confluent"
efs_volume_configuration {
file_system_id = var.efs-fsid
root_directory = "/Platform/confluent"
transit_encryption = "ENABLED"
transit_encryption_port = 2999
authorization_config {
access_point_id = var.efs-confluent-fsap
iam = "ENABLED"
}
}
}
}
resource "aws_ecs_service" "ecs-zookeeper" {
name = "Zookeeper"
cluster = aws_ecs_cluster.ecs.id
task_definition = aws_ecs_task_definition.ecs-zookeeper.arn
enable_ecs_managed_tags = true
enable_execute_command = true
desired_count = 1
propagate_tags = "SERVICE"
launch_type = "EC2"
# only manual task rotation via task stop
deployment_minimum_healthy_percent = 33
deployment_maximum_percent = 100
network_configuration {
subnets = var.vpc_subnets
security_groups = [var.ECS-EC2-SG]
assign_public_ip = false
}
service_registries {
registry_arn = aws_service_discovery_service.discovery_service-zookeeper.arn
}
ordered_placement_strategy {
type = "spread"
field = "host"
}
ordered_placement_strategy {
type = "spread"
field = "attribute:ecs.availability-zone"
}
placement_constraints {
type = "memberOf"
expression = "attribute:program == PLATFORM"
}
lifecycle {
create_before_destroy = true
}
# count = var.zookeeper-instance-number
}
resource "aws_service_discovery_service" "discovery_service-zookeeper" {
name = "${var.project}-zookeeper"
dns_config {
namespace_id = aws_service_discovery_private_dns_namespace.discovery_namespace.id
dns_records {
ttl = 10
type = "A"
}
routing_policy = "MULTIVALUE"
}
health_check_custom_config {
failure_threshold = 1
}
# count = var.zookeeper-instance-number
}
Here is the Task Definition for reference
[
{
"name": "zookeeper",
"image": "${zookeeper_image}",
"cpu": 256,
"memory": 512,
"essential": true,
"portMappings": [
{
"containerPort": ${zookeeper_port},
"hostPort": ${zookeeper_port}
},
{
"containerPort": ${zookeeper_port_communication},
"hostPort": ${zookeeper_port_communication}
},
{
"containerPort": ${zookeeper_port_election},
"hostPort": ${zookeeper_port_election}
}
],
"environment": [
{
"name": "ZOO_SERVERS",
"value": "${zookeeper-servers}"
},
{
"name": "ZOO_STANDALONE_ENABLED",
"value": "false"
},
{
"name": "ZOO_ELECT_PORT_RETRY",
"value": "${zookeeper-elect-port-retry}"
},
{
"name": "ZOO_4LW_COMMANDS_WHITELIST",
"value": "${zookeeper_4lw_commands_whitelist}"
}
],
"mountPoints": [
{
"sourceVolume": "resolv",
"containerPath": "/etc/resolv.conf"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-region" : "${aws_region}",
"awslogs-group" : "/fargate/client/basis/program-zookeeper",
"awslogs-create-group": "true",
"awslogs-stream-prefix" : "program-zookeeper"
}
},
"workingDir": "/var/lib/zookeeper",
"mountPoints":[{
"sourceVolume": "client-confluent",
"containerPath": "/var/lib/zookeeper"
}]
}
]
Any help would be greatly appreciated!

Related

Error: InvalidParameterException: Task definition does not support launch_type FARGATE

I use Terraform to create ECS I got error
"InvalidParameterException: Task definition does not support launch_type FARGATE"
I checked other solution from other post but it does not help. Can anyone tell my why I got that error?
container-definition template
[
{
"name": "api",
"image": "${app_image}",
"essential": true,
"memoryReservation": 256,
"environment": [
{"name": "ALLOWED_HOSTS", "value": "${allowed_hosts}"}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "${log_group_name}",
"awslogs-region": "${log_group_region}",
"awslogs-stream-prefix": "api"
}
},
"portMappings": [
{
"containerPort": 80,
"hostPort": 80
}
]
}
]
ecs.tf
resource "aws_ecs_task_definition" "api" {
family = "${local.prefix}-api"
container_definitions = data.template_file.api_container_definitions.rendered
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = 256
memory = 512
execution_role_arn = aws_iam_role.task_execution_role.arn
task_role_arn = aws_iam_role.task_role.arn
volume {
name = "static"
}
tags = local.common_tags
}
resource "aws_ecs_service" "api" {
name = "${local.prefix}-api"
cluster = aws_ecs_cluster.main.name
task_definition = aws_ecs_task_definition.api.family
desired_count = 1
launch_type = "FARGATE"
network_configuration {
subnets = [
aws_subnet.public_a.id,
aws_subnet.public_b.id,
]
security_groups = [aws_security_group.ecs_service.id]
assign_public_ip = true
}
load_balancer {
target_group_arn = aws_alb_target_group.ecs_tg.id
container_name = "api"
container_port = var.app_port
}
}
Thank you

Terraform ECS Task Definition jsonencode issue

I get this error when applying terraform. It's clearly something wrong with my env_Vars. I've tried making name and value in quotes or without.
Error: ECS Task Definition container_definitions is invalid: Error
decoding JSON: json: cannot unmarshal number into Go struct field
KeyValuePair.Environment.Value of type string
locals:
locals {
task_name = "${var.project_name}-${var.environment}-pgadmin"
env_vars = [
{
name = "ENV",
value = var.environment
},
{
name = "POSTGRES_HOST",
value = module.rds.db_address
},
{
name = "POSTGRES_USER",
value = module.rds.db_username
},
{
name = "POSTGRES_PORT",
value = module.rdsdb_port
}
]
}
task def template:
data "template_file" "task-definition" {
template = file("${path.module}/container_definition_template.json.tpl")
vars = {
container_name = local.task_name
container_image = "dpage/pgadmin4"
container_port = 3001
env_variables = jsonencode(local.env_vars)
secrets = jsonencode(local.secrets)
}
}
Task def:
resource "aws_ecs_task_definition" "pgadmin_task_definition" {
family = local.task_name
container_definitions = data.template_file.task-definition.rendered
task_role_arn = aws_iam_role.ecsTaskRole.arn
network_mode = "awsvpc"
cpu = 1024
memory = 2048
requires_compatibilities = ["FARGATE"]
execution_role_arn = aws_iam_role.ecsTaskExecutionRole.arn
}
actual json template is :
[
{
"name": "${container_name}",
"image": "${container_image}",
"startTimeout": 120,
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "${aws_logs_group}",
"awslogs-region": "${aws_region}",
"awslogs-stream-prefix": "${aws_log_stream_prefix}"
}
},
"environment": ${env_variables},
"secrets": ${secrets},
"portMappings": [
{
"containerPort": ${container_port},
"hostPort": ${container_port}
}
]
}
]
I think this happens becuase module.rdsdb_port is number in local.env_vars, not string. You can try with:
value = tostring(module.rdsdb_port)

How to install Spark, Hadoop on EMR using Terraform?

resource "aws_emr_cluster" "cluster" {
name = "emr-test-arn"
release_label = "emr-4.6.0"
applications = ["Spark", "Hadoop"]
Mentioning "Spark", "Hadoop" in applications parameter as shown above - will that install Hadoop and Spark on the EMR?
Or does it just "prepare" the cluster in some way to work with Hadoop and Spark (and we should perform additional steps to install Hadoop and Spark on the EMR cluster)?
You could follow the official docs with examples like below. It works fine
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/emr_cluster
resource "aws_emr_cluster" "cluster" {
name = "emr-test-arn"
release_label = "emr-4.6.0"
applications = ["Spark"]
additional_info = <<EOF
{
"instanceAwsClientConfiguration": {
"proxyPort": 8099,
"proxyHost": "myproxy.example.com"
}
}
EOF
termination_protection = false
keep_job_flow_alive_when_no_steps = true
ec2_attributes {
subnet_id = aws_subnet.main.id
emr_managed_master_security_group = aws_security_group.sg.id
emr_managed_slave_security_group = aws_security_group.sg.id
instance_profile = aws_iam_instance_profile.emr_profile.arn
}
master_instance_group {
instance_type = "m4.large"
}
core_instance_group {
instance_type = "c4.large"
instance_count = 1
ebs_config {
size = "40"
type = "gp2"
volumes_per_instance = 1
}
bid_price = "0.30"
autoscaling_policy = <<EOF
{
"Constraints": {
"MinCapacity": 1,
"MaxCapacity": 2
},
"Rules": [
{
"Name": "ScaleOutMemoryPercentage",
"Description": "Scale out if YARNMemoryAvailablePercentage is less than 15",
"Action": {
"SimpleScalingPolicyConfiguration": {
"AdjustmentType": "CHANGE_IN_CAPACITY",
"ScalingAdjustment": 1,
"CoolDown": 300
}
},
"Trigger": {
"CloudWatchAlarmDefinition": {
"ComparisonOperator": "LESS_THAN",
"EvaluationPeriods": 1,
"MetricName": "YARNMemoryAvailablePercentage",
"Namespace": "AWS/ElasticMapReduce",
"Period": 300,
"Statistic": "AVERAGE",
"Threshold": 15.0,
"Unit": "PERCENT"
}
}
}
]
}
EOF
}
ebs_root_volume_size = 100
tags = {
role = "rolename"
env = "env"
}
bootstrap_action {
path = "s3://elasticmapreduce/bootstrap-actions/run-if"
name = "runif"
args = ["instance.isMaster=true", "echo running on master node"]
}
configurations_json = <<EOF
[
{
"Classification": "hadoop-env",
"Configurations": [
{
"Classification": "export",
"Properties": {
"JAVA_HOME": "/usr/lib/jvm/java-1.8.0"
}
}
],
"Properties": {}
},
{
"Classification": "spark-env",
"Configurations": [
{
"Classification": "export",
"Properties": {
"JAVA_HOME": "/usr/lib/jvm/java-1.8.0"
}
}
],
"Properties": {}
}
]
EOF
service_role = aws_iam_role.iam_emr_service_role.arn
}

Take ECS Task Definition environment variables from Terraform input variables

I am trying to deploy ECS task definition with Terraform. Here is my ECS task definition resource code:
resource "aws_ecs_task_definition" "my_TD" {
family = "my_container"
container_definitions = <<DEFINITION
[{
"name": "my_container",
"image": "${format("%s:qa", var.my_ecr_arn)}",
"portMappings": [
{
"containerPort": 80,
"hostPort": 80
}
],
"memory": 300,
"networkMode": "awsvpc",
"environment": [
{
"name": "PORT",
"value": "80"
},
{
"name": "Token",
"value": "xxxxxxxx"
}
]
}
]
DEFINITION
requires_compatibilities = ["EC2"]
network_mode = "awsvpc"
cpu = "256"
memory = "512"
task_role_arn = var.ecs_role
execution_role_arn = var.ecs_role
}
The environment variables are hardcoded here. So, I tried to take those environment variables from terraform input. So, I modified with:
variable "my_env_variables"{
default = [
{
"name": "PORT",
"value": "80"
},
{
"name": "token",
"value": "xxxxx"
}
]
}
...
...
"environment" : "${var.my_env_variables}"
...
...
It's giving me an issue like this:
var.my_env_variables is tuple with 1 element
Cannot include the given value in a string template: string required.
I am new to Terraform. How can I solve this issue?
You need json string, which you can get using jsonencode. So you could try the following:
resource "aws_ecs_task_definition" "my_TD" {
family = "my_container"
container_definitions = <<DEFINITION
[{
"name": "my_container",
"image": "${format("%s:qa", var.my_ecr_arn)}",
"portMappings": [
{
"containerPort": 80,
"hostPort": 80
}
],
"memory": 300,
"networkMode": "awsvpc",
"environment": ${jsonencode(var.my_env_variables)}
}
]
DEFINITION
requires_compatibilities = ["EC2"]
network_mode = "awsvpc"
cpu = "256"
memory = "512"
task_role_arn = var.ecs_role
execution_role_arn = var.ecs_role
}

Security group does not appear to belong to the same VPC as the input subnets

This is my terraform file to create a Fargate ECS service.
variable "aws_region" { }
variable "flavor" { } # test or prod
variable "task_worker_service_name" { }
variable "task_cpu" {}
variable "task_memory" {}
variable "az_count" {}
terraform {
required_version = "= 0.12.6"
}
provider "aws" {
version = "~> 2.21.1"
region = "${var.aws_region}"
}
data "aws_availability_zones" "available" {}
data "aws_iam_policy_document" "ecs_service_policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = [ "ecs.amazonaws.com" ]
}
}
}
data "aws_iam_policy_document" "task_worker_iam_role_policy" {
statement {
actions = [ "sts:AssumeRole" ]
principals {
type = "Service"
identifiers = [
"ecs-tasks.amazonaws.com"
]
}
}
}
data "aws_iam_policy_document" "assume_role_policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = [ "ecs-tasks.amazonaws.com" ]
}
}
}
resource "aws_iam_role" "ecs_service_role" {
name = "${var.flavor}-task-ecs-service-role"
path = "/"
assume_role_policy = "${data.aws_iam_policy_document.ecs_service_policy.json}"
}
resource "aws_iam_role_policy_attachment" "ecs_service_role_attachment" {
role = "${aws_iam_role.ecs_service_role.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole"
}
resource "aws_vpc" "ecs" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
instance_tenancy = "default"
tags = {
Name = "ecs"
}
}
resource "aws_security_group" "vpc_ecs_task_worker" {
name = "${var.flavor}-vpc_ecs_task_worker"
description = "ECS Allowed Ports"
ingress {
from_port = 32768
to_port = 65535
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_iam_role" "ecs_task_execution_role" {
name = "${var.flavor}-ecs-task-worker-task-execution-role"
assume_role_policy = data.aws_iam_policy_document.assume_role_policy.json
}
resource "aws_iam_role" "task_worker_iam_role" {
name = "${var.flavor}-task-worker-role"
path = "/"
assume_role_policy = data.aws_iam_policy_document.task_worker_iam_role_policy.json
}
# Create var.az_count private subnets, each in a different AZ
resource "aws_subnet" "private" {
count = "${var.az_count}"
cidr_block = "${cidrsubnet(aws_vpc.ecs.cidr_block, 8, count.index)}"
availability_zone = "${data.aws_availability_zones.available.names[count.index]}"
vpc_id = "${aws_vpc.ecs.id}"
}
resource "aws_ecs_task_definition" "task_worker" {
family = "${var.flavor}-${var.task_worker_service_name}"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = var.task_cpu
memory = var.task_memory
execution_role_arn = aws_iam_role.ecs_task_execution_role.arn
task_role_arn = aws_iam_role.task_worker_iam_role.arn
container_definitions = <<JSON
[
{
"dnsSearchDomains": null,
"logConfiguration": null,
"entryPoint": null,
"portMappings": [],
"command": null,
"linuxParameters": null,
"cpu": ${var.task_cpu},
"environment": [],
"resourceRequirements": null,
"ulimits": null,
"dnsServers": null,
"mountPoints": [],
"workingDirectory": null,
"secrets": null,
"dockerSecurityOptions": null,
"memory": null,
"memoryReservation": ${var.task_memory},
"volumesFrom": [],
"stopTimeout": null,
"image": "us-west-2.amazonaws.com/task:4383669",
"startTimeout": null,
"dependsOn": null,
"disableNetworking": null,
"interactive": null,
"healthCheck": null,
"essential": true,
"links": null,
"hostname": null,
"extraHosts": null,
"pseudoTerminal": null,
"user": null,
"readonlyRootFilesystem": null,
"dockerLabels": null,
"systemControls": null,
"privileged": null,
"name": "task-worker"
}
]
JSON
}
resource "aws_ecs_cluster" "task_pool" {
name = "${var.flavor}-task-pool"
}
resource "aws_ecs_service" "task_service" {
name = "${var.flavor}-task-worker-service"
cluster = "${aws_ecs_cluster.task_pool.id}"
task_definition = "${aws_ecs_task_definition.task_worker.arn}"
launch_type = "FARGATE"
desired_count = 2
network_configuration {
subnets = "${aws_subnet.private[*].id}"
security_groups = ["${aws_security_group.vpc_ecs_task_worker.id}" ]
assign_public_ip = "true"
}
}
When I tried to apply it I got this error:
InvalidParameterException: Security group sg-0e5f55bea9222dd00 does not appear to belong to the same VPC as the input subnets.
sg-0e5f55bea9222dd00 corresponds to the newly created security group aws_security_group.vpc_ecs_pdf_conversion.
I don't understand why this error message is thrown. The ingress and egress seem ok to me. How can I fix it?
Looks like, you are missing VPC reference in the security group creation section.
It should look like something below.
resource "aws_security_group" "vpc_ecs_task_worker" {
name = "${var.flavor}-vpc_ecs_task_worker"
description = "ECS Allowed Ports"
vpc_id = "${aws_vpc.ecs.id}"
ingress {
from_port = 32768
to_port = 65535
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}}
In your code vpc_id section is missing.