Post

AWS Solutions Architect Associate: Domain 4 - Design Cost-Optimized Architectures

Complete guide to Domain 4: designing cost-optimized AWS architectures using pricing models, resource optimization, and cost management strategies.

Introduction

Domain 4 focuses on designing architectures that are cost-effective while maintaining performance and reliability. This domain represents approximately 16% of the exam and requires understanding AWS pricing models, resource optimization, and cost management tools.

AWS Pricing Models

On-Demand Instances

Pay for compute capacity by the second with no long-term commitments.

Use Cases: - Development and testing environments - Unpredictable workloads - Short-term projects - Spiky traffic patterns

Pricing Example (t3.medium in us-east-1):

On-Demand: $0.0416 per hour Monthly (730 hours): ~$30.37

Reserved Instances (RIs)

Commit to 1 or 3 years for significant discounts.

ReservedInstance: Term: 1-year or 3-year PaymentOption: - AllUpfront: Largest discount (30-40%) - PartialUpfront: Medium discount (20-30%) - NoUpfront: Smallest discount (10-20%) Example_3Year_AllUpfront: t3.medium: $0.0175 per hour (58% savings) Monthly: ~$12.78 Annual: ~$153 3-Year_Cost: ~$459

Breaking Even Calculation:

RI Cost: $459 for 3 years On-Demand: $30.37/month × 36 months = $1,093 Savings: $1,093 - $459 = $634 (58% reduction) Break-even: ~3 months

Savings Plans

Flexible pricing providing up to 72% discount.

SavingsPlan: Compute_Savings_Plan: - Flexibility: Any instance family, region, OS - Discount: Up to 66% vs On-Demand - Best_For: Stable compute needs across multiple instance types EC2_Instance_Savings_Plan: - Flexibility: Same family in same region - Discount: Up to 72% vs On-Demand - Best_For: Predictable workloads in specific region

Spot Instances

Up to 90% discount for flexible workloads.

import boto3 ec2 = boto3.client('ec2') # Request spot instances response = ec2.request_spot_instances( SpotPrice='0.05', # Max price willing to pay InstanceCount=5, Type='one-time', # or 'persistent' LaunchSpecification={ 'ImageId': 'ami-12345678', 'InstanceType': 't3.medium', 'KeyName': 'my-key', 'SecurityGroups': ['web-sg'] } ) # Monitor spot instance requests requests = ec2.describe_spot_instance_requests( SpotInstanceRequestIds=[r['SpotInstanceRequestId'] for r in response['SpotInstanceRequests']] )

Spot vs On-Demand Pricing:

On-Demand t3.medium: $0.0416/hour Spot t3.medium: ~$0.004-0.008/hour (80-90% discount) Monthly: - On-Demand: $30.37 - Spot: $3-5.84 - Savings: $24.53-27.37 per instance!

Right-Sizing Strategies

Analyzing Current Resource Utilization

import boto3 from datetime import datetime, timedelta cloudwatch = boto3.client('cloudwatch') # Get CPU utilization metrics response = cloudwatch.get_metric_statistics( Namespace='AWS/EC2', MetricName='CPUUtilization', Dimensions=[ { 'Name': 'InstanceId', 'Value': 'i-1234567890abcdef0' } ], StartTime=datetime.utcnow() - timedelta(days=30), EndTime=datetime.utcnow(), Period=3600, # 1 hour Statistics=['Average', 'Maximum'] ) # Analyze metrics metrics = response['Datapoints'] avg_cpu = sum(m['Average'] for m in metrics) / len(metrics) max_cpu = max(m['Maximum'] for m in metrics) print(f"Average CPU: {avg_cpu:.2f}%") print(f"Peak CPU: {max_cpu:.2f}%") # Recommendations if avg_cpu < 10: print("Consider downsizing to t3.small") elif avg_cpu < 20: print("Current size appropriate or downsize to t3.medium")

Compute Optimizer Recommendations

# Enable Compute Optimizer aws compute-optimizer put-recommendation-preferences \ --resource-type Ec2Instance \ --enhanced-infrastructure-metrics Active # Get recommendations aws compute-optimizer get-ec2-instance-recommendations \ --query 'instanceRecommendations[*].[instanceArn,currentInstanceType,recommendationOptions[0].instanceType,recommendationOptions[0].savingsOpportunity.savingsOpportunityPercentage]' \ --output table

Storage Optimization

S3 Storage Classes

StorageClasses: S3_Standard: Cost: $0.023 per GB Retrieval: Immediate UseCase: Frequently accessed data S3_Standard_IA: Cost: $0.0125 per GB Retrieval: Minutes MinimumBillableSize: 128KB UseCase: Infrequent access S3_Glacier_Instant: Cost: $0.004 per GB Retrieval: Minutes UseCase: Archive with quick access S3_Glacier_Deep_Archive: Cost: $0.00099 per GB Retrieval: Hours UseCase: Long-term archival

Lifecycle Policies

{ "Rules": [ { "Id": "Archive old data", "Status": "Enabled", "Prefix": "logs/", "Transitions": [ { "Days": 30, "StorageClass": "STANDARD_IA" }, { "Days": 90, "StorageClass": "GLACIER" }, { "Days": 365, "StorageClass": "DEEP_ARCHIVE" } ], "Expiration": { "Days": 2555 } } ] }

EBS Volume Optimization

