Container Cost Optimization on ECS and EKS

Optimize container costs on ECS and EKS through right-sizing, Spot instances, Fargate Spot, and resource efficiency strategies.

Container workloads offer unique optimization opportunities through right-sizing, Spot capacity, and resource efficiency strategies on ECS and EKS.

Resource Right-Sizing

Container Insights Analysis

import boto3

cloudwatch = boto3.client('cloudwatch')

def analyze_container_utilization(cluster, service, days=7):
    end = datetime.utcnow()
    start = end - timedelta(days=days)
    
    metrics = {}
    
    for metric in ['CpuUtilized', 'CpuReserved', 'MemoryUtilized', 'MemoryReserved']:
        response = cloudwatch.get_metric_statistics(
            Namespace='ECS/ContainerInsights',
            MetricName=metric,
            Dimensions=[
                {'Name': 'ClusterName', 'Value': cluster},
                {'Name': 'ServiceName', 'Value': service}
            ],
            StartTime=start,
            EndTime=end,
            Period=3600,
            Statistics=['Average', 'Maximum']
        )
        
        if response['Datapoints']:
            metrics[metric] = {
                'avg': sum(d['Average'] for d in response['Datapoints']) / len(response['Datapoints']),
                'max': max(d['Maximum'] for d in response['Datapoints'])
            }
    
    # Calculate utilization percentages
    cpu_utilization = (metrics['CpuUtilized']['avg'] / metrics['CpuReserved']['avg']) * 100
    memory_utilization = (metrics['MemoryUtilized']['avg'] / metrics['MemoryReserved']['avg']) * 100
    
    return {
        'cpu_utilization_avg': cpu_utilization,
        'memory_utilization_avg': memory_utilization,
        'recommendation': generate_recommendation(cpu_utilization, memory_utilization)
    }

def generate_recommendation(cpu_util, mem_util):
    if cpu_util < 30 and mem_util < 30:
        return 'Significantly over-provisioned. Consider reducing by 50%'
    elif cpu_util < 50 and mem_util < 50:
        return 'Over-provisioned. Consider reducing by 25%'
    elif cpu_util > 80 or mem_util > 80:
        return 'Near capacity. Consider increasing resources'
    return 'Appropriately sized'

Fargate Spot

Task Definition with Spot

SpotTaskDefinition:
  Type: AWS::ECS::TaskDefinition
  Properties:
    Family: spot-service
    Cpu: '256'
    Memory: '512'
    NetworkMode: awsvpc
    RequiresCompatibilities:
      - FARGATE
    ContainerDefinitions:
      - Name: app
        Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/app:latest
        Essential: true
        PortMappings:
          - ContainerPort: 8080

SpotService:
  Type: AWS::ECS::Service
  Properties:
    Cluster: !Ref Cluster
    TaskDefinition: !Ref SpotTaskDefinition
    DesiredCount: 5
    CapacityProviderStrategy:
      - CapacityProvider: FARGATE_SPOT
        Weight: 4
        Base: 0
      - CapacityProvider: FARGATE
        Weight: 1
        Base: 2  # 2 On-Demand tasks for stability

Graviton Migration

Cost Comparison

def compare_graviton_savings():
    comparisons = {
        'Fargate': {
            'x86': {'cpu': 0.04048, 'memory': 0.004445},  # per vCPU-hour, per GB-hour
            'arm64': {'cpu': 0.03238, 'memory': 0.003556}  # 20% cheaper
        },
        'EC2': {
            'm6i.large': 0.096,
            'm6g.large': 0.077  # Graviton, 20% cheaper
        }
    }
    
    # Calculate monthly savings for 10 tasks, 2 vCPU, 4 GB each
    tasks = 10
    vcpu = 2
    memory_gb = 4
    hours = 730
    
    x86_cost = tasks * hours * (vcpu * comparisons['Fargate']['x86']['cpu'] + 
                                 memory_gb * comparisons['Fargate']['x86']['memory'])
    
    arm_cost = tasks * hours * (vcpu * comparisons['Fargate']['arm64']['cpu'] + 
                                memory_gb * comparisons['Fargate']['arm64']['memory'])
    
    return {
        'x86_monthly_cost': x86_cost,
        'graviton_monthly_cost': arm_cost,
        'monthly_savings': x86_cost - arm_cost,
        'savings_percentage': ((x86_cost - arm_cost) / x86_cost) * 100
    }

Multi-Architecture Build

# buildspec.yml for multi-arch images
version: 0.2

phases:
  pre_build:
    commands:
      - aws ecr get-login-password | docker login --username AWS --password-stdin $ECR_REPO
      - docker buildx create --use
  
  build:
    commands:
      - |
        docker buildx build \
          --platform linux/amd64,linux/arm64 \
          --tag $ECR_REPO:latest \
          --push .

EKS Cost Optimization

Karpenter for Efficient Scaling

apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
  name: cost-optimized
spec:
  requirements:
    - key: karpenter.sh/capacity-type
      operator: In
      values: ["spot", "on-demand"]
    - key: kubernetes.io/arch
      operator: In
      values: ["amd64", "arm64"]
    - key: node.kubernetes.io/instance-type
      operator: In
      values:
        - m5.large
        - m5a.large
        - m5.xlarge
        - m6g.large
        - m6g.xlarge
  
  limits:
    resources:
      cpu: 1000
      memory: 2000Gi
  
  provider:
    subnetSelector:
      karpenter.sh/discovery: my-cluster
    securityGroupSelector:
      karpenter.sh/discovery: my-cluster
    
  consolidation:
    enabled: true

Pod Resource Requests

apiVersion: apps/v1
kind: Deployment
metadata:
  name: cost-optimized-app
spec:
  template:
    spec:
      containers:
        - name: app
          resources:
            requests:
              cpu: "100m"      # Start small
              memory: "128Mi"
            limits:
              cpu: "500m"      # Allow bursting
              memory: "256Mi"

Working with Warqline

We are a cloud engineering consultancy and an official AWS and Google Cloud partner. If you are running this in production and want a second pair of eyes, we scope work in a free 45-minute technical call: you describe what you are running and what worries you, and we tell you what we would look at first.

Talk to an engineer

Conclusion

Container cost optimization combines right-sizing based on actual utilization, Spot capacity for fault-tolerant workloads, and Graviton migration for consistent savings. Monitor utilization continuously and adjust resources accordingly.