The OCI Application Load Balancer operates at layer 7 and handles everything the Network Load Balancer does not: TLS termination, HTTP path routing, header transformation, WAF integration, SSL cipher policy enforcement, and session persistence. This post covers building a production load balancer configuration in Terraform with SSL termination using OCI Certificates, path-based routing to multiple backend sets, and custom health check configuration.
Step 1: Load Balancer Provisioning
resource "oci_load_balancer_load_balancer" "production_lb" {
compartment_id = var.compartment_id
display_name = "production-application-lb"
shape = "flexible"
shape_details {
minimum_bandwidth_in_mbps = 100
maximum_bandwidth_in_mbps = 1000
}
is_private = false
subnet_ids = [var.public_subnet_id]
network_security_group_ids = [var.lb_nsg_id]
defined_tags = { "Operations.Environment" = "production", "Operations.ManagedBy" = "terraform" }
}
output "lb_public_ip" {
value = oci_load_balancer_load_balancer.production_lb.ip_addresses[0].ip_address
}
Step 2: Backend Sets
resource "oci_load_balancer_backend_set" "orders_api" {
load_balancer_id = oci_load_balancer_load_balancer.production_lb.id
name = "orders-api-backend-set"
policy = "ROUND_ROBIN"
health_checker {
protocol = "HTTP"
url_path = "/health"
port = 8080
return_code = 200
interval_ms = 10000
timeout_in_millis = 3000
retries = 3
# Only consider backend healthy on stable response
is_force_plain_text = true
}
session_persistence_configuration {
cookie_name = "orders-session"
disable_fallback = false
}
}
resource "oci_load_balancer_backend" "orders_api_1" {
load_balancer_id = oci_load_balancer_load_balancer.production_lb.id
backendset_name = oci_load_balancer_backend_set.orders_api.name
ip_address = var.orders_api_instance_1_ip
port = 8080
weight = 50
backup = false
drain = false
offline = false
}
resource "oci_load_balancer_backend" "orders_api_2" {
load_balancer_id = oci_load_balancer_load_balancer.production_lb.id
backendset_name = oci_load_balancer_backend_set.orders_api.name
ip_address = var.orders_api_instance_2_ip
port = 8080
weight = 50
backup = false
drain = false
offline = false
}
resource "oci_load_balancer_backend_set" "payments_api" {
load_balancer_id = oci_load_balancer_load_balancer.production_lb.id
name = "payments-api-backend-set"
policy = "LEAST_CONNECTIONS"
health_checker {
protocol = "HTTP"
url_path = "/health"
port = 8081
return_code = 200
interval_ms = 10000
timeout_in_millis = 3000
retries = 3
}
}
Step 3: HTTPS Listener with Path Routing
resource "oci_load_balancer_cipher_suite" "modern_tls" {
load_balancer_id = oci_load_balancer_load_balancer.production_lb.id
name = "modern-tls-cipher-suite"
ciphers = [
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384"
]
}
resource "oci_load_balancer_rule_set" "api_path_routing" {
load_balancer_id = oci_load_balancer_load_balancer.production_lb.id
name = "api-path-routing"
items {
action = "REDIRECT"
conditions {
attribute_name = "PATH"
attribute_value = "/api/orders"
operator = "PREFIX_MATCH"
}
redirect_uri {
protocol = "HTTPS"
host = "{host}"
port = "443"
path = "/api/v2/orders"
query = "{query}"
}
response_code = 308
}
}
resource "oci_load_balancer_listener" "https" {
load_balancer_id = oci_load_balancer_load_balancer.production_lb.id
name = "https-listener"
default_backend_set_name = oci_load_balancer_backend_set.orders_api.name
port = 443
protocol = "HTTP"
rule_set_names = [oci_load_balancer_rule_set.api_path_routing.name]
ssl_configuration {
certificate_ids = [var.tls_certificate_id]
server_order_preference = "ENABLED"
cipher_suite_name = oci_load_balancer_cipher_suite.modern_tls.name
protocols = ["TLSv1.2", "TLSv1.3"]
verify_peer_certificate = false
}
connection_configuration {
idle_timeout_in_seconds = 60
}
}
resource "oci_load_balancer_listener" "http_redirect" {
load_balancer_id = oci_load_balancer_load_balancer.production_lb.id
name = "http-to-https-redirect"
default_backend_set_name = oci_load_balancer_backend_set.orders_api.name
port = 80
protocol = "HTTP"
rule_set_names = ["http-to-https-redirect-ruleset"]
}
Step 4: Unhealthy Backend Alarm
resource "oci_monitoring_alarm" "lb_unhealthy_backend" {
compartment_id = var.compartment_id
display_name = "lb-unhealthy-backend"
is_enabled = true
metric_compartment_id = var.compartment_id
namespace = "oci_lbaas"
query = "UnHealthyBackendServers[5m]{loadBalancerId = 'LB_OCID'}.max() > 0"
severity = "CRITICAL"
pending_duration = "PT5M"
destinations = [var.ops_notification_topic_id]
body = "One or more backends on the production load balancer are failing health checks. Check application health endpoints and instance connectivity."
}
Operational Notes
Set the health check interval to 10 seconds and retries to 3 for production backends. This marks a backend unhealthy after 30 seconds of failures and removes it from rotation. Too aggressive an interval generates a high volume of health check requests to your backends. Too lenient an interval means unhealthy backends serve client requests for too long before being removed. 10 seconds with 3 retries is a good production balance.
Use OCI Certificates Service to manage the TLS certificate rather than uploading the certificate directly to the load balancer. When the certificate auto-renews in Certificates Service, the load balancer picks up the new version automatically on the next TLS handshake without any configuration change. Certificates uploaded directly to the load balancer require a manual update when they expire.
Regards,
Osama
#OCI #OracleCloud #LoadBalancer #SSL #TLS #Terraform #IaC #TechBlog #Oracle #PlatformEngineering #CloudNetworking #OracleCloudInfrastructure #PathRouting #HealthCheck #CipherSuite #SessionPersistence #Certificates #HighAvailability #HTTPS #WebTraffic
Leave a Reply