my blog

$ cat aws-with-terraform/part-03.md

Advanced Load Balancing on AWS: HTTP Headers, Query Strings, and Network Load Balancer with Auto Scaling

Why I Built These Projects

After basic ALB routing, I wanted to explore the really advanced stuff the kind of routing that powers modern SaaS applications and high-performance systems. I built two projects:

  1. Advanced ALB Routing – HTTP headers, query string redirects, and complex routing logic
  2. Network Load Balancer with Auto Scaling – Layer 4 load balancing with dynamic instance scaling

These projects taught me how production systems handle complex traffic patterns and scale automatically under load.

Project 1: Advanced ALB Routing with HTTP Headers

What Makes This Advanced?

Basic ALB routes by path (/app1) or host (app1.domain.com). Advanced ALB can route based on:

  • HTTP headers – Custom headers in requests
  • Query strings – URL parameters like ?website=aws-eks
  • HTTP header redirects – Redirect specific domains to external URLs
  • Multiple conditions – Combine different routing rules

Real-World Use Cases

HTTP Header Routing:

Mobile app sends: Header: “User-Agent: MyApp/iOS/1.0”

→ Routes to mobile-optimized backend

API client sends: Header: “API-Version: v2”

→ Routes to v2 API servers

Query String Redirects:

Old marketing URL: example.com?campaign=summer-sale

→ Redirects to: marketing.example.com/summer-sale

Documentation link: docs.com?topic=kubernetes

→ Redirects to: docs.com/kubernetes/

Technical Implementation – Advanced ALB

1. HTTP Header-Based Routing

This is my actual configuration for routing based on custom HTTP headers:

