The OCI Application Load Balancer terminates HTTP, inspects headers, routes by path, and applies WAF rules. That processing costs latency. When your workload is TCP-based, does not need HTTP inspection, or requires preserving the client source IP all the way to the backend, the Network Load Balancer is the correct choice. It operates at layer 4, passes packets without termination, and delivers lower latency with higher throughput.
When to Use the Network Load Balancer
Use the Network Load Balancer for non-HTTP workloads: Oracle Database listener on TCP 1521, gRPC with true pass-through, DNS over TCP and UDP, and any workload where the backend needs to see the real client IP. The Application Load Balancer always presents its own IP to the backend, not the client IP. The Network Load Balancer passes the original source IP through unchanged when source preservation is enabled.
Step 1: Provision the NLB
resource "oci_network_load_balancer_network_load_balancer" "production_nlb" {
compartment_id = var.compartment_id
display_name = "production-nlb"
subnet_id = var.private_subnet_id
is_private = true
is_preserve_source_destination = true
network_security_group_ids = [var.nlb_nsg_id]
defined_tags = {
"Operations.Environment" = "production"
"Operations.ManagedBy" = "terraform"
}
}
output "nlb_ip_addresses" {
value = oci_network_load_balancer_network_load_balancer.production_nlb.ip_addresses
}
Step 2: Backend Set for Oracle Database
resource "oci_network_load_balancer_backend_set" "oracle_db" {
network_load_balancer_id = oci_network_load_balancer_network_load_balancer.production_nlb.id
name = "oracle-db-backend-set"
policy = "FIVE_TUPLE"
is_preserve_source = true
health_checker {
protocol = "TCP"
port = 1521
interval_in_millis = 10000
timeout_in_millis = 3000
retries = 3
}
}
resource "oci_network_load_balancer_backend" "db_primary" {
network_load_balancer_id = oci_network_load_balancer_network_load_balancer.production_nlb.id
backend_set_name = oci_network_load_balancer_backend_set.oracle_db.name
name = "db-primary"
ip_address = var.db_primary_ip
port = 1521
weight = 100
is_backup = false
is_drain = false
is_offline = false
}
resource "oci_network_load_balancer_backend" "db_standby" {
network_load_balancer_id = oci_network_load_balancer_network_load_balancer.production_nlb.id
backend_set_name = oci_network_load_balancer_backend_set.oracle_db.name
name = "db-standby"
ip_address = var.db_standby_ip
port = 1521
weight = 1
is_backup = true
is_drain = false
is_offline = false
}
Step 3: Listeners for Oracle DB and DNS
resource "oci_network_load_balancer_listener" "oracle_db_tcp" {
network_load_balancer_id = oci_network_load_balancer_network_load_balancer.production_nlb.id
name = "oracle-db-tcp-1521"
default_backend_set_name = oci_network_load_balancer_backend_set.oracle_db.name
port = 1521
protocol = "TCP"
}
resource "oci_network_load_balancer_backend_set" "dns_servers" {
network_load_balancer_id = oci_network_load_balancer_network_load_balancer.production_nlb.id
name = "dns-backend-set"
policy = "THREE_TUPLE"
health_checker {
protocol = "UDP"
port = 53
interval_in_millis = 10000
timeout_in_millis = 3000
retries = 3
}
}
resource "oci_network_load_balancer_listener" "dns_udp" {
network_load_balancer_id = oci_network_load_balancer_network_load_balancer.production_nlb.id
name = "dns-udp-53"
default_backend_set_name = oci_network_load_balancer_backend_set.dns_servers.name
port = 53
protocol = "UDP"
}
resource "oci_network_load_balancer_listener" "dns_tcp" {
network_load_balancer_id = oci_network_load_balancer_network_load_balancer.production_nlb.id
name = "dns-tcp-53"
default_backend_set_name = oci_network_load_balancer_backend_set.dns_servers.name
port = 53
protocol = "TCP"
}
Step 4: NSG Rules for Source IP Preservation
resource "oci_core_network_security_group_security_rule" "nlb_healthcheck_to_db" {
network_security_group_id = var.db_nsg_id
direction = "INGRESS"
protocol = "6"
source_type = "CIDR_BLOCK"
source = var.nlb_subnet_cidr
tcp_options {
destination_port_range { min = 1521; max = 1521 }
}
description = "Allow NLB health checks to Oracle DB"
}
# Critical: with source IP preservation, client IPs arrive at the backend
# Backend NSG must allow the CLIENT subnet, not just the NLB subnet
resource "oci_core_network_security_group_security_rule" "client_to_db_via_nlb" {
network_security_group_id = var.db_nsg_id
direction = "INGRESS"
protocol = "6"
source_type = "CIDR_BLOCK"
source = var.app_subnet_cidr
tcp_options {
destination_port_range { min = 1521; max = 1521 }
}
description = "Allow app tier connections to Oracle DB via NLB with source IP preserved"
}
Step 5: Health Check Alarm
resource "oci_monitoring_alarm" "nlb_unhealthy_backend" {
compartment_id = var.compartment_id
display_name = "nlb-unhealthy-backend"
is_enabled = true
metric_compartment_id = var.compartment_id
namespace = "oci_nlb"
query = "UnhealthyBackendServers[5m]{networkLoadBalancerId = 'NLB_OCID'}.max() > 0"
severity = "CRITICAL"
pending_duration = "PT5M"
destinations = [var.ops_notification_topic_id]
body = "One or more backends on the production NLB are failing health checks. Check database listener status immediately."
}
Operational Notes
When is_preserve_source_destination = true, traffic arrives at the backend with the original client IP as the source. Backend NSG rules must allow the client subnet CIDR, not just the NLB subnet CIDR. This is the most common NLB configuration mistake: NSG rules allow the NLB IP, health checks pass, but client connections are dropped because the backend NSG does not permit the actual client source IP.
The FIVE_TUPLE policy hashes on source IP, source port, destination IP, destination port, and protocol. For Oracle Database connections this ensures the same client consistently reaches the same backend, maintaining session stickiness without application-layer session management. For stateless workloads, THREE_TUPLE or TWO_TUPLE are simpler and sufficient.
Regards,
Osama
#OCI #OracleCloud #NetworkLoadBalancer #Terraform #IaC #Networking #TechBlog #Oracle #PlatformEngineering #CloudNetworking #OracleCloudInfrastructure #Layer4 #LoadBalancing #HighAvailability #OracleDatabase #TCP #UDP #HealthChecks #NSG #DevOps
Leave a comment