3. Provision an ECS Cluster

Goal

Deploy a containerized nginx application on AWS ECS Fargate to learn container orchestration fundamentals.

What you’ll learn:

  • How to create ECS clusters, task definitions, and services
  • When to use Fargate for serverless container workloads
  • How IAM roles, security groups, and CloudWatch logging work together
  • How to configure public network access for containers

Table of Contents

  1. Prerequisites
  2. Exercise Steps
  3. Common Issues
  4. Summary
  5. Done! 🎉

Prerequisites

Before starting, ensure you have:

  • ✓ AWS account with administrative access
  • ✓ AWS region selected (e.g., us-east-1 or eu-west-1)
  • ✓ Default VPC exists in your region (AWS creates this automatically)
  • ✓ Web browser for AWS Console access

Exercise Steps

Overview

  1. Create IAM Roles
  2. Create CloudWatch Log Group
  3. Create Security Group
  4. Create ECS Cluster
  5. Create Task Definition
  6. Create ECS Service
  7. Verify Deployment

Step 1: Create IAM Roles

ECS requires two IAM roles: a task execution role (for pulling images and writing logs) and a task role (for your application to access AWS services).

Create Task Execution Role

  1. Navigate to IAM → Roles → Create role

  2. Select “AWS service” → “Elastic Container Service” → “Elastic Container Service Task”

  3. Click Next

  4. Select the policy: AmazonECSTaskExecutionRolePolicy

  5. Click Next

  6. Enter role name: dev-ecs-task-execution-role

  7. Click Create role

Create Task Role

  1. Click Create role again

  2. Select “AWS service” → “Elastic Container Service” → “Elastic Container Service Task”

  3. Click Next

  4. Skip adding policies (nginx doesn’t need AWS API access)

  5. Click Next

  6. Enter role name: dev-ecs-task-role

  7. Click Create role

ℹ Concept Deep Dive

Task Execution Role is used by ECS infrastructure to pull container images from Docker Hub/ECR and write logs to CloudWatch. It needs the AmazonECSTaskExecutionRolePolicy managed policy.

Task Role is used by your application code inside the container to access AWS services like S3, DynamoDB, etc. For nginx (a web server), we don’t need any AWS permissions, so this role has no policies attached.

âš  Common Mistakes

  • Selecting “Elastic Container Service” instead of “Elastic Container Service Task” as the use case
  • Forgetting to attach AmazonECSTaskExecutionRolePolicy to the execution role
  • Confusing which role does what (execution = infrastructure, task = application)

✓ Quick check: Two roles created with names dev-ecs-task-execution-role and dev-ecs-task-role

Step 2: Create CloudWatch Log Group

CloudWatch Logs captures container stdout/stderr for debugging and monitoring.

  1. Navigate to CloudWatch → Log groups

  2. Click Create log group

  3. Enter log group name: /ecs/dev/nginx

  4. Select retention: “7 days”

  5. Click Create

ℹ Concept Deep Dive

The naming pattern /ecs/environment/service organizes logs hierarchically. ECS automatically creates log streams within this group named ecs/nginx/<task-id> when containers start.

âš  Common Mistakes

  • Missing the leading slash - must be /ecs/dev/nginx not ecs/dev/nginx
  • Typos in the log group name - it must match exactly in the task definition

✓ Quick check: Log group /ecs/dev/nginx visible with 7-day retention

Step 3: Create Security Group

Security groups control network traffic to your containers. You need to allow HTTP (port 80) from the internet.

  1. Navigate to EC2 → Security Groups

  2. Click Create security group

  3. Enter security group name: dev-ecs-tasks-sg

  4. Enter description: “Security group for ECS tasks - allows HTTP from internet”

  5. Select your default VPC

  6. Under Inbound rules, click Add rule:

    • Type: HTTP
    • Source: 0.0.0.0/0
    • Description: Allow HTTP from internet
  7. Verify Outbound rules has:

    • Type: All traffic
    • Destination: 0.0.0.0/0
  8. Click Create security group

ℹ Concept Deep Dive

