From 3b3d6b30a089f263155b5383fb5617e4a3584b1e Mon Sep 17 00:00:00 2001 From: Stefan Hattrell <29941279+digorgonzola@users.noreply.github.com> Date: Wed, 13 Dec 2023 14:39:08 +1100 Subject: [PATCH] deploy: container vars refactoring --- deploy/container/vars.yaml | 10 ++ deploy/github/dev.env | 16 ++- deploy/github/production.env | 15 +- deploy/github/staging.env | 13 +- deploy/tf/alb.tf | 2 +- deploy/tf/ecs.tf | 155 +++++++++------------ deploy/tf/get-parameters.tf | 16 --- deploy/tf/{vars-shared.tf => variables.tf} | 44 +++--- deploy/tf/vars-container.tf | 51 ------- deploy/tg/terragrunt.hcl | 22 ++- 10 files changed, 147 insertions(+), 197 deletions(-) create mode 100644 deploy/container/vars.yaml rename deploy/tf/{vars-shared.tf => variables.tf} (59%) delete mode 100644 deploy/tf/vars-container.tf diff --git a/deploy/container/vars.yaml b/deploy/container/vars.yaml new file mode 100644 index 0000000..f6baa9c --- /dev/null +++ b/deploy/container/vars.yaml @@ -0,0 +1,10 @@ +allowed_hosts: "*" +allowed_cidr_nets: "0.0.0.0/0" +django_secret_key: changeme +db_host: db_host +db_name: app_db +db_user: app_db +db_secret_name: /my/db/secret +db_secret_region: ap-southeast-2 +s3_storage_bucket_name: appbucket +s3_storage_bucket_region: ap-southeast-2 diff --git a/deploy/github/dev.env b/deploy/github/dev.env index 43f7c7d..09fc297 100644 --- a/deploy/github/dev.env +++ b/deploy/github/dev.env @@ -1,9 +1,15 @@ -ALB_PARAMETER_NAME=shared-alb-dev-sydney -APP_NAME=sample-django-app-mybranch +# general environment variables for Terragrunt +ALB_PARAMETER_NAME=my-alb-parameter +APP_NAME=sample-django-app AWS_ACCOUNT_ID=123456789012 AWS_REGION=ap-southeast-2 -ECR_PARAMETER_NAME=api ECR_REGISTRY=123456789012.dkr.ecr.ap-southeast-2.amazonaws.com ECR_REPOSITORY=api -ENVIRONMENT=mydev-stack -RDS_PARAMETER_NAME=db01/primary/development +ENVIRONMENT=development + +# container definition variables +DB_HOST=my-rds-db.endpoint +DB_SECRET_NAME=/rds/my-rds-db/secret +DB_SECRET_REGION=ap-southeast-2 +S3_STORAGE_BUCKET_NAME=my-app-bucket +S3_STORAGE_BUCKET_REGION=ap-southeast-2 diff --git a/deploy/github/production.env b/deploy/github/production.env index 9055ce2..74827f9 100644 --- a/deploy/github/production.env +++ b/deploy/github/production.env @@ -1,11 +1,18 @@ +# general environment variables for Terragrunt ALB_PARAMETER_NAME=shared-alb-devops-sydney APP_NAME=sample-django-app AWS_ACCOUNT_ID=450356697252 AWS_REGION=ap-southeast-2 -DB_NAME=api_prod -DB_USER=api_prod -ECR_PARAMETER_NAME=api ECR_REGISTRY=450356697252.dkr.ecr.ap-southeast-2.amazonaws.com ECR_REPOSITORY=api ENVIRONMENT=production -RDS_PARAMETER_NAME=stefan-db/primary/evaluation + +# container definition variables +ALLOWED_CIDR_NETS=10.32.48.0/20,10.32.64.0/20,10.32.80.0/20 +DB_HOST=stefan-db-rds-primary-evaluation.gamma.aodn.org.au +DB_NAME=api_prod +DB_SECRET_NAME=/rds/stefan-db/primary/evaluation/api +DB_SECRET_REGION=ap-southeast-2 +DB_USER=api_prod +S3_STORAGE_BUCKET_NAME=sample-django-app-production-450356697252 +S3_STORAGE_BUCKET_REGION=ap-southeast-2 diff --git a/deploy/github/staging.env b/deploy/github/staging.env index 57741e5..b9613b2 100644 --- a/deploy/github/staging.env +++ b/deploy/github/staging.env @@ -1,9 +1,18 @@ +# general environment variables for Terragrunt ALB_PARAMETER_NAME=shared-alb-devops-sydney APP_NAME=sample-django-app AWS_ACCOUNT_ID=450356697252 AWS_REGION=ap-southeast-2 -ECR_PARAMETER_NAME=api ECR_REGISTRY=450356697252.dkr.ecr.ap-southeast-2.amazonaws.com ECR_REPOSITORY=api ENVIRONMENT=staging -RDS_PARAMETER_NAME=stefan-db/primary/evaluation + +# container definition variables +ALLOWED_CIDR_NETS=10.32.48.0/20,10.32.64.0/20,10.32.80.0/20 +DB_HOST=stefan-db-rds-primary-evaluation.gamma.aodn.org.au +DB_NAME=api +DB_SECRET_NAME=/rds/stefan-db/primary/evaluation/api +DB_SECRET_REGION=ap-southeast-2 +DB_USER=api +S3_STORAGE_BUCKET_NAME=sample-django-app-staging-450356697252 +S3_STORAGE_BUCKET_REGION=ap-southeast-2 diff --git a/deploy/tf/alb.tf b/deploy/tf/alb.tf index 43aa9b2..e88b353 100644 --- a/deploy/tf/alb.tf +++ b/deploy/tf/alb.tf @@ -1,6 +1,6 @@ resource "aws_lb_target_group" "app" { name = "${var.app_name}-${var.environment}" - port = 80 + port = var.nginx_proxy ? var.proxy_port : var.app_port protocol = "HTTP" target_type = "ip" vpc_id = local.vpc_id diff --git a/deploy/tf/ecs.tf b/deploy/tf/ecs.tf index b2ea89a..53fab05 100644 --- a/deploy/tf/ecs.tf +++ b/deploy/tf/ecs.tf @@ -1,33 +1,68 @@ locals { - # set container definition variables with default fallback values from ssm if available - app_vars = { - allowed_hosts = var.allowed_hosts - allowed_cidr_nets = coalesce(var.allowed_cidr_nets, local.private_subnet_cidrs) - django_secret_key = var.django_secret_key - db_host = coalesce(var.db_host, local.rds_url) - db_name = var.db_name - db_user = var.db_user - db_secret_name = var.db_secret_name - db_secret_region = var.db_secret_region - s3_storage_bucket_name = coalesce( - var.s3_storage_bucket_name, - "sample-django-app-${local.bucket_suffix}" - ) - s3_storage_bucket_region = coalesce( - var.s3_storage_bucket_region, - data.aws_region.current.name - ) - } - nginx_vars = { app_host = "127.0.0.1" - app_port = 9000 - listen_port = var.container_port + app_port = var.app_port + listen_port = var.proxy_port } - app_container_vars = [for k, v in local.app_vars : { name = upper(k), value = v }] + app_container_vars = [for k, v in var.container_vars : { name = upper(k), value = v }] nginx_container_vars = [for k, v in local.nginx_vars : { name = upper(k), value = v }] - ecr_registry = split("/", local.ecr_repository_url)[0] + + container_definitions = var.nginx_proxy ? merge(local.app_container_definition, local.nginx_container_definition) : local.app_container_definition + app_container_definition = { + app = { + name = var.app_container_name + image = startswith(var.image, "sha256") ? "${var.ecr_registry}@${var.image}" : "${var.ecr_registry}:${var.image}" + health_check = { + command = ["CMD-SHELL", "uwsgi-is-ready --stats-socket /tmp/statsock > /dev/null 2>&1 || exit 1"] + } + readonly_root_filesystem = false + essential = true + memory_reservation = 256 + environment = local.app_container_vars + port_mappings = [ + { + name = var.app_container_name + containerPort = var.app_port + hostPort = var.app_port + } + ] + mount_points = [ + { + readOnly = false + containerPath = "/vol/web" + sourceVolume = "static" + } + ] + } + } + nginx_container_definition = { + nginx = { + name = "nginx" + image = "${var.ecr_registry}/nginx-proxy:latest" + health_check = { + command = ["CMD-SHELL", "curl -so /dev/null http://localhost/health || exit 1"] + } + readonly_root_filesystem = false + essential = true + memory_reservation = 256 + environment = local.nginx_container_vars + port_mappings = [ + { + name = "nginx" + containerPort = var.proxy_port + hostPort = var.proxy_port + } + ] + mount_points = [ + { + readOnly = false + containerPath = "/vol/static" + sourceVolume = "static" + } + ] + } + } } module "ecs" { @@ -84,58 +119,7 @@ module "ecs" { wait_for_steady_state = true # Container definition(s) - container_definitions = { - app = { - name = var.container_name - image = startswith(var.image, "sha256") ? "${local.ecr_repository_url}@${var.image}" : "${local.ecr_repository_url}:${var.image}" - health_check = { - command = ["CMD-SHELL", "uwsgi-is-ready --stats-socket /tmp/statsock > /dev/null 2>&1 || exit 1"] - } - readonly_root_filesystem = false - essential = true - memory_reservation = 256 - environment = local.app_container_vars - port_mappings = [ - { - name = var.container_name - containerPort = 9000 - hostPort = 9000 - } - ] - mount_points = [ - { - readOnly = false - containerPath = "/vol/web" - sourceVolume = "static" - } - ] - } - nginx = { - name = "nginx" - image = "${local.ecr_registry}/nginx-proxy:latest" - health_check = { - command = ["CMD-SHELL", "curl -so /dev/null http://localhost/health || exit 1"] - } - readonly_root_filesystem = false - essential = true - memory_reservation = 256 - environment = local.nginx_container_vars - port_mappings = [ - { - name = "nginx" - containerPort = var.container_port - hostPort = var.container_port - } - ] - mount_points = [ - { - readOnly = false - containerPath = "/vol/static" - sourceVolume = "static" - } - ] - } - } + container_definitions = local.container_definitions deployment_circuit_breaker = { enable = true @@ -145,8 +129,8 @@ module "ecs" { load_balancer = { service = { target_group_arn = aws_lb_target_group.app.arn - container_name = "nginx" - container_port = var.container_port + container_name = var.nginx_proxy ? "nginx" : "app" + container_port = var.nginx_proxy ? var.proxy_port : var.app_port } } @@ -155,8 +139,8 @@ module "ecs" { security_group_rules = { ingress_vpc = { type = "ingress" - from_port = var.container_port - to_port = var.container_port + from_port = var.nginx_proxy ? var.proxy_port : var.app_port + to_port = var.nginx_proxy ? var.proxy_port : var.app_port protocol = "tcp" cidr_blocks = [local.vpc_cidr] } @@ -179,18 +163,15 @@ module "ecs" { "s3:DeleteObject", "s3:PutObjectAcl" ] - resources = [ - "arn:aws:s3:::${var.s3_storage_bucket_name}", - "arn:aws:s3:::${var.s3_storage_bucket_name}/*" - ] + resources = flatten([for bucket in module.s3.wrapper : + split(",", "arn:aws:s3:::${bucket.s3_bucket_id},arn:aws:s3:::${bucket.s3_bucket_id}/*" + )]) }, { actions = [ "secretsmanager:GetSecretValue" ] - resources = [ - "arn:aws:secretsmanager:${var.db_secret_region}:*:secret:${var.db_secret_name}*" - ] + resources = ["arn:aws:secretsmanager:${data.aws_region.current.name}:*:secret:/rds*"] } ] diff --git a/deploy/tf/get-parameters.tf b/deploy/tf/get-parameters.tf index d335379..d80de7c 100644 --- a/deploy/tf/get-parameters.tf +++ b/deploy/tf/get-parameters.tf @@ -13,12 +13,6 @@ locals { public_subnet_cidrs = nonsensitive(data.aws_ssm_parameter.public_subnet_cidrs.value) private_subnets = split(",", nonsensitive(data.aws_ssm_parameter.private_subnets.value)) private_subnet_cidrs = nonsensitive(data.aws_ssm_parameter.private_subnet_cidrs.value) - - # ecr values - ecr_repository_url = nonsensitive(data.aws_ssm_parameter.ecr_repository_url.value) - - # rds values - rds_url = nonsensitive(data.aws_ssm_parameter.rds_url.value) } # alb parameters @@ -66,13 +60,3 @@ data "aws_ssm_parameter" "zonename" { data "aws_ssm_parameter" "zoneid" { name = "/core/zone_id" } - -# ecr parameters -data "aws_ssm_parameter" "ecr_repository_url" { - name = "/apps/ecr/${var.ecr_parameter_name}/ecr_repository_url" -} - -# rds parameters -data "aws_ssm_parameter" "rds_url" { - name = "/rds/${var.rds_parameter_name}/endpoint" -} diff --git a/deploy/tf/vars-shared.tf b/deploy/tf/variables.tf similarity index 59% rename from deploy/tf/vars-shared.tf rename to deploy/tf/variables.tf index 7365ca4..6e09b4c 100644 --- a/deploy/tf/vars-shared.tf +++ b/deploy/tf/variables.tf @@ -1,40 +1,38 @@ -# ssm parameters variable "alb_parameter_name" { description = "The parameter name to derive the ALB details from." type = string } -variable "ecr_parameter_name" { - description = "The parameter name to derive the ALB details from." - type = string -} - -variable "rds_parameter_name" { - description = "The parameter name to derive the database host from." +variable "app_container_name" { + description = "The name of the primary application container" type = string + default = "app" } -# general variables variable "app_name" { description = "The name of the application e.g. sample-django-app" type = string } +variable "app_port" { + description = "The port to expose to the nginx proxy on the application container." + type = number + default = 9000 +} + variable "app_hostnames" { description = "Hostnames to associate with the application" type = list(string) } -variable "container_name" { - description = "The name of the primary application container" - type = string - default = "app" +variable "container_vars" { + description = "Map of key/pair values to pass to the container definition." + type = map(any) } -variable "container_port" { - description = "The port to expose to the load balancer on the container" - type = number - default = 80 +variable "ecr_registry" { + description = "The registry to pull docker images from." + type = string } variable "environment" { @@ -46,3 +44,15 @@ variable "image" { description = "The digest/tag of the docker image to pull from ECR" type = string } + +variable "nginx_proxy" { + description = "Whether or not to side-load an nginx container in the task definition" + type = bool + default = true +} + +variable "proxy_port" { + description = "The port to expose to the load balancer on the container" + type = number + default = 80 +} diff --git a/deploy/tf/vars-container.tf b/deploy/tf/vars-container.tf deleted file mode 100644 index 145fa69..0000000 --- a/deploy/tf/vars-container.tf +++ /dev/null @@ -1,51 +0,0 @@ -# Container environment variables -variable "allowed_hosts" { - description = "Hosts allowed to access the application container (i.e. 127.0.0.1)" - type = string -} - -variable "allowed_cidr_nets" { - description = "Subnet CIDR's allowed to access the application container" - type = string -} - -variable "django_secret_key" { - description = "The secret key for django app" - type = string -} - -variable "db_host" { - description = "Override variable for database host" - type = string -} - -variable "db_name" { - description = "The name of the database" - type = string -} - -variable "db_user" { - description = "The user to connect to the database with" - type = string -} - -variable "db_secret_name" { - description = "The name of the secret to fetch DB login credentials from" - type = string -} - -variable "db_secret_region" { - description = "The region to fetch the secret from" - type = string - default = "ap-southeast-2" -} - -variable "s3_storage_bucket_name" { - description = "Name of the S3 bucket to use" - type = string -} - -variable "s3_storage_bucket_region" { - description = "The bucket region" - type = string -} diff --git a/deploy/tg/terragrunt.hcl b/deploy/tg/terragrunt.hcl index bc7f9c9..865f92d 100644 --- a/deploy/tg/terragrunt.hcl +++ b/deploy/tg/terragrunt.hcl @@ -9,25 +9,19 @@ inputs = { # fetch the ssm parameter names alb_parameter_name = get_env("ALB_PARAMETER_NAME") - ecr_parameter_name = get_env("ECR_PARAMETER_NAME") - rds_parameter_name = get_env("RDS_PARAMETER_NAME") # DNS hostnames to associate with the container app_hostnames = ["api-${local.global.environment}"] - # get docker environment variable values with default fallback values - allowed_hosts = get_env("ALLOWED_HOSTS", "*") - allowed_cidr_nets = get_env("ALLOWED_CIDR_NETS", "") - django_secret_key = get_env("DJANGO_SECRET_KEY", "changeme") - db_host = get_env("DB_HOST", "") - db_name = get_env("DB_NAME", "api") - db_user = get_env("DB_USER", "api") - db_secret_name = get_env("DB_SECRET_NAME", "/rds/stefan-db/primary/evaluation/api") - db_secret_region = get_env("DB_SECRET_REGION", "ap-southeast-2") - s3_storage_bucket_name = get_env("S3_STORAGE_BUCKET_NAME", "") - s3_storage_bucket_region = get_env("S3_STORAGE_BUCKET_REGION", "") + # container-specific environment variables + container_vars = local.container_vars + + ecr_registry = get_env("ECR_REGISTRY") } locals { - global = include.global.locals + container_var_defaults = yamldecode(file("../container/vars.yaml")) + # get any overrides from the environment (e.g. GitHub deployment variables) + container_vars = { for k, v in local.container_var_defaults : k => can(get_env(upper(k))) ? get_env(upper(k)) : v } + global = include.global.locals }