diff --git a/examples/aws-ecs-service-discovery-deployment/README.md b/examples/aws-ecs-service-discovery-deployment/README.md new file mode 100644 index 00000000..4b1b7bc5 --- /dev/null +++ b/examples/aws-ecs-service-discovery-deployment/README.md @@ -0,0 +1,65 @@ +# examples/ecs-service-discovery-deployment + +This example deploys Gubernator into AWS ECS with AWS ECS Service Discovery as peer discovery service. + +This will set up an ECS Cluster that runs AWS Fargate Spot (which is very cheap to run). + +To test this, you'd need to be inside the VPC that this example creates or somehow peer the VPC you're going to use with this example. + +Also don't forget to add NS records generated from this to your main Hosted Zone. +e.g. your main hosted zone is `example.com` and you input `dns_namespace` as `gubernator.example.com`, this will create a new Hosted Zone in Route53 with NS record (`gubernator.example.com`) and make sure to add those NS record to `example.com` else when you query `gubernator.example.com` it won't work. +Real gubernator would be accessible in `app.gubernator.example.com` + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 0.12.26 | +| [aws](#requirement\_aws) | >= 3.35.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | 3.54.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [app\_security\_group](#module\_app\_security\_group) | terraform-aws-modules/security-group/aws | ~> 3.0 | +| [ecs\_cluster](#module\_ecs\_cluster) | HENNGE/ecs/aws | ~> 2.0 | +| [gubernator\_service](#module\_gubernator\_service) | HENNGE/ecs/aws//modules/simple/fargate-spot | ~> 2.0 | +| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 3.0 | + +## Resources + +| Name | Type | +|------|------| +| [aws_cloudwatch_log_group.gubernator](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | +| [aws_iam_role.ecs_agent_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role.ecs_task_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy.ecs_agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | +| [aws_iam_role_policy.ecs_exec_permissions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | +| [aws_iam_role_policy.ecs_task_cloudwatch_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | +| [aws_service_discovery_public_dns_namespace.namespace](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/service_discovery_public_dns_namespace) | resource | +| [aws_service_discovery_service.gubernator](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/service_discovery_service) | resource | +| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | +| [aws_region.current_region](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [dns\_namespace](#input\_dns\_namespace) | The domain name the service should run. Your Gubernator instances will be available at `app..` | `string` | n/a | yes | +| [gubernator\_config](#input\_gubernator\_config) | Map of ECS Configuration for gubernator service. map(cpu, memory) | `any` |
{
"cpu": 256,
"memory": 512
}
| no | +| [gubernator\_debug\_mode](#input\_gubernator\_debug\_mode) | Enable GUBER\_DEBUG env flag | `bool` | `false` | no | +| [gubernator\_repository](#input\_gubernator\_repository) | Gubernator Docker Repository. e.g. thrawn01/gubernator | `string` | n/a | yes | +| [gubernator\_version](#input\_gubernator\_version) | Gubernator docker tag to use | `string` | n/a | yes | +| [prefix](#input\_prefix) | Prefix of created resources | `string` | n/a | yes | +| [vpc\_cidr](#input\_vpc\_cidr) | IPv4 CIDR Notation for VPC IP range. e.g. 10.3.0.0/16 | `string` | n/a | yes | + +## Outputs + +No outputs. + diff --git a/examples/aws-ecs-service-discovery-deployment/cloudwatch_logs.tf b/examples/aws-ecs-service-discovery-deployment/cloudwatch_logs.tf new file mode 100644 index 00000000..28c8f759 --- /dev/null +++ b/examples/aws-ecs-service-discovery-deployment/cloudwatch_logs.tf @@ -0,0 +1,9 @@ +resource "aws_cloudwatch_log_group" "gubernator" { + name = "${var.prefix}/gubernator" + + retention_in_days = 90 + + lifecycle { + ignore_changes = [retention_in_days] + } +} diff --git a/examples/aws-ecs-service-discovery-deployment/ecs.tf b/examples/aws-ecs-service-discovery-deployment/ecs.tf new file mode 100644 index 00000000..7af14ab4 --- /dev/null +++ b/examples/aws-ecs-service-discovery-deployment/ecs.tf @@ -0,0 +1,8 @@ +module "ecs_cluster" { + source = "HENNGE/ecs/aws" + version = "~> 2.0" + + name = "${var.prefix}-cluster" + + enable_container_insights = true +} diff --git a/examples/aws-ecs-service-discovery-deployment/ecs_gubernator.tf b/examples/aws-ecs-service-discovery-deployment/ecs_gubernator.tf new file mode 100644 index 00000000..41729c85 --- /dev/null +++ b/examples/aws-ecs-service-discovery-deployment/ecs_gubernator.tf @@ -0,0 +1,66 @@ +data "aws_region" "current_region" {} + +module "gubernator_service" { + source = "HENNGE/ecs/aws//modules/simple/fargate-spot" + version = "~> 2.0" + + ignore_desired_count_changes = true + + name = "${var.prefix}-gubernator-service" + cluster = module.ecs_cluster.name + cpu = var.gubernator_config["cpu"] + memory = var.gubernator_config["memory"] + desired_count = lookup(var.gubernator_config, "desired_count", 1) + + iam_daemon_role = aws_iam_role.ecs_agent_role.arn + iam_task_role = aws_iam_role.ecs_task_role.arn + + security_groups = [module.app_security_group.this_security_group_id] + vpc_subnets = module.vpc.public_subnets + assign_public_ip = true + + service_registry = { + registry_arn = aws_service_discovery_service.gubernator.arn + } + + container_definitions = jsonencode( + [ + { + name = "gubernator", + image = "${var.gubernator_repository}:${var.gubernator_version}", + cpu = var.gubernator_config["cpu"], + memory = var.gubernator_config["memory"], + essential = true, + linuxParameters = { + initProcessEnabled = true + }, + portMappings = [ + { containerPort = 80 }, + { containerPort = 81 }, + ], + logConfiguration = { + logDriver = "awslogs", + options = { + awslogs-group = aws_cloudwatch_log_group.gubernator.name, + awslogs-region = data.aws_region.current_region.name, + awslogs-stream-prefix = var.gubernator_version + } + }, + environment = [ + for env_key, env_value in local.gubernator_env_vars : + { + name = env_key, + value = env_value + } + ], + } + ] + ) + + enable_deployment_circuit_breaker_with_rollback = true + wait_for_steady_state = true + enable_execute_command = true + + propagate_tags = "SERVICE" + enable_ecs_managed_tags = true +} diff --git a/examples/aws-ecs-service-discovery-deployment/ecs_iam.tf b/examples/aws-ecs-service-discovery-deployment/ecs_iam.tf new file mode 100644 index 00000000..a0582c09 --- /dev/null +++ b/examples/aws-ecs-service-discovery-deployment/ecs_iam.tf @@ -0,0 +1,107 @@ +resource "aws_iam_role" "ecs_task_role" { + name = "${var.prefix}-ecs-task-role" + + assume_role_policy = jsonencode( + { + Version = "2008-10-17", + Statement = [ + { + Sid = "", + Effect = "Allow", + Principal = { + Service = "ecs-tasks.amazonaws.com" + }, + Action = "sts:AssumeRole" + } + ] + } + ) +} + +resource "aws_iam_role" "ecs_agent_role" { + name = "${var.prefix}-ecs-agent-role" + + assume_role_policy = jsonencode( + { + Version = "2008-10-17", + Statement = [ + { + Sid = "", + Effect = "Allow", + Principal = { + Service = "ecs-tasks.amazonaws.com" + }, + Action = "sts:AssumeRole" + } + ] + } + ) +} + +resource "aws_iam_role_policy" "ecs_task_cloudwatch_access" { + name = "${var.prefix}-ecs-task-cloudwatch-access" + role = aws_iam_role.ecs_task_role.id + + policy = jsonencode( + { + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Action = [ + "sts:AssumeRole", + "logs:CreateLogStream", + "logs:PutLogEvents", + "logs:DescribeLogStreams", + "logs:GetLogEvents", + "cloudwatch:PutMetricData" + ], + Resource = "*" + } + ] + } + ) +} + +resource "aws_iam_role_policy" "ecs_exec_permissions" { + name = "${var.prefix}-ecs-exec-permissions" + role = aws_iam_role.ecs_task_role.id + policy = jsonencode( + { + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Action = [ + "ssmmessages:CreateControlChannel", + "ssmmessages:CreateDataChannel", + "ssmmessages:OpenControlChannel", + "ssmmessages:OpenDataChannel" + ], + Resource = "*" + } + ] + } + ) +} + +resource "aws_iam_role_policy" "ecs_agent" { + name = "${var.prefix}-ecs-agent" + role = aws_iam_role.ecs_agent_role.id + + policy = jsonencode( + { + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Action = [ + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + Resource = "*" + } + ] + } + ) +} diff --git a/examples/aws-ecs-service-discovery-deployment/ecs_service_discovery.tf b/examples/aws-ecs-service-discovery-deployment/ecs_service_discovery.tf new file mode 100644 index 00000000..9abae49f --- /dev/null +++ b/examples/aws-ecs-service-discovery-deployment/ecs_service_discovery.tf @@ -0,0 +1,18 @@ +resource "aws_service_discovery_public_dns_namespace" "namespace" { + name = local.service_namespace +} + +resource "aws_service_discovery_service" "gubernator" { + name = local.gubernator_service_discovery + dns_config { + namespace_id = aws_service_discovery_public_dns_namespace.namespace.id + dns_records { + ttl = 10 + type = "A" + } + routing_policy = "MULTIVALUE" + } + health_check_custom_config { + failure_threshold = 1 + } +} diff --git a/examples/aws-ecs-service-discovery-deployment/main.tf b/examples/aws-ecs-service-discovery-deployment/main.tf new file mode 100644 index 00000000..3b7faaa1 --- /dev/null +++ b/examples/aws-ecs-service-discovery-deployment/main.tf @@ -0,0 +1,12 @@ +locals { + service_namespace = var.dns_namespace + gubernator_service_discovery = "app" + gubernator_service_host = "${local.gubernator_service_discovery}.${local.service_namespace}" + gubernator_env_vars = { + GUBER_GRPC_ADDRESS = "0.0.0.0:81" + GUBER_HTTP_ADDRESS = "0.0.0.0:80" + GUBER_PEER_DISCOVERY_TYPE = "dns" + GUBER_DNS_FQDN = local.gubernator_service_host + GUBER_DEBUG = var.gubernator_debug_mode ? "true" : "false" + } +} \ No newline at end of file diff --git a/examples/aws-ecs-service-discovery-deployment/outputs.tf b/examples/aws-ecs-service-discovery-deployment/outputs.tf new file mode 100644 index 00000000..e69de29b diff --git a/examples/aws-ecs-service-discovery-deployment/sg.tf b/examples/aws-ecs-service-discovery-deployment/sg.tf new file mode 100644 index 00000000..b15bce1d --- /dev/null +++ b/examples/aws-ecs-service-discovery-deployment/sg.tf @@ -0,0 +1,22 @@ +module "app_security_group" { + source = "terraform-aws-modules/security-group/aws" + version = "~> 3.0" + + name = "${var.prefix}-app-sg" + vpc_id = module.vpc.vpc_id + + # Ingress from this SG + ingress_with_self = [ + { + rule = "all-all" + }, + ] + + ingress_ipv6_cidr_blocks = [module.vpc.vpc_ipv6_cidr_block] + ingress_rules = ["all-all"] + + # Allow all egress + egress_cidr_blocks = ["0.0.0.0/0"] + egress_ipv6_cidr_blocks = ["::/0"] + egress_rules = ["all-all"] +} diff --git a/examples/aws-ecs-service-discovery-deployment/variables.tf b/examples/aws-ecs-service-discovery-deployment/variables.tf new file mode 100644 index 00000000..ad1ddb37 --- /dev/null +++ b/examples/aws-ecs-service-discovery-deployment/variables.tf @@ -0,0 +1,37 @@ +variable "prefix" { + type = string + description = "Prefix of created resources" +} + +# VPC Options +variable "vpc_cidr" { + description = "IPv4 CIDR Notation for VPC IP range. e.g. 10.3.0.0/16" + type = string +} + +variable "dns_namespace" { + description = "The domain name the service should run. Your Gubernator instances will be available at `app.." + type = string +} + +variable "gubernator_repository" { + type = string + description = "Gubernator Docker Repository. e.g. thrawn01/gubernator" +} + +variable "gubernator_version" { + type = string + description = "Gubernator docker tag to use" +} + +variable "gubernator_config" { + description = "Map of ECS Configuration for gubernator service. map(cpu, memory)" + default = { cpu = 256, memory = 512 } + type = any +} + +variable "gubernator_debug_mode" { + description = "Enable GUBER_DEBUG env flag" + default = false + type = bool +} diff --git a/examples/aws-ecs-service-discovery-deployment/versions.tf b/examples/aws-ecs-service-discovery-deployment/versions.tf new file mode 100644 index 00000000..18468156 --- /dev/null +++ b/examples/aws-ecs-service-discovery-deployment/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 0.12.26" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 3.35.0" + } + } +} diff --git a/examples/aws-ecs-service-discovery-deployment/vpc.tf b/examples/aws-ecs-service-discovery-deployment/vpc.tf new file mode 100644 index 00000000..085184bd --- /dev/null +++ b/examples/aws-ecs-service-discovery-deployment/vpc.tf @@ -0,0 +1,23 @@ +data "aws_availability_zones" "available" {} + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "~> 3.0" + + name = "${var.prefix}-vpc" + cidr = var.vpc_cidr + azs = data.aws_availability_zones.available.names + + public_subnets = [for i in range(length(data.aws_availability_zones.available.names)) : cidrsubnet(var.vpc_cidr, 8, i)] + + enable_nat_gateway = false + enable_dhcp_options = true + enable_dns_hostnames = true + enable_dns_support = true + dhcp_options_domain_name_servers = ["AmazonProvidedDNS"] + + enable_ipv6 = true + assign_ipv6_address_on_creation = true + public_subnet_assign_ipv6_address_on_creation = true + public_subnet_ipv6_prefixes = range(length(data.aws_availability_zones.available.names)) +}