Inbound rules allow HTTP traffic (port 80) from anywhere (0.0.0.0/0). For production, you’d restrict this or use a load balancer.

Outbound rules allow all traffic so the container can pull images from Docker Hub and make any HTTP requests.

âš  Common Mistakes

  • Selecting the wrong VPC - it won’t appear during ECS service creation
  • Forgetting the inbound rule for port 80 - nginx will be unreachable

✓ Quick check: Security group has HTTP inbound rule and all-traffic outbound rule

Step 4: Create ECS Cluster

An ECS cluster is a logical grouping of container resources. With Fargate, there are no servers to manage.

  1. Navigate to Elastic Container Service → Clusters

  2. Click Create cluster

  3. Enter cluster name: dev-cluster

  4. Verify no infrastructure is selected (Fargate is serverless)

  5. Check “Use Container Insights”

  6. Click Create

ℹ Concept Deep Dive

Fargate is serverless - AWS manages all infrastructure. You only define CPU, memory, and networking for your containers.

Container Insights provides CloudWatch metrics for CPU, memory, and network usage (~$0.30/month but valuable for learning).

âš  Common Mistakes

  • Trying to select EC2 infrastructure - leave it empty for Fargate-only

✓ Quick check: Cluster shows “ACTIVE” status

Step 5: Create Task Definition

A task definition is the blueprint for your container - it defines the image, CPU, memory, ports, and logging.

  1. Navigate to Task definitions → Create new task definition

  2. Enter task definition family: dev-nginx

  3. Under Infrastructure:

    • Launch type: AWS Fargate
    • OS/Architecture: Linux/X86_64
    • CPU: .25 vCPU
    • Memory: 0.5 GB
  4. Under Task roles:

    • Task execution role: dev-ecs-task-execution-role
    • Task role: dev-ecs-task-role
  5. Under Container - 1:

    • Name: nginx
    • Image URI: nginx:latest
  6. Under Port mappings, click Add:

    • Container port: 80
    • Protocol: TCP
    • App protocol: HTTP
  7. Expand Log collection:

    • Check “Use log collection”
    • Log driver: AWS CloudWatch
    • awslogs-group: /ecs/dev/nginx
    • awslogs-region: (your region, auto-filled)
    • awslogs-stream-prefix: ecs
  8. Click Create

ℹ Concept Deep Dive

CPU and Memory: Fargate has specific valid combinations. We use the minimum (0.25 vCPU, 0.5 GB) because nginx is lightweight and this is the cheapest option (~$10/month for 24/7).

Network mode: Fargate uses awsvpc automatically - each task gets its own elastic network interface (ENI) with a unique IP address.

Log configuration: The awslogs driver sends container stdout/stderr directly to CloudWatch. The stream prefix creates streams as ecs/nginx/<task-id>.

âš  Common Mistakes

  • Invalid CPU/memory combination - must use 0.25 vCPU with 0.5 GB
  • Typo in log group name - logs won’t appear
  • Wrong port number - nginx listens on port 80 by default
  • Not enabling log collection - you won’t see any logs

✓ Quick check: Task definition dev-nginx:1 shows ACTIVE with Fargate compatibility

Step 6: Create ECS Service

An ECS service maintains your desired number of running tasks and handles deployments.

  1. Navigate to Clusters → dev-cluster → Services tab

  2. Click Create

  3. Under Environment:

    • Compute options: Launch type
    • Launch type: FARGATE
  4. Under Deployment configuration:

    • Application type: Service
    • Task definition family: dev-nginx
    • Revision: 1 (latest)
    • Service name: dev-nginx-service
    • Desired tasks: 1
  5. Under Networking:

    • VPC: Select your default VPC
    • Subnets: Select all available subnets
    • Security group: Select “Use an existing security group”
    • Choose dev-ecs-tasks-sg
    • Remove any default security groups
    • Public IP: Turn ON (CRITICAL!)
  6. Verify Load balancing is set to “None”

  7. Verify Service auto scaling is “Do not use”

  8. Click Create

