AWS EC2: Complete Compute Guide

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)
Security: Always Use IMDSv2
IMDSv2 requires a session token, protecting against SSRF attacks. Configure instances to require IMDSv2:
# 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.