AWS EC2: Complete Compute and Instance Guide

AWS EC2: Complete Compute Guide

Software developer at work
Software developer at work

Amazon EC2 (Elastic Compute Cloud) is the backbone of AWS compute. From web servers to machine learning training, EC2 provides resizable compute capacity in the cloud. Understanding instance types, pricing, and optimization is key to running cost-effective workloads.

EC2 Instance Types

Family Optimized For Use Cases
T3/T3a Burstable performance Dev/test, small web apps
M6i/M7i General purpose Web servers, app servers, databases
C6i/C7i Compute intensive Batch processing, gaming, HPC
R6i/R7i Memory intensive In-memory databases, caching
P4/P5 GPU compute ML training, graphics rendering
I4i Storage intensive Data warehouses, distributed filesystems
Graviton (g suffix) ARM-based, cost-effective Up to 40% better price-performance

EC2 Pricing Options

Pricing Model Discount Commitment Best For
On-Demand 0% None Short-term, unpredictable
Savings Plans Up to 72% 1 or 3 years Flexible, steady-state
Reserved Instances Up to 75% 1 or 3 years Specific instance type
Spot Instances Up to 90% None (can be interrupted) Fault-tolerant, batch jobs

Launching EC2 with Terraform

# Latest Amazon Linux 2023 AMI
data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["al2023-ami-*-x86_64"]
  }
}

# EC2 Instance
resource "aws_instance" "web" {
  ami                    = data.aws_ami.amazon_linux.id
  instance_type          = "t3.medium"
  subnet_id              = aws_subnet.public.id
  vpc_security_group_ids = [aws_security_group.web.id]
  iam_instance_profile   = aws_iam_instance_profile.web.name

  # EBS root volume
  root_block_device {
    volume_type           = "gp3"
    volume_size           = 50
    iops                  = 3000
    throughput            = 125
    encrypted             = true
    delete_on_termination = true
  }

  # User data for bootstrapping
  user_data = <<-EOF
    #!/bin/bash
    dnf update -y
    dnf install -y httpd
    systemctl start httpd
    systemctl enable httpd
  EOF

  tags = {
    Name        = "web-server"
    Environment = "production"
  }
}

# Elastic IP for static public IP
resource "aws_eip" "web" {
  instance = aws_instance.web.id
  domain   = "vpc"
}

EC2 with Python Boto3

import boto3

ec2 = boto3.client('ec2')

# Launch instance
def launch_instance(ami_id, instance_type, subnet_id, sg_id):
    response = ec2.run_instances(
        ImageId=ami_id,
        InstanceType=instance_type,
        MinCount=1,
        MaxCount=1,
        SubnetId=subnet_id,
        SecurityGroupIds=[sg_id],
        TagSpecifications=[{
            'ResourceType': 'instance',
            'Tags': [{'Key': 'Name', 'Value': 'web-server'}]
        }]
    )
    return response['Instances'][0]['InstanceId']

# List running instances
def list_instances():
    response = ec2.describe_instances(
        Filters=[{'Name': 'instance-state-name', 'Values': ['running']}]
    )

    for reservation in response['Reservations']:
        for instance in reservation['Instances']:
            name = next(
                (t['Value'] for t in instance.get('Tags', []) if t['Key'] == 'Name'),
                'Unnamed'
            )
            print(f"{name}: {instance['InstanceId']} - {instance['InstanceType']}")

# Stop/start instances
def stop_instance(instance_id):
    ec2.stop_instances(InstanceIds=[instance_id])
    print(f"Stopping {instance_id}")

def start_instance(instance_id):
    ec2.start_instances(InstanceIds=[instance_id])
    print(f"Starting {instance_id}")

Auto Scaling Groups

# Launch Template
resource "aws_launch_template" "web" {
  name_prefix   = "web-"
  image_id      = data.aws_ami.amazon_linux.id
  instance_type = "t3.medium"

  network_interfaces {
    security_groups = [aws_security_group.web.id]
  }

  iam_instance_profile {
    name = aws_iam_instance_profile.web.name
  }

  user_data = base64encode(<<-EOF
    #!/bin/bash
    dnf update -y
    dnf install -y httpd
    systemctl start httpd
  EOF)
}

# Auto Scaling Group
resource "aws_autoscaling_group" "web" {
  name                = "web-asg"
  min_size            = 2
  max_size            = 10
  desired_capacity    = 2
  vpc_zone_identifier = aws_subnet.private[*].id
  target_group_arns   = [aws_lb_target_group.web.arn]

  launch_template {
    id      = aws_launch_template.web.id
    version = "$Latest"
  }

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

# Target Tracking Scaling Policy
resource "aws_autoscaling_policy" "cpu" {
  name                   = "cpu-target-tracking"
  autoscaling_group_name = aws_autoscaling_group.web.name
  policy_type            = "TargetTrackingScaling"

  target_tracking_configuration {
    predefined_metric_specification {
      predefined_metric_type = "ASGAverageCPUUtilization"
    }
    target_value = 70.0
  }
}

Spot Instances for Cost Savings

# Spot Fleet for batch processing
resource "aws_spot_fleet_request" "batch" {
  iam_fleet_role                      = aws_iam_role.spot_fleet.arn
  target_capacity                     = 10
  allocation_strategy                 = "capacityOptimized"
  terminate_instances_with_expiration = true

  launch_specification {
    instance_type = "c5.xlarge"
    ami           = data.aws_ami.amazon_linux.id
    subnet_id     = aws_subnet.private[0].id
  }

  launch_specification {
    instance_type = "c5a.xlarge"
    ami           = data.aws_ami.amazon_linux.id
    subnet_id     = aws_subnet.private[0].id
  }

  launch_specification {
    instance_type = "c6i.xlarge"
    ami           = data.aws_ami.amazon_linux.id
    subnet_id     = aws_subnet.private[0].id
  }
}

Instance Metadata Service (IMDS)

# Terraform: Require IMDSv2
resource "aws_instance" "secure" {
  # ... other config

  metadata_options {
    http_endpoint               = "enabled"
    http_tokens                 = "required"  # Force IMDSv2
    http_put_response_hop_limit = 1
  }
}

# Access IMDSv2 from instance
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" \
  -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")

curl -H "X-aws-ec2-metadata-token: $TOKEN" \
  http://169.254.169.254/latest/meta-data/instance-id

Best Practices

  • Right-size instances: Use AWS Compute Optimizer recommendations
  • Use Graviton: ARM-based instances offer 40% better price-performance
  • Savings Plans: Commit to steady-state usage for up to 72% savings
  • Spot for fault-tolerant: Use Spot Instances for batch, CI/CD, dev/test
  • IMDSv2: Always require IMDSv2 for security
  • Use SSM Session Manager: Replace SSH with Session Manager for access
  • EBS gp3: Use gp3 volumes for better cost and performance than gp2

Further Reading

Pro Tip: Use AWS Compute Optimizer to analyze your EC2 usage and get right-sizing recommendations. It can identify over-provisioned instances and suggest cost-effective alternatives.

Jennifer Walsh

Jennifer Walsh

Author & Expert

Senior Cloud Solutions Architect with 12 years of experience in AWS, Azure, and GCP. Jennifer has led enterprise migrations for Fortune 500 companies and holds AWS Solutions Architect Professional and DevOps Engineer certifications. She specializes in serverless architectures, container orchestration, and cloud cost optimization. Previously a senior engineer at AWS Professional Services.

156 Articles
View All Posts