rules = {
  # Rule 1: Route App1 based on custom header
  myapp1-rule = {
    actions = [{
      type = "weighted-forward"
      target_groups = [
        {
          target_group_key = "mytg1"
          weight           = 1
        }
      ]
      stickiness = {
        enabled  = true
        duration = 3600
      }
    }]
    conditions = [{
      http_header = {
        http_header_name = "custom-header"
        values = ["app-1", "app1", "my-app-1"]
      }
    }]
  }

How HTTP header routing works:

Request with header:

GET / HTTP/1.1
Host: myapps11.rezaops.com
custom-header: app-1

ALB checks:

  1. Does request have custom-header? YES
  2. Is value app-1, app1, or my-app-1? YES
  3. Route to mytg1 (App1 target group)

Request without header or different value:

GET / HTTP/1.1
Host: myapps11.rezaops.com

→ Falls through to default action (fixed response)

Multiple header values:

values = ["app-1", "app1", "my-app-1"]

ALB accepts ANY of these values. This is useful for:

  • Different naming conventions: app-1 vs app1
  • Legacy compatibility: old clients use my-app-1, new use app1
  • API versioning: v1, version-1, api-v1

Testing HTTP header routing:

# Route to App1
curl -H "custom-header: app-1" https://myapps11.rezaops.com

# Route to App2
curl -H "custom-header: app-2" https://myapps11.rezaops.com

# No header - fixed response
curl https://myapps11.rezaops.com

2. Query String Redirect Rule

Redirect based on URL query parameters:

my-redirect-query = {
  priority = 3
  actions = [{
    type        = "redirect"
    status_code = "HTTP_302"
    host        = "stacksimplify.com"
    path        = "/aws-eks/"
    query       = ""  # Clear original query string
    protocol    = "HTTPS"
  }]

  conditions = [{
    query_string = {
      key   = "website"
      value = "aws-eks"
    }
  }]
}

What happens step by step:

User visits: https://myapps11.rezaops.com?website=aws-eks

  1. ALB receives request
  2. Checks query string: website=aws-eks
  3. Matches condition ✓
  4. Returns HTTP 302 redirect:
HTTP/1.1 302 Found
Location: https://stacksimplify.com/aws-eks/
  1. Browser automatically follows redirect
  2. User ends up at external website

Why HTTP 302 instead of 301?

302 = Temporary Redirect

  • Browser doesn’t cache
  • Can change redirect destination later
  • Analytics track each redirect
  • Used for: campaigns, tracking, temporary moves

301 = Permanent Redirect

  • Browser caches forever
  • SEO passes page rank
  • Can’t easily change later
  • Used for: permanent URL changes

Clearing query string:

query = ""

Original URL: myapps11.rezaops.com?website=aws-eks&utm=source Redirect to: stacksimplify.com/aws-eks/ (no query string)

To keep query string:

query = "#{query_string}"  # Pass through original

3. Host Header Redirect Rule

Redirect entire domains to external URLs:

my-redirect-hh = {
  priority = 4
  actions = [{
    type        = "redirect"
    status_code = "HTTP_302"
    host        = "stacksimplify.com"
    path        = "/azure-aks/azure-kubernetes-service-introduction/"
    query       = ""
    protocol    = "HTTPS"
  }]

  conditions = [{
    host_header = {
      values = ["azure-aks11.rezaops.com"]
    }
  }]
}

Use case: Domain migration

Old domain: azure-aks11.rezaops.com New domain: stacksimplify.com/azure-aks/…

Any request to old domain redirects to new location:

https://azure-aks11.rezaops.com

→ https://stacksimplify.com/azure-aks/azure-kubernetes-service-introduction/

https://azure-aks11.rezaops.com/docs

→ https://stacksimplify.com/azure-aks/azure-kubernetes-service-introduction/

https://azure-aks11.rezaops.com/anything

→ https://stacksimplify.com/azure-aks/azure-kubernetes-service-introduction/

Notice: ALL paths redirect to same destination. To preserve paths:

path = "/azure-aks#{path}"  # Appends original path

4. Rule Priority and Evaluation Order

myapp1-rule = { }      # Priority 1 (automatic)
myapp2-rule = { }      # Priority 2 (automatic)
my-redirect-query = {
  priority = 3         # Explicit priority
}
my-redirect-hh = {
  priority = 4
}

How ALB evaluates rules:

  1. Starts with priority 1
  2. Checks conditions
  3. If match → Execute action, STOP
  4. If no match → Try priority 2
  5. Continue until match or reach default action

Example request flow:

Request: GET /?website=aws-eks HTTP/1.1

  • Priority 1 (HTTP header): No custom-header → SKIP
  • Priority 2 (HTTP header): No custom-header → SKIP
  • Priority 3 (Query string): Has website=aws-eks → MATCH! Redirect to stacksimplify.com

Request: GET / HTTP/1.1 with custom-header: app-1

  • Priority 1 (HTTP header): Has custom-header: app-1 → MATCH! Forward to App1
  • (Rules 2, 3, 4 never evaluated)

5. DNS Configuration for Multiple Domains

I create Route53 records for different routing scenarios:

# Main domain for header-based routing
resource "aws_route53_record" "default_dns" {
  zone_id = data.aws_route53_zone.mydomain.zone_id
  name = "myapps11.rezaops.com"
  type = "A"
  
  alias {
    name = module.alb.dns_name
    zone_id = module.alb.zone_id
    evaluate_target_health = true
  }
}

# Domain for host header redirect
resource "aws_route53_record" "app1_dns" {
  zone_id = data.aws_route53_zone.mydomain.zone_id
  name = "azure-aks11.rezaops.com"
  type = "A"
  
  alias {
    name = module.alb.dns_name
    zone_id = module.alb.zone_id
    evaluate_target_health = true
  }
}

Both domains point to SAME ALB! The ALB decides what to do based on Host header:

myapps11.rezaops.com → Check for custom headers or query strings

azure-aks11.rezaops.com → Redirect to stacksimplify.com

Project 2: Network Load Balancer with Auto Scaling

NLB vs ALB – When to Use Each

Application Load Balancer (Layer 7):

  • HTTP/HTTPS only
  • Content-based routing (paths, headers, cookies)
  • SSL termination
  • WebSocket support
  • Slower (parses HTTP)

Network Load Balancer (Layer 4):

  • TCP/UDP/TLS protocols
  • Ultra-low latency (~100 microseconds)
  • Millions of requests per second
  • Static IP addresses
  • Preserves source IP
  • No content inspection

When I use NLB:

  • Gaming servers (low latency critical)
  • IoT applications (millions of devices)
  • TCP-based protocols (non-HTTP)
  • Need static IP for whitelisting
  • Extreme performance requirements

Technical Implementation – NLB

1. NLB Configuration

module "nlb" {
  source  = "terraform-aws-modules/alb/aws"
  version = "9.11.0"

  name = "mynlb"
  load_balancer_type = "network"  # KEY DIFFERENCE
  vpc_id = module.vpc.vpc_id
  subnets = module.vpc.public_subnets
  dns_record_client_routing_policy = "availability_zone_affinity"
  security_groups = [module.loadbalancer_sg.security_group_id]
  
  enable_deletion_protection = false

Key parameter: dns_record_client_routing_policy

dns_record_client_routing_policy = "availability_zone_affinity"

This means:

Client in us-east-1a → Connects to NLB node in us-east-1a

Client in us-east-1b → Connects to NLB node in us-east-1b

Benefits:

  • Lower latency (same AZ = faster)
  • Lower data transfer costs (no cross-AZ charges)
  • Better performance for latency-sensitive apps

Alternative value:

dns_record_client_routing_policy = "any_availability_zone"

Distributes traffic evenly, ignoring client location.

2. TCP and TLS Listeners

listeners = {
  # Listener 1: Plain TCP
  my-tcp = {
    port     = 80
    protocol = "TCP"
    forward = {
      target_group_key = "mytg1"
    }
  }
  
  # Listener 2: TLS (encrypted TCP)
  my-tls = {
    port            = 443
    protocol        = "TLS"
    certificate_arn = module.acm.acm_certificate_arn
    forward = {
      target_group_key = "mytg1"
    }
  }
}

TCP vs TLS listeners:

TCP (port 80):

  • No encryption
  • Load balancer passes packets directly
  • Client connects directly to backend instance
  • Fastest (no decryption overhead)

TLS (port 443):

  • Load balancer terminates SSL/TLS
  • Decrypts traffic at NLB
  • Forwards plain HTTP to backends
  • Client sees encrypted connection
  • Backend sees plain HTTP

Traffic flow comparison:

ALB with HTTPS:

Client --HTTPS--> ALB --HTTP--> Instance
       (decrypts here)

NLB with TLS:

Client --TLS--> NLB --TCP--> Instance
      (decrypts here)

NLB with TCP passthrough:

Client --TCP--> NLB --TCP--> Instance
       (no encryption, pure speed)

3. Auto Scaling Group with Launch Template

Launch template defines what instances look like:

resource "aws_launch_template" "my_launch_template" {
  name = "my_launch_template"
  description = "My launch template"
  image_id = data.aws_ami.amzlinux2.id
  instance_type = var.instance_type
  
  vpc_security_group_ids = [module.public_bastion_sg.security_group_id]
  key_name = var.instance_keypair
  user_data = filebase64("${path.module}/app1-install.sh")
  
  ebs_optimized = true
  update_default_version = true

  block_device_mappings {
    device_name = "/dev/sda1"
    ebs {
      volume_size = 20
      volume_type = "gp2"
      delete_on_termination = true
    }
  }
  
  monitoring {
    enabled = true  # Detailed CloudWatch monitoring
  }
  
  tag_specifications {
    resource_type = "instance"
    tags = {
      Name = "myasg"
    }
  }
}

ebs_optimized = true

  • Dedicated bandwidth for EBS operations
  • Better disk I/O performance
  • Recommended for production

update_default_version = true

  • New version becomes default automatically
  • ASG uses latest version for new instances
  • Old instances keep old version until replaced

Monitoring enabled:

  • 1-minute metrics instead of 5-minute
  • More granular CloudWatch data
  • Costs extra ($2.10/instance/month)
  • Essential for auto scaling responsiveness

4. Auto Scaling Group Configuration

resource "aws_autoscaling_group" "my_asg" {
  name_prefix = "myasg_"
  desired_capacity = 2
  max_size = 5
  min_size = 2
  vpc_zone_identifier = module.vpc.public_subnets

  target_group_arns = [module.nlb.target_groups["mytg1"].arn] 
  health_check_type = "EC2"

  launch_template {
    id = aws_launch_template.my_launch_template.id
    version = aws_launch_template.my_launch_template.latest_version
  }
  
  instance_refresh {
    strategy = "Rolling"
    preferences {
      min_healthy_percentage = 50
    }
    triggers = [ "desired_capacity" ]
  }

  tag {
    key = "Owners"
    value = "Web-Team"
    propagate_at_launch = true
  }
}

Instance refresh – Rolling updates:

instance_refresh {
  strategy = "Rolling"
  preferences {
    min_healthy_percentage = 50
  }
}

What happens when I update launch template:

Without instance refresh:

  • Old instances keep running
  • New instances use new template
  • Mixed versions in ASG

With instance refresh (rolling):

  1. ASG has 4 instances
  2. Terminates 2 instances (50%)
  3. Launches 2 new instances with new template
  4. Waits for health checks
  5. Terminates remaining 2 old instances
  6. Launches 2 more new instances
  7. All instances now use new template

min_healthy_percentage = 50:

  • Always keeps 50% instances running
  • Lower = faster updates, more risk
  • Higher = slower updates, safer

For production:

min_healthy_percentage = 90  # Keep 90% healthy

health_check_type = “EC2”

Options:

  • EC2 – Checks if instance is running
  • ELB – Checks load balancer health checks

I use EC2 for simplicity. For production, use ELB for better reliability.

5. Target Tracking Scaling Policy

This is the magic that makes auto scaling work:

resource "aws_autoscaling_policy" "avg_cpu_policy_greater_than_xx" {
  name = "avg-cpu-policy-greater-than-xx"
  policy_type = "TargetTrackingScaling"
  autoscaling_group_name = aws_autoscaling_group.my_asg.id 
  estimated_instance_warmup = 180  # 3 minutes
  
  target_tracking_configuration {
    predefined_metric_specification {
      predefined_metric_type = "ASGAverageCPUUtilization"
    }
    target_value = 50.0  # Keep CPU at 50%
  }
}

How target tracking works:

Current state: 2 instances, average CPU = 30%

  • Below target (50%)
  • No action needed

Traffic increases: 2 instances, average CPU = 70%

  • Above target!
  • ASG: “I need more instances to bring average down to 50%”
  • Launches 1 more instance
  • Now: 3 instances, average CPU = 47% ✓

Traffic increases more: 3 instances, average CPU = 65%

  • Still above target
  • Launches 1 more instance
  • Now: 4 instances, average CPU = 49% ✓

Traffic decreases: 4 instances, average CPU = 30%

  • Below target
  • ASG: “I have too many instances”
  • Terminates 1 instance
  • Now: 3 instances, average CPU = 40% ✓

estimated_instance_warmup = 180

When new instance launches:

  • Takes ~60 seconds to boot
  • Another ~60 seconds to install Apache
  • Another ~30 seconds to get first traffic
  • Total: ~150 seconds to be “production ready”

During warmup period:

  • Instance doesn’t count toward CPU average
  • Prevents premature scaling decisions
  • Avoids scaling thrashing

Alternative predefined metrics:

# Request count per target
predefined_metric_type = "ALBRequestCountPerTarget"
target_value = 1000  # 1000 requests per instance

# Network bytes in
predefined_metric_type = "ASGAverageNetworkIn"
target_value = 10000000  # 10MB/s

# Network bytes out
predefined_metric_type = "ASGAverageNetworkOut"
target_value = 20000000  # 20MB/s

6. Scheduled Scaling Actions

Scale based on predictable patterns:

# Scale up at 7 AM UTC
resource "aws_autoscaling_schedule" "increase_capacity_7am" {
  scheduled_action_name  = "increase-capacity-7am"
  min_size               = 2
  max_size               = 10
  desired_capacity       = 8
  start_time             = "2030-03-30T11:00:00Z"
  recurrence             = "00 09 * * *"  # Cron: 9 AM UTC daily
  autoscaling_group_name = aws_autoscaling_group.my_asg.id 
}

# Scale down at 9 PM UTC
resource "aws_autoscaling_schedule" "decrease_capacity_5pm" {
  scheduled_action_name  = "decrease-capacity-5pm"
  min_size               = 2
  max_size               = 10
  desired_capacity       = 2
  start_time             = "2030-03-30T21:00:00Z"
  recurrence             = "00 21 * * *"  # Cron: 9 PM UTC daily
  autoscaling_group_name = aws_autoscaling_group.my_asg.id
}

Cron syntax explained:

00 09 * * *
│  │  │ │ │
│  │  │ │ └─ Day of week (0-7, 0=Sunday)
│  │  │ └─── Month (1-12)
│  │  └───── Day of month (1-31)
│  └──────── Hour (0-23)
└─────────── Minute (0-59)

Examples:

00 09 * * *        # Every day at 9 AM
00 09 * * 1-5      # Weekdays at 9 AM
00 */4 * * *       # Every 4 hours
30 08 1 * *        # First day of month at 8:30 AM
00 18 * * 5        # Every Friday at 6 PM

Real-world scheduling patterns:

E-commerce site:

# Black Friday preparation
recurrence = "00 06 25 11 *"  # Nov 25 at 6 AM
desired_capacity = 20

# Post-Christmas scale down
recurrence = "00 00 27 12 *"  # Dec 27 at midnight
desired_capacity = 5

Business hours scaling:

# Monday-Friday 8 AM
recurrence = "00 08 * * 1-5"
desired_capacity = 10

# Monday-Friday 6 PM
recurrence = "00 18 * * 1-5"
desired_capacity = 2

scheduled vs target tracking:

Both can run simultaneously!

  • Scheduled: Sets capacity at specific times
  • Target tracking: Adjusts capacity based on metrics

Example scenario:

7 AM: Scheduled action sets desired=8

8 AM: Traffic spike, CPU>50%, target tracking adds 2 more → 10 instances

9 AM: Traffic normal, CPU<50%, target tracking removes 2 → 8 instances

9 PM: Scheduled action sets desired=2

7. SNS Notifications for Scaling Events

Get email alerts when ASG scales:

# Create SNS topic
resource "aws_sns_topic" "myasg_sns_topic" {
  name = "myasg-sns-topic-${random_pet.this.id}"
}

# Subscribe email
resource "aws_sns_topic_subscription" "myasg_sns_topic_subscription" {
  topic_arn = aws_sns_topic.myasg_sns_topic.arn
  protocol  = "email"
  endpoint  = "rezachegini1994@gmail.com"
}

# Configure notifications
resource "aws_autoscaling_notification" "myasg_notifications" {
  group_names = [aws_autoscaling_group.my_asg.id]
  notifications = [
    "autoscaling:EC2_INSTANCE_LAUNCH",
    "autoscaling:EC2_INSTANCE_TERMINATE",
    "autoscaling:EC2_INSTANCE_LAUNCH_ERROR",
    "autoscaling:EC2_INSTANCE_TERMINATE_ERROR",
  ]
  topic_arn = aws_sns_topic.myasg_sns_topic.arn 
}

Email notifications I receive:

Instance launch:

Event: autoscaling:EC2_INSTANCE_LAUNCH
Time: 2025-01-15 10:30:00
Group: myasg_20250115
Cause: Target tracking increased capacity
Instance: i-0abcd1234efgh5678

Instance terminate:

Event: autoscaling:EC2_INSTANCE_TERMINATE
Time: 2025-01-15 22:15:00
Group: myasg_20250115
Cause: Scheduled action decreased capacity
Instance: i-0abcd1234efgh5678

Launch error:

Event: autoscaling:EC2_INSTANCE_LAUNCH_ERROR
Time: 2025-01-15 15:45:00
Group: myasg_20250115
Cause: Insufficient capacity in AZ
StatusMessage: We currently do not have sufficient t2.micro capacity

random_pet for unique names:

resource "random_pet" "this" {
  length = 2
}
# Generates: "mighty-panda", "happy-tiger", etc.

I use this to avoid SNS topic name conflicts when deploying multiple times.

Comparing Load Balancer Types

Application Load Balancer (ALB)

  • OSI Layer: Layer 7 (Application)
  • Protocols: HTTP, HTTPS, WebSocket
  • Latency: ~10–20 ms
  • Throughput: ~100K requests/sec
  • Routing: Path, host header, query string
  • SSL Termination: Yes
  • Static IP: No (DNS-based)
  • Client IP: Lost (forwarded via headers)
  • Health Checks: HTTP-based
  • Best for: Web apps, APIs, microservices

Network Load Balancer (NLB)

  • OSI Layer: Layer 4 (Transport)
  • Protocols: TCP, UDP, TLS
  • Latency: ~100 microseconds
  • Throughput: Millions of requests/sec
  • Routing: IP + Port
  • SSL Termination: Yes (TLS listeners)
  • Static IP: Yes (per AZ)
  • Client IP: Preserved
  • Health Checks: TCP-based
  • Best for: Gaming, IoT, real-time & non-HTTP workloads

Real-World Lessons

1. HTTP Header Case Sensitivity

My mistake:

http_header_name = "Custom-Header"

Tested with:

curl -H "custom-header: app-1" https://myapps11.rezaops.com

Result: Didn’t work!

Why: HTTP headers are case-insensitive in HTTP spec, but ALB conditions are case-sensitive!

Solution:

http_header_name = "custom-header"  # Lowercase

Or configure ALB to normalize headers (not available in Terraform module).

2. Query String Encoding

Problem: URL with spaces: https://myapps11.rezaops.com?website=aws eks

Didn’t match:

query_string = {
  key = "website"
  value = "aws eks"
}

Reason: Browsers URL-encode spaces: aws%20eks

Solution:

value = "aws%20eks"  # URL-encoded

Or use wildcards:

value = "aws*"  # Matches "aws eks", "aws-eks", "aws_eks"

3. Auto Scaling Cooldown vs Warmup

I confused these two concepts:

Cooldown (old, don’t use):

  • Wait period after scaling action
  • Blocks ALL scaling during cooldown
  • Too rigid for modern applications

Warmup (modern, use this):

estimated_instance_warmup = 180
  • Excludes new instances from metrics temporarily
  • Doesn’t block other scaling actions
  • Better for dynamic workloads

Target tracking uses warmup automatically. Only set cooldown for simple scaling policies.

4. ASG Instance Refresh Failed

Error:

Instance refresh failed: MinHealthyPercentage requirement not met

What happened:

  • Tried to refresh 4 instances
  • min_healthy_percentage = 75
  • Killed 1 instance (3 left = 75%)
  • New instance took 5 minutes to be healthy
  • During that time, only 3 healthy (75%)
  • Tried to kill another → Would drop to 2 (50%)
  • Refused!

Solution:

min_healthy_percentage = 50  # More flexible

Or increase instance count before refresh:

# Scale up first
aws autoscaling set-desired-capacity --auto-scaling-group-name myasg --desired-capacity 6

# Then refresh
aws autoscaling start-instance-refresh --auto-scaling-group-name myasg

5. NLB Preserves Source IP – Security Group Issues

Problem: Private instance security group:

ingress_cidr_blocks = [module.vpc.vpc_cidr_block]  # 10.0.0.0/16

Connections from internet failed health checks!

Why: NLB preserves source IP. Client IP is public (not from VPC).

Solution for NLB:

ingress_cidr_blocks = ["0.0.0.0/0"]  # Allow from anywhere

Or use ALB which rewrites source IP to ALB’s IP (inside VPC).

6. SNS Email Subscription Requires Confirmation

What happened: Deployed infrastructure, never got emails!

Why: SNS requires email confirmation:

  1. Terraform creates subscription
  2. AWS sends confirmation email
  3. Must click “Confirm subscription”
  4. Then emails start working

Can’t automate this for security.

Check spam folder for confirmation email!

7. Launch Template Version Management

Problem: Updated launch template, but ASG used old version!

Cause:

version = "$Latest"  # Wrong!

This doesn’t auto-update. Terraform doesn’t know to trigger refresh.

Solution:

version = aws_launch_template.my_launch_template.latest_version

Plus:

update_default_version = true  # In launch template

Now when template changes, ASG knows to refresh.

Cost Comparison

Advanced ALB Routing:

Same as basic ALB:

  • ALB: $22.50/month
  • LCU: ~$5-10/month
  • ACM: Free
  • Route53: $0.50/month
  • EC2 (4 instances): $28/month
  • Total: ~$56-61/month

NLB with Auto Scaling:

  • NLB: $22.50/month
  • NLCU: ~$5-15/month (depends on traffic)
  • EC2 (2-5 instances): $14-35/month (dynamic)
  • CloudWatch: $2/month (detailed monitoring)
  • SNS: $0.50/month
  • Total: ~$44-75/month (varies with load)

Cost optimization for ASG: Set aggressive scale-down:

min_size = 1  # Scale to 1 at night
recurrence = "00 22 * * *"  # 10 PM scale down
desired_capacity = 1

Deployment Commands

Advanced ALB:

cd ALB-HTTPHeader-QueryString-Redirects/terraform-manifests
terraform init
terraform apply

# Test HTTP header routing
curl -H "custom-header: app-1" https://myapps11.rezaops.com

# Test query string redirect
curl -I "https://myapps11.rezaops.com?website=aws-eks"

# Test host header redirect
curl -I "https://azure-aks11.rezaops.com"

NLB with Auto Scaling:

cd AWS-NLB-Network-Load-Balancer/terraform-manifests
terraform init
terraform apply

# Confirm SNS email subscription
# Check email and click confirmation link

# Test NLB
curl http://nlb.rezaops.com
curl https://nlb.rezaops.com

# Watch auto scaling
aws autoscaling describe-auto-scaling-groups \
  --auto-scaling-group-names myasg_xxxxx

# Trigger scaling by generating load
ab -n 100000 -c 100 http://nlb.rezaops.com/

What I Learned

  1. ALB routing is incredibly flexible – Can route on almost any HTTP attribute
  2. NLB is for performance – When latency matters more than features
  3. Auto Scaling is not magic – Requires careful tuning of thresholds and warmup times
  4. Scheduled scaling saves money – Scale down during off-hours
  5. Target tracking is powerful – Set target CPU and forget about it
  6. Instance refresh is crucial – For zero-downtime deployments
  7. Monitoring is essential – SNS alerts help catch issues early

$ ls -t root-access/ | head -3

Hello world! uncategorized 1 min Building a Job Board API, Part 1: The Foundation job-board-api · 1/1 16 min Part 13 – Kubernetes Autoscaling in Action: A FastAPI Microservice That Scales Itself home-lab · 13/13 6 min

Leave a comment

Your email address will not be published. Required fields are marked *