Free previewArchitecture note8 min read
GitHub → ECS Flow
Every component from GitHub push to running ECS tasks, source control, CI/CD, Docker, ECR, ECS, ALB, IAM, and CloudWatch in one end-to-end map.
Deployment Pipeline: Code → Production
Developer pushes code to GitHub
↓
GitHub detects push (webhook)
↓
GitHub Actions workflow triggered
↓
Build stage: Compile, test, lint
↓
Build Docker image
↓
Push to ECR (Docker registry)
↓
Update ECS task definition (new image URI)
↓
ECS service detects new task definition
↓
Rolling deployment: drain old tasks, start new ones
↓
Application running on new code
↓
CloudWatch monitors health
↓
If unhealthy: auto-rollback to previous version
All Components Involved
Source Control (GitHub)
plain text
Your code lives here
├─ main branch (production)
├─ dev branch (development)
└─ feature branches (work in progress)
When you push to main:
└─ GitHub webhook fires → tells GitHub Actions "something changed"GitHub Actions (CI/CD Orchestration)
plain text
Listens for webhook from GitHub
↓
Runs workflow file: .github/workflows/deploy.yml
↓
Workflow defines jobs:
├─ Build: compile, test, lint
├─ Docker: create image
├─ Push: upload to ECR
└─ Deploy: update ECSExample workflow file:
yaml
name: Deploy to ECS
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run tests
run: npm test
- name: Build Docker image
run: docker build -t rds-course:latest .
- name: Push to ECR
run: |
aws ecr get-login-password | docker login --username AWS --password-stdin $ECR_URL
docker tag rds-course:latest $ECR_URL/rds-course:$GITHUB_SHA
docker push $ECR_URL/rds-course:$GITHUB_SHA
- name: Update ECS task definition
run: |
aws ecs update-service \
--cluster production \
--service rds-course-service \
--task-definition rds-course:NEW_REVISIONDocker (Containerization)
plain text
Dockerfile: Recipe for your application
├─ Start from base image (node:18, python:3.11, etc.)
├─ Copy code into image
├─ Install dependencies
├─ Build/compile
└─ Set startup command
Result: Immutable artifact (Docker image)
└─ Same image runs everywhere: dev, staging, prodExample Dockerfile:
docker
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]ECR (Elastic Container Registry)
plain text
AWS's Docker image storage
└─ Like Docker Hub but for your private company images
When GitHub Actions pushes image:
├─ Authenticates to ECR (via IAM role)
├─ Uploads image layers
├─ Tags with: timestamp, git commit hash, "latest"
└─ Stores versioned images (never delete old ones)
Later: ECS pulls from ECR when launching tasksECS (Elastic Container Service)
plain text
Orchestrator that runs Docker containers at scale
Components:
├─ Cluster: logical grouping of EC2 instances or Fargate
├─ Task Definition: blueprint (Docker image, CPU, memory, ports, env vars)
├─ Service: keeps desired number of tasks running
│ └─ If a task dies, service launches a new one
└─ Task: running instance of the application
Deployment flow:
└─ You update task definition with new image URI
├─ ECS detects change
├─ Stops old tasks (gracefully drains connections)
├─ Launches new tasks with new image
└─ Updates load balancer routingALB (Application Load Balancer)
plain text
Routes incoming traffic to healthy tasks
Flow:
├─ User request → ALB
├─ ALB checks: which tasks are healthy?
├─ Routes to healthy task
├─ During deployment:
│ ├─ Old tasks: connection draining (30s to finish existing requests)
│ ├─ New tasks: warm up (health checks pass)
│ └─ Traffic switches seamlessly
└─ No downtimeIAM Roles (Authentication)
plain text
GitHub Actions needs permissions:
├─ Read code from GitHub
├─ Push to ECR
└─ Update ECS service
ECS tasks need permissions:
├─ Read secrets from Secrets Manager
├─ Write logs to CloudWatch
└─ Pull images from ECR
Each component gets minimal permissions (least privilege)CloudWatch (Monitoring)
plain text
Collects metrics + logs
During deployment:
├─ Old tasks: logs redirect to CloudWatch
├─ New tasks: logs stream immediately
├─ Metrics tracked:
│ ├─ CPU utilization
│ ├─ Memory usage
│ ├─ Request count
│ └─ Error rate
If error rate spikes:
└─ Alarm triggers → auto-rollback (optional)Simplified Diagram
plain text
Developer (You)
↓
↓ git push main
↓
GitHub (Code repo)
↓
↓ webhook: "code changed"
↓
GitHub Actions (CI/CD)
├─ Runs tests
├─ Builds Docker image
└─ Pushes to ECR
↓
↓ docker push image:v123
↓
ECR (Image registry)
↓
↓ "new image available"
↓
ECS Service
├─ Old Task 1: ❌ Stop (graceful drain)
├─ Old Task 2: ❌ Stop
├─ New Task 1: ✅ Start (new image)
├─ New Task 2: ✅ Start
└─ Health check: pass → add to ALB
↓
↓ Route traffic
↓
ALB (Load Balancer)
↓
↓ HTTP/HTTPS
↓
Internet (Users)Deployment Strategies
Rolling Deployment (Most common)
plain text
Before:
Task 1 (old) → ALB → User
Task 2 (old) → ALB → User
Task 3 (old) → ALB → User
During:
Task 1 (old) → ALB ✅ still routing
Task 2 (new) ↓ starting...
Task 3 (new) ↓ starting...
After:
Task 1 (new) → ALB → User
Task 2 (new) → ALB → User
Task 3 (new) → ALB → User
Zero downtime ✅
Gradual rollout ✅
Can rollback mid-deployment ✅Blue-Green Deployment
plain text
Before:
Blue environment (old code)
├─ Task 1, 2, 3 running
└─ ALB routing to Blue
During:
Green environment (new code)
├─ Task 1, 2, 3 running (parallel)
└─ Running health checks
After:
ALB switches: Blue → Green
├─ Instant cutover
├─ Old Blue environment stays running (instant rollback available)
└─ If bad: switch back to Blue instantly
Zero downtime ✅
Instant rollback ✅
Needs double resources ❌ (expensive)Canary Deployment
plain text
Before:
All tasks running old code
During:
Send 10% traffic to new code
├─ Monitor error rate
├─ If error rate < threshold: continue
├─ Else: stop, rollback
90% → old code
10% → new code
Monitor 5 minutes...
50% → old code
50% → new code
Monitor 5 minutes...
0% → old code
100% → new code ✅
Catches bugs early ✅
Gradual rollout ✅
Complex setup ❌Key Concepts You Need to Understand
Task Definition (ECS)
plain text
A template that says:
├─ Use this Docker image
├─ Allocate this much CPU (256, 512, 1024 units)
├─ Allocate this much memory (512MB, 1GB, 2GB)
├─ Map port 3000 (container) to 80 (ALB)
├─ Environment variables (DATABASE_URL, etc.)
├─ Log configuration (CloudWatch)
└─ Secrets (Secrets Manager references)
When you deploy:
└─ You create NEW task definition revision
(old one stays around for rollback)Desired Count
plain text
ECS Service: "I want 3 tasks running at all times"
If one task dies:
└─ Service launches a new one (auto-healing)
During deployment:
├─ Old: 3 tasks (old image)
├─ New: 1 task (new image). ALB health check
├─ If healthy: stop 1 old, start 1 new
├─ Repeat until: 0 old, 3 new
└─ Desired count always = 3 ✅Health Checks
plain text
ALB asks: "Is this task healthy?"
Task has an HTTP endpoint: /health
└─ Returns 200 OK if ready
ALB checks every 10 seconds:
├─ Task responsive? Yes → route traffic
├─ Task responsive? No → remove from routing
During deployment:
├─ New task starts
├─ Fails health checks (5-10 tries)
├─ Finally passes
├─ ALB adds to rotation
└─ Old task can now drain safelyConnection Draining
plain text
Old task receives shutdown signal
Before draining:
├─ ALB stops sending NEW requests
├─ Old task finishes existing requests (max 30s)
└─ Task shuts down cleanly
Result: No in-flight request loss ✅Common Pitfalls
Task Definition Not Updated
plain text
❌ You push new code
❌ GitHub Actions runs
❌ Docker image pushed to ECR
❌ But you forgot to update task definition
❌ ECS still using old image
❌ Users don't see your new code
Fix: Always update task definition in workflowNo Rollback Plan
plain text
❌ Deploy new code
❌ 10 minutes later: users report bugs
❌ You scramble to fix code
❌ 30 minutes of downtime
Fix: Keep previous task definition
└─ In ECS console: click "Rollback to previous revision"
└─ Takes 2 minutesHealth Check Too Strict
plain text
❌ Health check URL: /health
❌ New code takes 15 seconds to boot
❌ ALB times out after 10 seconds
❌ Task fails health check
❌ Never routes traffic to new task
❌ Deployment stuck
Fix: Increase health check timeout or fix code startup timeNot Running Tests in CI
plain text
❌ Push broken code to GitHub
❌ GitHub Actions doesn't run tests
❌ Broken code deployed to production
❌ Users affected
Fix: Always run tests before Docker build