ℹ Concept Deep Dive

Desired count of 1 means ECS will always try to keep 1 task running. If it crashes, ECS automatically starts a new one.

Public IP assignment is CRITICAL - without it, your container cannot be accessed from the internet even with the correct security group. This is the most common mistake.

Multiple subnets allow ECS to distribute tasks across availability zones for better reliability.

âš  Common Mistakes

  • Forgetting to enable Public IP - #1 reason services are unreachable
  • Selecting only one subnet - limits availability
  • Not removing default security groups - may conflict with your custom one

✓ Quick check: Service shows “ACTIVE” with “Running count: 1”

Step 7: Verify Deployment

Verify your nginx container is running and accessible.

Find Task Public IP

  1. Navigate to your cluster → Tasks tab

  2. Wait for task status to become RUNNING (1-2 minutes)

  3. Click the task ID

  4. Find the “Public IP” in the Configuration section

  5. Copy the IP address

Test Nginx

  1. Open a browser tab

  2. Navigate to http://<your-public-ip>

  3. Verify you see “Welcome to nginx!” page

Check Logs

  1. Navigate to CloudWatch → Log groups → /ecs/dev/nginx

  2. Click the log stream (named ecs/nginx/<task-id>)

  3. Verify you see nginx access logs from your browser requests

ℹ Concept Deep Dive

Task lifecycle: PROVISIONING → PENDING (pulling image) → RUNNING. The PENDING phase can take 1-2 minutes for Docker Hub to deliver the image.

Public IP changes on every task restart since there’s no load balancer. This is temporary infrastructure for learning.

âš  Common Mistakes

  • Testing before task reaches RUNNING state
  • Using https:// instead of http:// - we haven’t configured SSL
  • Checking logs immediately - wait 30 seconds for them to appear

✓ Success indicators:

  • ✓ Task status is RUNNING
  • ✓ Task has a public IP assigned
  • ✓ Browser shows “Welcome to nginx!” page
  • ✓ CloudWatch logs show HTTP 200 access logs

Common Issues

“Task failed to start” or keeps restarting:

  • Check ECS service Events tab for error messages
  • Verify CPU/memory is 0.25 vCPU with 0.5 GB
  • Ensure task execution role has AmazonECSTaskExecutionRolePolicy
  • Check CloudWatch logs for container errors

“Cannot pull container image”:

  • Docker Hub rate limits (100 pulls per 6 hours for anonymous users)
  • Wait 30 minutes and force a new deployment
  • Verify outbound security group rules allow all traffic

“Task has no public IP”:

  • Update service: Clusters → Services → Update
  • Under Networking, enable “Auto-assign public IP”
  • Force new deployment

“Cannot access nginx” (connection timeout):

  • Verify security group allows port 80 from 0.0.0.0/0
  • Ensure using http:// not https://
  • Confirm task is in RUNNING state
  • Try from different network (mobile hotspot) to rule out firewall

“No logs in CloudWatch”:

  • Verify log group name matches exactly: /ecs/dev/nginx
  • Check task execution role has log writing permissions
  • Wait 1-2 minutes for logs to appear
  • Verify “Use log collection” was enabled in task definition

Summary

You’ve successfully deployed nginx on ECS Fargate using the AWS Console. Your infrastructure includes:

Key takeaway:

ECS with Fargate provides serverless container orchestration - you define what to run (task definition) and how many instances (service desired count), and AWS handles all infrastructure management, scaling, and health monitoring.

Done! 🎉

You’ve learned how to deploy containerized applications on ECS Fargate using the AWS Console. This foundation prepares you for production deployments with load balancers, auto-scaling, and custom VPCs.

To clean up resources:

  1. Delete the ECS service (Clusters → Services → Delete)
  2. Delete the ECS cluster (Clusters → Delete)
  3. Delete the task definition (Task definitions → Deregister)
  4. Delete the security group (EC2 → Security Groups → Delete)
  5. Delete the log group (CloudWatch → Log groups → Delete)
  6. Delete the IAM roles (IAM → Roles → Delete)