# Analyze EBS volume performance aws cloudwatch get-metric-statistics \ --namespace AWS/EBS \ --metric-name VolumeReadBytes \ --dimensions Name=VolumeId,Value=vol-12345 \ --start-time 2025-11-01T00:00:00Z \ --end-time 2025-11-05T00:00:00Z \ --period 3600 \ --statistics Sum # Convert gp2 to gp3 for cost savings # gp3: $0.10 per GB per month # gp2: $0.10 per GB per month # But gp3 offers better performance at same cost

Database Cost Optimization

RDS Pricing Strategy

RDS_Optimization: Use_Aurora_MySQL: CostSavings: "Up to 90% vs MySQL with same features" Features: - Automatic failover - Automatic backups - Read replicas included - Pay per second Use_DynamoDB_for_Scale: Benefit: "No management, automatic scaling" Mode: - OnDemand: Pay per request - Provisioned: Pay for capacity Reserved_Capacity: Discount: "30-50% vs on-demand" Commitment: "1 or 3 years"

Multi-AZ Cost Trade-offs

Single AZ RDS MySQL (db.t3.medium): - Cost: $0.175/hour = $128/month - Data: No redundancy Multi-AZ RDS MySQL (db.t3.medium): - Cost: $0.35/hour = $256/month - Benefit: Automatic failover, high availability - Trade-off: 2x cost for reliability

Lambda Cost Optimization

Memory vs Duration Trade-off

import boto3 # Cost calculation helper def calculate_lambda_cost(memory_mb, duration_ms, invocations): # AWS pricing: $0.0000002 per GB-second gb_seconds = (memory_mb / 1024) * (duration_ms / 1000) * invocations cost = gb_seconds * 0.0000002 # Free tier: 1M invocations + 400,000 GB-seconds per month free_gb_seconds = 400000 if gb_seconds > free_gb_seconds: cost = (gb_seconds - free_gb_seconds) * 0.0000002 return cost # Compare scenarios scenarios = [ {"name": "128MB", "memory": 128, "duration": 3000, "invocations": 1000000}, {"name": "512MB", "memory": 512, "duration": 1000, "invocations": 1000000}, {"name": "1024MB", "memory": 1024, "duration": 500, "invocations": 1000000}, ] for scenario in scenarios: cost = calculate_lambda_cost( scenario["memory"], scenario["duration"], scenario["invocations"] ) print(f"{scenario['name']}: ${cost:.2f}")

Cost Monitoring and Analysis

AWS Cost Explorer

# Get daily cost and usage aws ce get-cost-and-usage \ --time-period Start=2025-10-01,End=2025-11-01 \ --granularity DAILY \ --metrics "BlendedCost" \ --group-by Type=DIMENSION,Key=SERVICE

Setting up Budget Alerts

import boto3 budgets = boto3.client('budgets') # Create monthly budget budgets.create_budget( AccountId='123456789', Budget={ 'BudgetName': 'Monthly-Spend-Limit', 'BudgetLimit': { 'Amount': '5000', 'Unit': 'USD' }, 'TimeUnit': 'MONTHLY', 'BudgetType': 'COST', 'CostFilters': { 'Service': ['Amazon Elastic Compute Cloud'] } }, NotificationsWithSubscribers=[ { 'Notification': { 'NotificationType': 'FORECASTED', 'ComparisonOperator': 'GREATER_THAN', 'Threshold': 80, 'ThresholdType': 'PERCENTAGE' }, 'Subscribers': [ { 'SubscriptionType': 'EMAIL', 'Address': 'billing@example.com' } ] } ] )

Cost Optimization Patterns

Architecture Comparison

Scenario: Web application serving 100M requests/month Option 1: On-Demand EC2 (t3.large) - Cost: $0.0832/hour × 730 hours = $60.74/month per instance - Servers needed: 2 (HA) = $121.48/month - Plus: RDS Multi-AZ = $256/month - Total: ~$377/month Option 2: Reserved Instances (1-year) - Cost: $0.035/hour × 730 hours = $25.55/month per instance - Servers needed: 2 = $51.10/month (60% savings!) - Plus: RDS RI = $128/month (50% savings!) - Total: ~$179/month - Annual Savings: ~$2,376

Removing Unused Resources

# Find unattached EBS volumes aws ec2 describe-volumes \ --query 'Volumes[?State==`available`].[VolumeId,Size,CreateTime]' \ --output table # Delete unused security groups aws ec2 describe-security-groups \ --query 'SecurityGroups[?length(IpPermissions)==`0` && length(IpPermissionsEgress)==`1`]' # Find unused Elastic IPs aws ec2 describe-addresses \ --query 'Addresses[?AssociationId==null].[PublicIp,AllocationId]' \ --output table

Common Exam Questions

Q: You have a predictable database workload. What offers the best cost savings? A: 3-year Reserved Instance with all-upfront payment (up to 50-60% savings)

Q: Which service reduces costs for temporary workloads? A: Spot instances (up to 90% discount) or Lambda (pay per invocation)

Q: How do you optimize storage costs for compliance archives? A: Use S3 Lifecycle policies to transition to Glacier/Deep Archive

Key Takeaways

  1. Match pricing model to workload characteristics
  2. Right-size resources based on actual utilization
  3. Use lifecycle policies for data storage
  4. Reserve capacity for predictable workloads
  5. Use spot instances for fault-tolerant applications
  6. Monitor and optimize continuously
  7. Remove unused resources regularly

Resources

This post is licensed under CC BY 4.0 by the author.