OCI Instance Configuration and Pools: Autoscaling Patterns with Terraform

OCI Instance Pools let you maintain a group of identically configured compute instances and scale them in and out based on demand. An Instance Configuration is the launch template: it captures the shape, image, network configuration, user data, and tags. The Instance Pool uses that configuration to add or remove instances when autoscaling policies trigger. This post covers creating an Instance Configuration, Instance Pool, load balancer integration, and CPU-based autoscaling with Terraform.

Step 1: Instance Configuration

resource "oci_core_instance_configuration" "api_server_config" {
  compartment_id = var.compartment_id
  display_name   = "orders-api-server-config-v2"

  instance_details {
    instance_type = "compute"

    launch_details {
      compartment_id = var.compartment_id
      shape          = "VM.Standard.E4.Flex"
      display_name   = "orders-api-server"

      shape_config {
        ocpus         = 4
        memory_in_gbs = 32
      }

      source_details {
        source_type = "image"
        image_id    = var.orders_api_image_id
      }

      create_vnic_details {
        subnet_id             = var.app_subnet_id
        nsg_ids               = [var.app_server_nsg_id]
        assign_public_ip      = false
      }

      metadata = {
        ssh_authorized_keys = var.ssh_public_key
        user_data           = base64encode(var.api_server_cloud_init)
      }

      defined_tags = {
        "Operations.Environment" = "production"
        "Operations.Application" = "orders"
        "Operations.ManagedBy"   = "terraform"
      }
    }
  }
}

output "instance_config_id" { value = oci_core_instance_configuration.api_server_config.id }

Step 2: Instance Pool with Load Balancer

resource "oci_core_instance_pool" "api_server_pool" {
  compartment_id            = var.compartment_id
  instance_configuration_id = oci_core_instance_configuration.api_server_config.id
  display_name              = "orders-api-server-pool"
  size                      = 3  # Baseline: 3 instances

  placement_configurations {
    availability_domain = data.oci_identity_availability_domains.ads.availability_domains[0].name
    primary_subnet_id   = var.app_subnet_id
    fault_domains       = ["FAULT-DOMAIN-1", "FAULT-DOMAIN-2", "FAULT-DOMAIN-3"]
  }

  # Automatically register instances with the load balancer backend set
  load_balancers {
    load_balancer_id = var.production_lb_id
    backend_set_name = "orders-api-backend-set"
    port             = 8080
    vnic_selection   = "PrimaryVnic"
  }

  defined_tags = { "Operations.Environment" = "production", "Operations.ManagedBy" = "terraform" }
}

output "pool_id" { value = oci_core_instance_pool.api_server_pool.id }

Step 3: Autoscaling Configuration

resource "oci_autoscaling_auto_scaling_configuration" "api_cpu_autoscaler" {
  compartment_id       = var.compartment_id
  display_name         = "orders-api-cpu-autoscaler"
  is_enabled           = true

  auto_scaling_resources {
    id   = oci_core_instance_pool.api_server_pool.id
    type = "instancePool"
  }

  policies {
    policy_type  = "threshold"
    display_name = "cpu-based-scaling"
    capacity {
      initial = 3
      min     = 2
      max     = 12
    }
    rules {
      display_name = "scale-out-high-cpu"
      action {
        type  = "CHANGE_COUNT_BY"
        value = 2  # Add 2 instances when scaling out
      }
      metric {
        metric_type = "CPU_UTILIZATION"
        threshold {
          operator = "GT"
          value    = 75
        }
      }
    }
    rules {
      display_name = "scale-in-low-cpu"
      action {
        type  = "CHANGE_COUNT_BY"
        value = -1  # Remove 1 instance when scaling in
      }
      metric {
        metric_type = "CPU_UTILIZATION"
        threshold {
          operator = "LT"
          value    = 30
        }
      }
    }
  }
}

# Scheduled scaling: pre-scale before known peak hours
resource "oci_autoscaling_auto_scaling_configuration" "api_scheduled_scaling" {
  compartment_id = var.compartment_id
  display_name   = "orders-api-scheduled-scale"
  is_enabled     = true

  auto_scaling_resources {
    id   = oci_core_instance_pool.api_server_pool.id
    type = "instancePool"
  }

  policies {
    policy_type  = "scheduled"
    display_name = "peak-hours-scale-up"
    execution_schedule {
      type       = "cron"
      expression = "0 7 * * MON-FRI"
      timezone   = "Asia/Riyadh"
    }
    resource_action {
      action_type = "power"
      action      = "START"
    }
    capacity {
      initial = 8
      min     = 6
      max     = 12
    }
  }
}

Operational Notes

Instance Pools register new instances with the load balancer backend set automatically when the pool scales out and deregister them when scaling in. The load balancer health check must pass before the pool counts the instance as healthy. If your health check is slow, new instances may receive traffic before the application is fully initialized. Use a dedicated startup health endpoint that returns 200 only after all application dependencies (database connections, cache warmup) are ready.

Scale in conservatively and scale out aggressively. Set the scale-in action to remove one instance at a time and require the CPU threshold to be sustained for 10 minutes before scale-in triggers. Scale out by 2 instances immediately when CPU exceeds the threshold. This asymmetry prevents removing capacity too quickly during brief idle periods while still adding capacity fast when demand spikes.

Regards,
Osama

#OCI #OracleCloud #Autoscaling #InstancePool #Terraform #IaC #TechBlog #Oracle #PlatformEngineering #CloudCompute #FinOps #OracleCloudInfrastructure #LoadBalancer #InstanceConfiguration #ScaleOut #ScheduledScaling #HighAvailability #CloudNative #DevOps #ComputeOptimization

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.