AW
Compute
Aws core v1.0.0
AWS Compute Services
Overview
This skill covers AWS compute services including EC2, Lambda, ECS, and Fargate. Understanding when to use each compute option is critical for building scalable, cost-effective applications on AWS.
Key Concepts
Compute Decision Tree
┌─────────────────────────────────────────────────────────────┐
│ AWS Compute Decision Tree │
├─────────────────────────────────────────────────────────────┤
│ │
│ What's your workload? │
│ │ │
│ ├── Short-lived, event-driven? │
│ │ └── Lambda (serverless) │
│ │ │
│ ├── Long-running containers? │
│ │ ├── Want to manage servers? │
│ │ │ └── ECS on EC2 │
│ │ └── Serverless containers? │
│ │ └── ECS on Fargate │
│ │ │
│ ├── Kubernetes required? │
│ │ └── EKS (Fargate or EC2) │
│ │ │
│ └── Full server control needed? │
│ └── EC2 │
│ │
│ Cost vs Control Spectrum: │
│ │
│ Lambda ◄──── Fargate ◄──── ECS/EC2 ◄──── EC2 │
│ (Least (Medium) (More) (Full │
│ Control) Control) │
│ │
└─────────────────────────────────────────────────────────────┘
Lambda Limits
| Resource | Default Limit |
|---|---|
| Memory | 128 MB - 10 GB |
| Timeout | 15 minutes max |
| Package Size | 50 MB (zipped), 250 MB (unzipped) |
| Concurrent Executions | 1000 per region |
| Payload Size | 6 MB sync, 256 KB async |
Best Practices
1. Right-Size Compute Resources
Match instance types to workload requirements.
2. Use Spot Instances for Fault-Tolerant Workloads
Save up to 90% on EC2 costs for interruptible workloads.
3. Enable Auto Scaling
Scale based on demand to optimize cost and performance.
4. Use Graviton Processors
ARM-based processors offer better price/performance.
5. Implement Proper Health Checks
Ensure load balancers can detect unhealthy instances.
Code Examples
Example 1: Lambda Function with CDK
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as logs from 'aws-cdk-lib/aws-logs';
import * as iam from 'aws-cdk-lib/aws-iam';
export class OrderServiceStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Lambda function with best practices
const orderFunction = new lambda.Function(this, 'OrderFunction', {
runtime: lambda.Runtime.NODEJS_20_X,
architecture: lambda.Architecture.ARM_64, // Graviton - better price/performance
handler: 'index.handler',
code: lambda.Code.fromAsset('lambda/order'),
// Memory/timeout configuration
memorySize: 1024,
timeout: cdk.Duration.seconds(30),
// Environment
environment: {
TABLE_NAME: orderTable.tableName,
LOG_LEVEL: 'INFO',
POWERTOOLS_SERVICE_NAME: 'order-service',
},
// Observability
tracing: lambda.Tracing.ACTIVE,
logRetention: logs.RetentionDays.ONE_WEEK,
// Performance
reservedConcurrentExecutions: 100, // Limit concurrency
// Insights
insightsVersion: lambda.LambdaInsightsVersion.VERSION_1_0_135_0,
});
// Grant permissions
orderTable.grantReadWriteData(orderFunction);
// API Gateway integration
const api = new apigateway.RestApi(this, 'OrderApi', {
restApiName: 'Order Service',
deployOptions: {
stageName: 'prod',
tracingEnabled: true,
loggingLevel: apigateway.MethodLoggingLevel.INFO,
metricsEnabled: true,
},
});
const ordersResource = api.root.addResource('orders');
ordersResource.addMethod('POST',
new apigateway.LambdaIntegration(orderFunction, {
proxy: true,
allowTestInvoke: false,
})
);
}
}
Example 2: ECS Fargate Service
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';
export class OrderServiceEcsStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// VPC
const vpc = new ec2.Vpc(this, 'OrderVpc', {
maxAzs: 3,
natGateways: 1,
});
// ECS Cluster
const cluster = new ecs.Cluster(this, 'OrderCluster', {
vpc,
containerInsights: true,
enableFargateCapacityProviders: true,
});
// Task Definition
const taskDefinition = new ecs.FargateTaskDefinition(this, 'OrderTaskDef', {
memoryLimitMiB: 512,
cpu: 256,
runtimePlatform: {
operatingSystemFamily: ecs.OperatingSystemFamily.LINUX,
cpuArchitecture: ecs.CpuArchitecture.ARM64, // Graviton
},
});
// Container
const container = taskDefinition.addContainer('OrderContainer', {
image: ecs.ContainerImage.fromEcrRepository(orderRepo, 'latest'),
logging: ecs.LogDrivers.awsLogs({
streamPrefix: 'order-service',
logRetention: logs.RetentionDays.ONE_WEEK,
}),
environment: {
SPRING_PROFILES_ACTIVE: 'prod',
},
healthCheck: {
command: ['CMD-SHELL', 'curl -f http://localhost:8080/actuator/health || exit 1'],
interval: cdk.Duration.seconds(30),
timeout: cdk.Duration.seconds(5),
retries: 3,
},
});
container.addPortMappings({
containerPort: 8080,
protocol: ecs.Protocol.TCP,
});
// Service with Auto Scaling
const service = new ecs.FargateService(this, 'OrderService', {
cluster,
taskDefinition,
desiredCount: 2,
minHealthyPercent: 100,
maxHealthyPercent: 200,
// Capacity providers for cost optimization
capacityProviderStrategies: [
{
capacityProvider: 'FARGATE_SPOT',
weight: 2,
base: 0,
},
{
capacityProvider: 'FARGATE',
weight: 1,
base: 1, // Always have 1 on-demand
},
],
// Circuit breaker
circuitBreaker: {
rollback: true,
},
});
// Auto Scaling
const scaling = service.autoScaleTaskCount({
minCapacity: 2,
maxCapacity: 10,
});
scaling.scaleOnCpuUtilization('CpuScaling', {
targetUtilizationPercent: 70,
scaleInCooldown: cdk.Duration.seconds(60),
scaleOutCooldown: cdk.Duration.seconds(60),
});
scaling.scaleOnRequestCount('RequestScaling', {
requestsPerTarget: 1000,
targetGroup: targetGroup,
});
// Load Balancer
const alb = new elbv2.ApplicationLoadBalancer(this, 'OrderALB', {
vpc,
internetFacing: true,
});
const listener = alb.addListener('HttpListener', {
port: 443,
certificates: [certificate],
});
const targetGroup = listener.addTargets('OrderTargets', {
port: 8080,
targets: [service],
healthCheck: {
path: '/actuator/health',
interval: cdk.Duration.seconds(30),
healthyThresholdCount: 2,
unhealthyThresholdCount: 3,
},
deregistrationDelay: cdk.Duration.seconds(30),
});
}
}
Example 3: EC2 Auto Scaling Group
import * as autoscaling from 'aws-cdk-lib/aws-autoscaling';
export class OrderServiceEc2Stack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Launch Template
const launchTemplate = new ec2.LaunchTemplate(this, 'OrderLaunchTemplate', {
instanceType: ec2.InstanceType.of(
ec2.InstanceClass.T4G,
ec2.InstanceSize.MEDIUM
),
machineImage: ec2.MachineImage.latestAmazonLinux2023({
cpuType: ec2.AmazonLinuxCpuType.ARM_64,
}),
securityGroup: appSecurityGroup,
role: instanceRole,
userData: ec2.UserData.custom(`#!/bin/bash
yum update -y
yum install -y docker
systemctl start docker
systemctl enable docker
# Pull and run application
aws ecr get-login-password --region ${region} | docker login --username AWS --password-stdin ${accountId}.dkr.ecr.${region}.amazonaws.com
docker pull ${ecrRepo}:latest
docker run -d -p 8080:8080 ${ecrRepo}:latest
`),
blockDevices: [
{
deviceName: '/dev/xvda',
volume: ec2.BlockDeviceVolume.ebs(20, {
encrypted: true,
volumeType: ec2.EbsDeviceVolumeType.GP3,
}),
},
],
});
// Auto Scaling Group with Mixed Instances
const asg = new autoscaling.AutoScalingGroup(this, 'OrderASG', {
vpc,
minCapacity: 2,
maxCapacity: 10,
desiredCapacity: 2,
mixedInstancesPolicy: {
instancesDistribution: {
onDemandBaseCapacity: 1,
onDemandPercentageAboveBaseCapacity: 25,
spotAllocationStrategy: autoscaling.SpotAllocationStrategy.CAPACITY_OPTIMIZED,
},
launchTemplate: launchTemplate,
launchTemplateOverrides: [
{ instanceType: ec2.InstanceType.of(ec2.InstanceClass.T4G, ec2.InstanceSize.MEDIUM) },
{ instanceType: ec2.InstanceType.of(ec2.InstanceClass.T4G, ec2.InstanceSize.LARGE) },
{ instanceType: ec2.InstanceType.of(ec2.InstanceClass.M6G, ec2.InstanceSize.MEDIUM) },
],
},
healthCheck: autoscaling.HealthCheck.elb({
grace: cdk.Duration.minutes(5),
}),
updatePolicy: autoscaling.UpdatePolicy.rollingUpdate({
maxBatchSize: 1,
minInstancesInService: 1,
pauseTime: cdk.Duration.minutes(5),
}),
});
// Scaling Policies
asg.scaleOnCpuUtilization('CpuScaling', {
targetUtilizationPercent: 70,
});
asg.scaleOnMetric('QueueDepthScaling', {
metric: sqsQueue.metricApproximateNumberOfMessagesVisible(),
scalingSteps: [
{ upper: 0, change: -1 },
{ lower: 100, change: +1 },
{ lower: 500, change: +3 },
],
adjustmentType: autoscaling.AdjustmentType.CHANGE_IN_CAPACITY,
});
}
}
Example 4: Lambda Powertools
// Lambda handler with Powertools
import { Logger } from '@aws-lambda-powertools/logger';
import { Tracer } from '@aws-lambda-powertools/tracer';
import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics';
import { injectLambdaContext } from '@aws-lambda-powertools/logger/middleware';
import { captureLambdaHandler } from '@aws-lambda-powertools/tracer/middleware';
import { logMetrics } from '@aws-lambda-powertools/metrics/middleware';
import middy from '@middy/core';
const logger = new Logger({ serviceName: 'order-service' });
const tracer = new Tracer({ serviceName: 'order-service' });
const metrics = new Metrics({ namespace: 'OrderService', serviceName: 'order-service' });
interface OrderEvent {
body: string;
requestContext: { requestId: string };
}
const createOrderHandler = async (event: OrderEvent) => {
const correlationId = event.requestContext.requestId;
// Add correlation ID to all logs
logger.appendKeys({ correlationId });
logger.info('Processing order request');
// Create traced segment
const segment = tracer.getSegment();
const subsegment = segment?.addNewSubsegment('processOrder');
try {
const order = JSON.parse(event.body);
// Trace external calls
tracer.putAnnotation('customerId', order.customerId);
tracer.putMetadata('orderItems', order.items);
const result = await processOrder(order);
// Record metrics
metrics.addMetric('OrderCreated', MetricUnits.Count, 1);
metrics.addMetric('OrderValue', MetricUnits.Count, result.total);
logger.info('Order created successfully', { orderId: result.id });
return {
statusCode: 201,
body: JSON.stringify(result),
};
} catch (error) {
logger.error('Order creation failed', error as Error);
metrics.addMetric('OrderFailed', MetricUnits.Count, 1);
subsegment?.addError(error as Error);
throw error;
} finally {
subsegment?.close();
}
};
export const handler = middy(createOrderHandler)
.use(injectLambdaContext(logger))
.use(captureLambdaHandler(tracer))
.use(logMetrics(metrics));
Example 5: Container Optimization
# Multi-stage build for Java application
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /app
# Cache dependencies
COPY pom.xml .
COPY .mvn .mvn
COPY mvnw .
RUN ./mvnw dependency:go-offline -B
# Build application
COPY src src
RUN ./mvnw package -DskipTests -Pnative
# Runtime stage
FROM eclipse-temurin:21-jre-alpine
# Security: non-root user
RUN addgroup -S app && adduser -S app -G app
USER app
WORKDIR /app
# Copy only the jar
COPY --from=builder /app/target/*.jar app.jar
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=30s --retries=3 \
CMD wget -q --spider http://localhost:8080/actuator/health || exit 1
# JVM tuning for containers
ENV JAVA_OPTS="-XX:+UseContainerSupport \
-XX:MaxRAMPercentage=75.0 \
-XX:InitialRAMPercentage=50.0 \
-XX:+UseG1GC \
-XX:+UseStringDeduplication \
-Djava.security.egd=file:/dev/./urandom"
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
Anti-Patterns
❌ Over-Provisioning
# WRONG - paying for unused capacity
Resources:
Instance:
Type: AWS::EC2::Instance
Properties:
InstanceType: m5.4xlarge # 16 vCPU, 64 GB for a simple web app
# ✅ CORRECT - right-size based on metrics
InstanceType: t3.medium # Start small, scale up based on data
❌ Lambda Cold Start Ignorance
// WRONG - connection per invocation
export const handler = async () => {
const client = new DynamoDBClient({}); // Cold start penalty
// ...
};
// ✅ CORRECT - reuse connections
const client = new DynamoDBClient({}); // Initialized once
export const handler = async () => {
// Reuse client
};