| 1 | Overview | 2 | Architecture Overview |
| 3 | AWS Service Topology | 4 | Implementation Guide |
| 5 | Decision Criteria | 6 | Cost Model |
| 7 | Anti-Patterns to Avoid | 8 | References |
Overview
AWS offers two primary container platforms — ECS (Elastic Container Service) and EKS (Elastic Kubernetes Service) — each with a Fargate launch type that removes EC2 instance management. The containerisation journey on AWS starts with ECR for image storage, moves through a CI/CD pipeline that builds and pushes images, and ends with either ECS or EKS running those images behind an Application Load Balancer.
The Fargate model means AWS manages the underlying infrastructure. You define CPU, memory, and network requirements per task, and AWS provisions the right compute. No SSH into nodes, no cluster upgrades, no capacity planning for the container host layer.
Architecture Overview
%%{init:{'theme':'base','themeVariables':{'fontSize':'14px','fontFamily':'IBM Plex Sans, system-ui, sans-serif','primaryColor':'#DBEAFE','primaryTextColor':'#1e3a5f','primaryBorderColor':'#2563EB','lineColor':'#374151','clusterBkg':'#F9FAFB','clusterBorder':'#D1D5DB','edgeLabelBackground':'#FFFFFF'},'flowchart':{'curve':'orthogonal','padding':30,'nodeSpacing':65,'rankSpacing':75,'useMaxWidth':true}}}%% flowchart TD START([Developer Pushes Code]) START --> CI[CI Pipeline\nBuild container image\nRun tests and SAST] CI --> SCAN{Image Security\nScan Passed?} SCAN -->|Vulnerabilities found| FAIL[Block deployment\nNotify engineer\nLog to Security Hub] SCAN -->|Clean| ECR[Push to ECR\nImmutable tags\nLifecycle policy] ECR --> DEPLOY{Deployment\nTarget} DEPLOY -->|No Kubernetes needed| ECS[ECS Fargate\nTask Definition\nService with desired count] DEPLOY -->|Kubernetes ecosystem| EKS[EKS Fargate\nor EC2 node groups\nHelm charts] ECS --> ALB[Application Load Balancer\nPath-based routing\nHealth check per target group] EKS --> ALB ALB --> USERS([Users Reach the Application]) style START fill:#4f8ef7,color:#fff style USERS fill:#10b981,color:#fff style FAIL fill:#fef3c7 style ECR fill:#e0f2fe
AWS Service Topology
%%{init:{'theme':'base','themeVariables':{'fontSize':'14px','fontFamily':'IBM Plex Sans, system-ui, sans-serif','primaryColor':'#DBEAFE','primaryTextColor':'#1e3a5f','primaryBorderColor':'#2563EB','lineColor':'#374151','clusterBkg':'#F9FAFB','clusterBorder':'#D1D5DB','edgeLabelBackground':'#FFFFFF'},'flowchart':{'curve':'orthogonal','padding':30,'nodeSpacing':65,'rankSpacing':75,'useMaxWidth':true}}}%% flowchart TD USER[Internet Traffic] subgraph INGRESS["Ingress Layer"] R53[Route 53\nDNS with health checks\nLatency-based routing] ACM[ACM Certificate\nAuto-renewed TLS\nWildcard support] ALB_C[Application Load Balancer\nHTTPS listener\nTarget group per service] WAF_C[AWS WAF\nRate limiting\nOWASP rule groups] end subgraph PLATFORM["ECS Fargate Cluster"] SVC1[Service A\n2 vCPU, 4 GB\nDesired count: 3] SVC2[Service B\n1 vCPU, 2 GB\nAuto-scaling policy] SVC3[Service C\n0.5 vCPU, 1 GB\nScheduled task] TASK_DEF[Task Definitions\nContainer image + tag\nEnvironment variables from SSM] end subgraph REGISTRY["Amazon ECR"] REPO[Private Repository\nImmutable image tags\nLifecycle: keep last 10] SCAN_ECR[Enhanced Scanning\nContinuous CVE scan\nFindings to Security Hub] end subgraph NETWORK["VPC Network"] PUB[Public Subnets\nALB only\nNo container workloads] PRIV[Private Subnets\nAll ECS tasks\nNAT Gateway for egress] end USER --> R53 --> ACM --> WAF_C --> ALB_C ALB_C --> SVC1 & SVC2 & SVC3 TASK_DEF -.->|Defines| SVC1 & SVC2 & SVC3 REPO --> TASK_DEF SCAN_ECR -.->|Scans| REPO ALB_C --> PUB SVC1 & SVC2 & SVC3 --> PRIV style USER fill:#4f8ef7,color:#fff style SCAN_ECR fill:#e0f2fe style PRIV fill:#e0f2fe
Implementation Guide
CDK Stack — ECS Fargate Service
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ecsPatterns from 'aws-cdk-lib/aws-ecs-patterns';
import * as ecr from 'aws-cdk-lib/aws-ecr';
import { Duration } from 'aws-cdk-lib';
export class ContainerPlatformStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// VPC — private subnets for tasks
const vpc = new ec2.Vpc(this, 'AppVpc', {
maxAzs: 3,
natGateways: 1,
subnetConfiguration: [
{ name: 'Public', subnetType: ec2.SubnetType.PUBLIC, cidrMask: 24 },
{ name: 'Private', subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
cidrMask: 24 },
],
});
// ECR repository with scanning
const repo = new ecr.Repository(this, 'AppRepo', {
repositoryName: 'app-service',
imageScanOnPush: true,
imageTagMutability: ecr.TagMutability.IMMUTABLE,
lifecycleRules: [{ maxImageCount: 10, description: 'Keep last 10 images' }],
});
// ECS Cluster
const cluster = new ecs.Cluster(this, 'AppCluster', {
vpc,
containerInsights: true,
});
// Fargate service with ALB
const service = new ecsPatterns.ApplicationLoadBalancedFargateService(
this, 'AppService', {
cluster,
cpu: 512,
memoryLimitMiB: 1024,
desiredCount: 3,
taskImageOptions: {
image: ecs.ContainerImage.fromEcrRepository(repo, 'latest'),
containerPort: 8080,
environment: { NODE_ENV: 'production' },
},
publicLoadBalancer: true,
assignPublicIp: false, // tasks in private subnets
listenerPort: 443,
});
// Auto-scaling
const scaling = service.service.autoScaleTaskCount({
minCapacity: 2,
maxCapacity: 20,
});
scaling.scaleOnCpuUtilization('CpuScaling', {
targetUtilizationPercent: 70,
scaleInCooldown: Duration.seconds(60),
scaleOutCooldown: Duration.seconds(30),
});
scaling.scaleOnRequestCount('RequestScaling', {
requestsPerTarget: 1000,
targetGroup: service.targetGroup,
});
// Health check tuning
service.targetGroup.configureHealthCheck({
path: '/health',
interval: Duration.seconds(30),
timeout: Duration.seconds(5),
healthyThresholdCount: 2,
unhealthyThresholdCount: 3,
});
}
}
Decision Criteria
| Factor | ECS Fargate | EKS Fargate | EKS EC2 Nodes |
|---|---|---|---|
| Operational overhead | Lowest | Medium | Highest |
| Kubernetes ecosystem | No | Yes | Yes |
| Cluster upgrades | None | Managed | Manual |
| Cost at scale | Medium | Medium | Lowest |
| Custom schedulers | No | Yes | Yes |
| Service mesh | App Mesh | Istio or App Mesh | Istio or App Mesh |
| Best for | Greenfield, smaller teams | Teams needing K8s APIs | Large-scale, K8s expertise |
Recommendation for most Ascendion projects: Start with ECS Fargate. Migrate to EKS only when you need a specific Kubernetes capability — custom operators, advanced scheduling, or client-required Kubernetes.
Cost Model
| Component | Cost Driver | Optimisation |
|---|---|---|
| Fargate tasks | vCPU and memory per second | Right-size to actual utilisation |
| NAT Gateway | Data processing and hourly | Share across services in same VPC |
| ALB | LCU hours and rule evaluations | Consolidate services on one ALB |
| ECR | Storage and data transfer | Lifecycle policies remove old images |
Fargate Spot reduces task cost by 70% for interruption-tolerant workloads. Use Spot for batch processing, non-production environments, and stateless services that restart gracefully.
Anti-Patterns to Avoid
Taking an application that was designed to run as a persistent server — holding in-memory sessions, writing to local disk, running background threads — and packaging it as a Docker image without changing the architecture. The application behaves unpredictably when Fargate replaces the task, local files disappear on restart, and sessions are lost on scale-in.
Containerise after making the application stateless. Move sessions to ElastiCache, move files to S3, move background jobs to SQS. Containers assume the task is replaceable at any time.
Deploying images tagged as latest or with a branch name. Two deployments of the same tag can produce different behaviour if the image was rebuilt. Impossible to roll back to a specific version when latest points to the newest build.
Tag every image with the Git commit SHA. ECR immutable tags prevent overwriting. Deployments reference an exact image digest, making every deployment reproducible and rollback trivial.
Flowchart
References
- AWS — Amazon ECS Developer Guide. docs.aws.amazon.com/ecs
- AWS — Amazon EKS Best Practices Guide. aws.github.io/aws-eks-best-practices
- AWS — Fargate pricing. aws.amazon.com/fargate/pricing
- CNCF — Cloud Native Landscape. landscape.cncf.io