diff --git a/bootstrap/cell/main.tf b/bootstrap/cell/main.tf new file mode 100644 index 0000000..eb3369f --- /dev/null +++ b/bootstrap/cell/main.tf @@ -0,0 +1,76 @@ +// Each cell of the dbsync extension containes 1 PVC, 1 Postgres instance, 1 +// PGBouncer that acts proxy and an amount of instances (commonly 3, one per +// network). +locals { + postgres_host = "postgres-dbsync-v3-${var.salt}" + db_volume_claim = coalesce(var.db_volume_claim, "pvc-${var.salt}") + postgres_config_name = coalesce(var.postgres_config_name, "postgres-config-${var.salt}") +} +module "dbsync_pvc" { + source = "../pvc" + namespace = var.namespace + volume_name = var.volume_name + storage_size = var.storage_size + name = local.db_volume_claim +} + +module "dbsync_postgres" { + source = "../postgres" + + namespace = var.namespace + db_volume_claim = local.db_volume_claim + instance_name = local.postgres_host + postgres_config_name = local.postgres_config_name + topology_zone = var.topology_zone + postgres_image_tag = var.postgres_image_tag + postgres_secret_name = var.postgres_secret_name + postgres_resources = var.postgres_resources +} + +module "dbsync_pgbouncer" { + source = "../pgbouncer" + + namespace = var.namespace + pg_bouncer_replicas = var.pgbouncer_replicas + certs_configmap_name = var.certs_configmap_name + pg_bouncer_user_settings = var.pgbouncer_user_settings + pg_bouncer_auth_user_password = var.pgbouncer_auth_user_password + instance_role = "pgbouncer" + postgres_secret_name = var.postgres_secret_name + instance_name = "postgres-dbsync-v3-${var.salt}" + postgres_instance_name = local.postgres_host +} + +module "dbsync_instances" { + source = "../instance" + for_each = var.instances + + namespace = var.namespace + network = each.value.network + salt = coalesce(each.value.salt, var.salt) + dbsync_image_tag = each.value.dbsync_image_tag + node_n2n_tcp_endpoint = each.value.node_n2n_tcp_endpoint + release = each.value.release + topology_zone = coalesce(each.value.topology_zone, var.topology_zone) + sync_status = each.value.sync_status + + enable_postgrest = each.value.enable_postgrest + postgres_database = "dbsync-${each.value.network}" + postgres_instance_name = local.postgres_host + postgres_secret_name = var.postgres_secret_name + + dbsync_resources = coalesce(each.value.dbsync_resources, { + "limits" = { + "memory" = "4Gi" + } + "requests" = { + "memory" = "4Gi" + "cpu" = "100m" + } + }) + dbsync_volume = coalesce(each.value.dbsync_volume, { + manual = false + storage_class = "fast" + size = "10Gi" + }) +} diff --git a/bootstrap/cell/variables.tf b/bootstrap/cell/variables.tf new file mode 100644 index 0000000..61fa7d6 --- /dev/null +++ b/bootstrap/cell/variables.tf @@ -0,0 +1,108 @@ +variable "namespace" { + type = string +} + +variable "salt" { + type = string + description = "Salt used to identify all components as part of the cell. Should be unique between cells." +} + +variable "certs_configmap_name" { + type = string + default = "pgbouncer-certs" +} + +// PVC +variable "volume_name" { + type = string +} + +variable "storage_size" { + type = string +} + +variable "db_volume_claim" { + type = string + default = null +} + +// Postgres +variable "topology_zone" { + type = string +} + +variable "postgres_image_tag" { + type = string +} + +variable "postgres_resources" { + type = object({ + requests = map(string) + limits = map(string) + }) + + default = { + "limits" = { + memory = "2Gi" + cpu = "4000m" + } + "requests" = { + memory = "2Gi" + cpu = "100m" + } + } +} + +variable "postgres_secret_name" { + type = string +} + +variable "postgres_config_name" { + type = string + default = null +} + +// PGBouncer +variable "pgbouncer_image_tag" { + default = "1.21.0" +} + +variable "pgbouncer_replicas" { + default = 1 +} + +variable "pgbouncer_user_settings" { + default = [] + type = list(object({ + name = string + password = string + max_connections = number + })) +} + +variable "pgbouncer_auth_user_password" { + type = string +} + +// Instance +variable "instances" { + type = map(object({ + salt = optional(string) + network = string + dbsync_image_tag = string + node_n2n_tcp_endpoint = string + release = string + sync_status = string + enable_postgrest = bool + topology_zone = optional(string) + dbsync_resources = optional(object({ + requests = map(string) + limits = map(string) + })) + dbsync_volume = optional(object({ + storage_class = string + size = string + })) + })) +} + diff --git a/bootstrap/feature/main.tf b/bootstrap/feature/main.tf index 24199ef..249216c 100644 --- a/bootstrap/feature/main.tf +++ b/bootstrap/feature/main.tf @@ -2,10 +2,6 @@ variable "namespace" { type = string } -variable "instance_name" { - type = string -} - variable "operator_image_tag" { type = string } diff --git a/bootstrap/main.tf b/bootstrap/main.tf new file mode 100644 index 0000000..040f764 --- /dev/null +++ b/bootstrap/main.tf @@ -0,0 +1,61 @@ +resource "kubernetes_namespace" "namespace" { + metadata { + name = var.namespace + } +} + +// Feature +module "dbsync_feature" { + depends_on = [kubernetes_namespace.namespace] + source = "./feature" + + namespace = var.namespace + operator_image_tag = var.operator_image_tag + metrics_delay = var.metrics_delay + dcu_per_second = var.dcu_per_second + postgres_password = var.postgres_password + postgres_secret_name = var.postgres_secret_name + pgbouncer_server_crt = var.pgbouncer_server_crt + pgbouncer_server_key = var.pgbouncer_server_key + + postgres_hosts = coalesce(var.postgres_hosts, [for key in keys(var.cells) : "postgres-dbsync-v3-${key}"]) +} + +// Service +module "dbsync_service" { + depends_on = [kubernetes_namespace.namespace] + source = "./service" + + namespace = var.namespace +} + +// Cells +module "dbsync_cells" { + depends_on = [module.dbsync_feature] + for_each = var.cells + source = "./cell" + + namespace = var.namespace + salt = each.key + + // PVC + volume_name = each.value.pvc.volume_name + storage_size = each.value.pvc.storage_size + db_volume_claim = each.value.pvc.name + + // PG + topology_zone = each.value.postgres.topology_zone + postgres_image_tag = each.value.postgres.image_tag + postgres_secret_name = var.postgres_secret_name + postgres_resources = each.value.postgres.resources + postgres_config_name = each.value.postgres.config_name + + // PGBouncer + pgbouncer_image_tag = var.pgbouncer_image_tag + pgbouncer_replicas = each.value.pgbouncer.replicas + pgbouncer_user_settings = var.pgbouncer_user_settings + pgbouncer_auth_user_password = var.pgbouncer_auth_user_password + + // Instances + instances = each.value.instances +} diff --git a/bootstrap/service/main.tf b/bootstrap/service/main.tf new file mode 100644 index 0000000..f55f6d8 --- /dev/null +++ b/bootstrap/service/main.tf @@ -0,0 +1,34 @@ +variable "namespace" { + type = string +} + +variable "service_name" { + default = "dbsync-v3-pgbouncer" +} + +resource "kubernetes_service_v1" "dbsync_v3_service" { + metadata { + namespace = var.namespace + name = var.service_name + annotations = { + "beta.kubernetes.io/aws-load-balancer-nlb-target-type" = "instance" + "service.beta.kubernetes.io/aws-load-balancer-scheme" = "internet-facing" + "service.beta.kubernetes.io/aws-load-balancer-type" = "external" + } + } + + spec { + type = "LoadBalancer" + load_balancer_class = "service.k8s.aws/nlb" + + port { + protocol = "TCP" + port = 5432 + target_port = 6432 + } + + selector = { + "role" = "pgbouncer" + } + } +} diff --git a/bootstrap/variables.tf b/bootstrap/variables.tf new file mode 100644 index 0000000..f5a8e06 --- /dev/null +++ b/bootstrap/variables.tf @@ -0,0 +1,133 @@ +variable "namespace" { + type = string +} + +// Feature +variable "operator_image_tag" { + type = string +} + +variable "metrics_delay" { + type = number + default = 60 +} + +variable "dcu_per_second" { + type = map(string) + default = { + "mainnet" = "10" + "preprod" = "5" + "preview" = "5" + "sanchonet" = "5" + } +} + +variable "postgres_secret_name" { + type = string + default = "postgres-secret" +} + +variable "postgres_password" { + type = string +} + +variable "pgbouncer_server_crt" { + type = string +} + +variable "pgbouncer_server_key" { + type = string +} + +variable "postgres_hosts" { + type = list(string) + default = null +} + +variable "operator_resources" { + type = object({ + limits = object({ + cpu = string + memory = string + }) + requests = object({ + cpu = string + memory = string + }) + }) + default = { + limits = { + cpu = "1" + memory = "512Mi" + } + requests = { + cpu = "50m" + memory = "512Mi" + } + } +} + +// PGBouncer +variable "pgbouncer_image_tag" { + type = string + default = "1.21.0" +} + +variable "pgbouncer_user_settings" { + default = [] + type = list(object({ + name = string + password = string + max_connections = number + })) +} + +variable "pgbouncer_auth_user_password" { + type = string +} + +variable "cells" { + type = map(object({ + pvc = object({ + volume_name = string + storage_size = string + name = optional(string) + }) + postgres = object({ + image_tag = string + topology_zone = string + config_name = optional(string) + resources = object({ + limits = object({ + cpu = string + memory = string + }) + requests = object({ + cpu = string + memory = string + }) + }) + }) + pgbouncer = object({ + replicas = number + }) + instances = map(object({ + salt = optional(string) + network = string + dbsync_image_tag = string + node_n2n_tcp_endpoint = string + release = string + sync_status = string + enable_postgrest = bool + topology_zone = optional(string) + dbsync_resources = optional(object({ + requests = map(string) + limits = map(string) + })) + dbsync_volume = optional(object({ + storage_class = string + size = string + })) + })) + })) +}