Scaling Applications Automatically with AWS Auto Scaling and Terraform
Learn how to implement auto scaling for your applications using AWS Auto Scaling Groups, Launch Templates, and Terraform
Scaling Applications Automatically with AWS Auto Scaling and Terraform
Auto Scaling helps ensure your application can handle varying loads efficiently while optimizing costs. This guide demonstrates how to implement auto scaling using AWS Auto Scaling Groups and Launch Templates with Terraform.
Video Tutorial
Learn more about implementing Auto Scaling with Terraform in AWS in this comprehensive video tutorial:
Prerequisites
- AWS CLI configured with appropriate permissions
- Terraform installed (version 1.0.0 or later)
- Basic understanding of EC2 and networking concepts
- Existing VPC with public and private subnets
Project Structure
terraform-autoscaling/
├── main.tf
├── variables.tf
├── outputs.tf
├── modules/
│ └── autoscaling/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
└── user-data/
└── init.sh
Launch Template Configuration
Create modules/autoscaling/main.tf:
# Launch Template
resource "aws_launch_template" "main" {
name_prefix = "${var.project_name}-template"
image_id = var.ami_id
instance_type = var.instance_type
network_interfaces {
associate_public_ip_address = false
security_groups = [aws_security_group.instance.id]
}
iam_instance_profile {
name = aws_iam_instance_profile.instance.name
}
user_data = base64encode(templatefile("${path.module}/../../user-data/init.sh", {
environment = var.environment
region = data.aws_region.current.name
}))
block_device_mappings {
device_name = "/dev/xvda"
ebs {
volume_size = 30
volume_type = "gp3"
delete_on_termination = true
encrypted = true
}
}
monitoring {
enabled = true
}
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
}
tag_specifications {
resource_type = "instance"
tags = merge(
var.tags,
{
Name = "${var.project_name}-instance"
}
)
}
lifecycle {
create_before_destroy = true
}
}
# Auto Scaling Group
resource "aws_autoscaling_group" "main" {
name = "${var.project_name}-asg"
desired_capacity = var.desired_capacity
max_size = var.max_size
min_size = var.min_size
target_group_arns = [aws_lb_target_group.main.arn]
vpc_zone_identifier = var.private_subnet_ids
health_check_type = "ELB"
health_check_grace_period = 300
launch_template {
id = aws_launch_template.main.id
version = "$Latest"
}
instance_refresh {
strategy = "Rolling"
preferences {
min_healthy_percentage = 50
}
}
dynamic "tag" {
for_each = merge(
var.tags,
{
Name = "${var.project_name}-instance"
}
)
content {
key = tag.key
value = tag.value
propagate_at_launch = true
}
}
lifecycle {
create_before_destroy = true
ignore_changes = [desired_capacity]
}
}
# Target Tracking Scaling Policy (CPU)
resource "aws_autoscaling_policy" "cpu" {
name = "${var.project_name}-cpu-policy"
autoscaling_group_name = aws_autoscaling_group.main.name
policy_type = "TargetTrackingScaling"
target_tracking_configuration {
predefined_metric_specification {
predefined_metric_type = "ASGAverageCPUUtilization"
}
target_value = var.cpu_target
}
}
# Target Tracking Scaling Policy (Request Count Per Target)
resource "aws_autoscaling_policy" "request" {
name = "${var.project_name}-request-policy"
autoscaling_group_name = aws_autoscaling_group.main.name
policy_type = "TargetTrackingScaling"
target_tracking_configuration {
predefined_metric_specification {
predefined_metric_type = "ALBRequestCountPerTarget"
resource_label = "${aws_lb.main.arn_suffix}/${aws_lb_target_group.main.arn_suffix}"
}
target_value = var.request_target
}
}
# Step Scaling Policy (Memory)
resource "aws_autoscaling_policy" "memory" {
name = "${var.project_name}-memory-policy"
autoscaling_group_name = aws_autoscaling_group.main.name
policy_type = "StepScaling"
adjustment_type = "ChangeInCapacity"
metric_aggregation_type = "Average"
step_adjustment {
scaling_adjustment = 1
metric_interval_lower_bound = 10
}
step_adjustment {
scaling_adjustment = 2
metric_interval_lower_bound = 20
}
}
# Application Load Balancer
resource "aws_lb" "main" {
name = "${var.project_name}-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.alb.id]
subnets = var.public_subnet_ids
enable_deletion_protection = true
access_logs {
bucket = aws_s3_bucket.logs.id
prefix = "alb-logs"
enabled = true
}
tags = merge(
var.tags,
{
Name = "${var.project_name}-alb"
}
)
}
# Target Group
resource "aws_lb_target_group" "main" {
name = "${var.project_name}-tg"
port = 80
protocol = "HTTP"
vpc_id = var.vpc_id
health_check {
enabled = true
healthy_threshold = 3
interval = 30
matcher = "200"
path = "/health"
port = "traffic-port"
protocol = "HTTP"
timeout = 5
unhealthy_threshold = 2
}
tags = merge(
var.tags,
{
Name = "${var.project_name}-tg"
}
)
}
# Scheduled Actions
resource "aws_autoscaling_schedule" "scale_up" {
scheduled_action_name = "${var.project_name}-scale-up"
min_size = var.peak_min_size
max_size = var.peak_max_size
desired_capacity = var.peak_desired_capacity
recurrence = "0 8 * * MON-FRI"
autoscaling_group_name = aws_autoscaling_group.main.name
}
resource "aws_autoscaling_schedule" "scale_down" {
scheduled_action_name = "${var.project_name}-scale-down"
min_size = var.min_size
max_size = var.max_size
desired_capacity = var.desired_capacity
recurrence = "0 18 * * MON-FRI"
autoscaling_group_name = aws_autoscaling_group.main.name
}
User Data Script
Create user-data/init.sh:
#!/bin/bash
set -e
# Install CloudWatch agent
yum install -y amazon-cloudwatch-agent
# Configure CloudWatch agent
cat > /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json <<EOF
{
"agent": {
"metrics_collection_interval": 60,
"run_as_user": "root"
},
"metrics": {
"metrics_collected": {
"mem": {
"measurement": [
"mem_used_percent"
]
}
}
}
}
EOF
# Start CloudWatch agent
systemctl enable amazon-cloudwatch-agent
systemctl start amazon-cloudwatch-agent
# Install application dependencies
yum update -y
yum install -y nginx
# Configure application
cat > /etc/nginx/conf.d/default.conf <<EOF
server {
listen 80;
server_name _;
location /health {
access_log off;
return 200 'healthy\n';
}
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host \$host;
proxy_cache_bypass \$http_upgrade;
}
}
EOF
# Start application
systemctl enable nginx
systemctl start nginx
Variables Configuration
Create variables.tf:
variable "project_name" {
description = "Name of the project"
type = string
}
variable "environment" {
description = "Environment name"
type = string
}
variable "vpc_id" {
description = "VPC ID"
type = string
}
variable "private_subnet_ids" {
description = "List of private subnet IDs"
type = list(string)
}
variable "public_subnet_ids" {
description = "List of public subnet IDs"
type = list(string)
}
variable "ami_id" {
description = "AMI ID for instances"
type = string
}
variable "instance_type" {
description = "Instance type"
type = string
default = "t3.micro"
}
variable "min_size" {
description = "Minimum size of ASG"
type = number
default = 1
}
variable "max_size" {
description = "Maximum size of ASG"
type = number
default = 5
}
variable "desired_capacity" {
description = "Desired capacity of ASG"
type = number
default = 2
}
variable "cpu_target" {
description = "Target CPU utilization"
type = number
default = 70
}
variable "request_target" {
description = "Target request count per instance"
type = number
default = 1000
}
variable "peak_min_size" {
description = "Minimum size during peak hours"
type = number
default = 2
}
variable "peak_max_size" {
description = "Maximum size during peak hours"
type = number
default = 10
}
variable "peak_desired_capacity" {
description = "Desired capacity during peak hours"
type = number
default = 4
}
Auto Scaling Strategies
-
Target Tracking Scaling
- CPU utilization
- Request count per target
- Custom metrics
-
Step Scaling
- Memory utilization
- Network throughput
- Custom alarms
-
Scheduled Scaling
- Business hours
- Peak periods
- Maintenance windows
Best Practices
-
Instance Configuration
- Use Launch Templates
- Enable detailed monitoring
- Implement proper tagging
- Configure instance refresh
-
Health Checks
- Use ELB health checks
- Configure grace periods
- Implement custom health checks
- Monitor instance health
-
Scaling Policies
- Set appropriate thresholds
- Use multiple metrics
- Implement cooldown periods
- Configure step adjustments
-
Cost Optimization
- Use appropriate instance types
- Configure scaling thresholds
Conclusion
You’ve learned how to implement Auto Scaling using Terraform in AWS. This setup provides:
- Automatic scaling based on demand
- Cost optimization
- High availability
- Performance optimization
Remember to:
- Monitor scaling activities
- Optimize scaling policies
- Test scaling scenarios
- Review cost implications