|
| 1 | +# Container Instances (mirror aws/ecs.tf services). |
| 2 | +# |
| 3 | +# OCI Container Instances have NO autoscaling and NO rolling deploys — each |
| 4 | +# instance is a fixed-size unit. Scale by raising api_count / web_count / |
| 5 | +# worker_count; deploy new images by restarting (image tag "latest" is |
| 6 | +# re-pulled on restart) or recreating instances. See README Notes. |
| 7 | + |
| 8 | +locals { |
| 9 | + availability_domain = data.oci_identity_availability_domains.main.availability_domains[0].name |
| 10 | + |
| 11 | + # Shared backend environment (api + worker), mirrors aws/ecs.tf |
| 12 | + # backend_environment (OCI env vars are a map, not a name/value list) |
| 13 | + backend_environment = { |
| 14 | + ENVIRONMENT = var.environment |
| 15 | + OCI_REGION = var.region |
| 16 | + DATABASE_HOST = oci_psql_db_system.main.network_details[0].primary_db_endpoint_private_ip |
| 17 | + # Read scaling guide: add when read replicas are enabled (see postgres.tf) |
| 18 | + # DATABASE_READ_HOST = <reader endpoint> |
| 19 | + DATABASE_NAME = var.db_name |
| 20 | + DATABASE_USER = var.db_user |
| 21 | + DATABASE_PASSWORD = var.DATABASE_PASSWORD |
| 22 | + REDIS_HOST = oci_redis_redis_cluster.main.primary_fqdn |
| 23 | + REDIS_PORT = "6379" |
| 24 | + REDIS_TLS = "true" |
| 25 | + STORAGE_BUCKET = oci_objectstorage_bucket.uploads.name |
| 26 | + STORAGE_NAMESPACE = local.namespace |
| 27 | + QUEUE_IDS = jsonencode({ |
| 28 | + for name in local.queue_names : name => oci_queue_queue.main[name].id |
| 29 | + }) |
| 30 | + QUEUE_MESSAGES_ENDPOINT = oci_queue_queue.main["default"].messages_endpoint |
| 31 | + } |
| 32 | +} |
| 33 | + |
| 34 | +# API |
| 35 | +resource "oci_container_instances_container_instance" "api" { |
| 36 | + count = var.api_count |
| 37 | + |
| 38 | + compartment_id = oci_identity_compartment.main.id |
| 39 | + availability_domain = local.availability_domain |
| 40 | + display_name = "${local.name_prefix}-api-${count.index}" |
| 41 | + |
| 42 | + shape = var.container_shape |
| 43 | + shape_config { |
| 44 | + ocpus = var.api_ocpus |
| 45 | + memory_in_gbs = var.api_memory_in_gbs |
| 46 | + } |
| 47 | + |
| 48 | + container_restart_policy = "ALWAYS" |
| 49 | + |
| 50 | + containers { |
| 51 | + display_name = "api" |
| 52 | + image_url = local.api_image |
| 53 | + |
| 54 | + environment_variables = merge(local.backend_environment, { |
| 55 | + JWT_SECRET = var.JWT_SECRET |
| 56 | + API_URL = local.api_url |
| 57 | + }) |
| 58 | + |
| 59 | + health_checks { |
| 60 | + health_check_type = "HTTP" |
| 61 | + port = 8000 |
| 62 | + path = "/health" |
| 63 | + interval_in_seconds = 30 |
| 64 | + timeout_in_seconds = 5 |
| 65 | + failure_threshold = 3 |
| 66 | + initial_delay_in_seconds = 60 |
| 67 | + failure_action = "KILL" |
| 68 | + } |
| 69 | + } |
| 70 | + |
| 71 | + vnics { |
| 72 | + subnet_id = oci_core_subnet.private.id |
| 73 | + nsg_ids = [oci_core_network_security_group.apps.id] |
| 74 | + is_public_ip_assigned = false |
| 75 | + } |
| 76 | + |
| 77 | + freeform_tags = local.common_tags |
| 78 | +} |
| 79 | + |
| 80 | +# Web |
| 81 | +resource "oci_container_instances_container_instance" "web" { |
| 82 | + count = var.web_count |
| 83 | + |
| 84 | + compartment_id = oci_identity_compartment.main.id |
| 85 | + availability_domain = local.availability_domain |
| 86 | + display_name = "${local.name_prefix}-web-${count.index}" |
| 87 | + |
| 88 | + shape = var.container_shape |
| 89 | + shape_config { |
| 90 | + ocpus = var.web_ocpus |
| 91 | + memory_in_gbs = var.web_memory_in_gbs |
| 92 | + } |
| 93 | + |
| 94 | + container_restart_policy = "ALWAYS" |
| 95 | + |
| 96 | + containers { |
| 97 | + display_name = "web" |
| 98 | + image_url = local.web_image |
| 99 | + |
| 100 | + environment_variables = { |
| 101 | + ENVIRONMENT = var.environment |
| 102 | + NEXT_PUBLIC_API_URL = local.api_url |
| 103 | + BETTER_AUTH_SECRET = var.BETTER_AUTH_SECRET |
| 104 | + BETTER_AUTH_URL = local.web_url |
| 105 | + GOOGLE_CLIENT_ID = var.GOOGLE_CLIENT_ID |
| 106 | + GOOGLE_CLIENT_SECRET = var.GOOGLE_CLIENT_SECRET |
| 107 | + GITHUB_CLIENT_ID = var.GITHUB_CLIENT_ID |
| 108 | + GITHUB_CLIENT_SECRET = var.GITHUB_CLIENT_SECRET |
| 109 | + KAKAO_CLIENT_ID = var.KAKAO_CLIENT_ID |
| 110 | + KAKAO_CLIENT_SECRET = var.KAKAO_CLIENT_SECRET |
| 111 | + } |
| 112 | + |
| 113 | + health_checks { |
| 114 | + health_check_type = "HTTP" |
| 115 | + port = 3000 |
| 116 | + path = "/api/health" |
| 117 | + interval_in_seconds = 30 |
| 118 | + timeout_in_seconds = 5 |
| 119 | + failure_threshold = 3 |
| 120 | + initial_delay_in_seconds = 60 |
| 121 | + failure_action = "KILL" |
| 122 | + } |
| 123 | + } |
| 124 | + |
| 125 | + vnics { |
| 126 | + subnet_id = oci_core_subnet.private.id |
| 127 | + nsg_ids = [oci_core_network_security_group.apps.id] |
| 128 | + is_public_ip_assigned = false |
| 129 | + } |
| 130 | + |
| 131 | + freeform_tags = local.common_tags |
| 132 | +} |
| 133 | + |
| 134 | +# Worker (no ingress — consumes queues) |
| 135 | +resource "oci_container_instances_container_instance" "worker" { |
| 136 | + count = var.worker_count |
| 137 | + |
| 138 | + compartment_id = oci_identity_compartment.main.id |
| 139 | + availability_domain = local.availability_domain |
| 140 | + display_name = "${local.name_prefix}-worker-${count.index}" |
| 141 | + |
| 142 | + shape = var.container_shape |
| 143 | + shape_config { |
| 144 | + ocpus = var.worker_ocpus |
| 145 | + memory_in_gbs = var.worker_memory_in_gbs |
| 146 | + } |
| 147 | + |
| 148 | + container_restart_policy = "ALWAYS" |
| 149 | + |
| 150 | + containers { |
| 151 | + display_name = "worker" |
| 152 | + image_url = local.worker_image |
| 153 | + |
| 154 | + environment_variables = merge(local.backend_environment, { |
| 155 | + OPENAI_API_KEY = var.OPENAI_API_KEY |
| 156 | + ANTHROPIC_API_KEY = var.ANTHROPIC_API_KEY |
| 157 | + GOOGLE_AI_API_KEY = var.GOOGLE_AI_API_KEY |
| 158 | + }) |
| 159 | + } |
| 160 | + |
| 161 | + vnics { |
| 162 | + subnet_id = oci_core_subnet.private.id |
| 163 | + nsg_ids = [oci_core_network_security_group.apps.id] |
| 164 | + is_public_ip_assigned = false |
| 165 | + } |
| 166 | + |
| 167 | + freeform_tags = local.common_tags |
| 168 | +} |
| 169 | + |
| 170 | +# The container instance resource exports only vnic_id — resolve private IPs |
| 171 | +# for the LB backends through the VNIC data source. |
| 172 | +data "oci_core_vnic" "api" { |
| 173 | + count = var.api_count |
| 174 | + vnic_id = oci_container_instances_container_instance.api[count.index].vnics[0].vnic_id |
| 175 | +} |
| 176 | + |
| 177 | +data "oci_core_vnic" "web" { |
| 178 | + count = var.web_count |
| 179 | + vnic_id = oci_container_instances_container_instance.web[count.index].vnics[0].vnic_id |
| 180 | +} |
0 commit comments