An enterprise-grade, highly available, cost-optimized microservices platform engineered on Amazon Web Services (AWS) using Terraform (Infrastructure as Code) and Docker containerization.
The platform implements a movie inventory management system and an asynchronous, decoupled billing processing engine. Public requests are ingested via an authenticated edge gateway, microservices communicate securely within private subnets via private DNS service discovery, sensitive credentials are managed with encryption at rest, and comprehensive observability is built into every layer.
- Architectural Overview
- System Architecture Diagram
- Microservices & Container Specifications
- Core Engineering Pillars
- Comprehensive 1-Month Cost Estimation
- Repository Layout
- Prerequisites
- Step-by-Step Deployment Guide
- Authentication & Cognito Setup
- API Reference & Functional Verification
- Billing & Queue Resilience Verification
- Operations, Monitoring & Troubleshooting
- Stakeholder Role-Play Q&A
- Teardown & Cost Elimination
The architecture implements modern cloud-native best practices to deliver a secure, scalable, and resilient distributed system:
- Edge Layer: Public traffic enters through AWS API Gateway (HTTP API v2) backed by an AWS Cognito JWT Authorizer. Unauthenticated requests are rejected at the edge with
401 Unauthorizedbefore reaching internal VPC resources. - Private Ingress: API Gateway communicates with an Internal Application Load Balancer (ALB) situated inside private subnets via an AWS VPC Link, eliminating public exposure of compute listeners.
- Compute Layer: Workloads run on Amazon Elastic Container Service (ECS) using the EC2 Launch Type backed by an Auto Scaling Group (ASG) across two Availability Zones. EC2 capacity is dynamically managed via an ECS Capacity Provider.
- Internal Communication: Microservices communicate privately using AWS Cloud Map (Private DNS Namespace) under
cloud-design.localusingawsvpcnetwork mode. - Asynchronous Decoupling: Billing transactions are published to RabbitMQ 4 with durable message queues. The
billing-appconsumes messages asynchronously, ensuring zero message loss even during consumer downtime. - Secrets Management: Passwords for PostgreSQL databases and RabbitMQ are securely stored in AWS Systems Manager (SSM) Parameter Store as
SecureStringtypes and decrypted at task runtime by the ECS Task Execution Role. - Unified Observability: Container stdout/stderr logs stream to Amazon CloudWatch Logs with a 7-day retention policy. A tailored CloudWatch Dashboard provides operational visibility into request throughput, latency percentiles (p50, p95, p99), target health, CPU/Memory utilization, and real-time error logs.
flowchart TB
subgraph Public_Internet[" Public Internet / Client Space "]
Client["Client / Postman"]
end
subgraph AWS_Cloud[" AWS Cloud Region (eu-west-2) "]
Cognito["Amazon Cognito User Pool<br/>(cloud-design-user-pool)"]
APIGW["API Gateway HTTP API<br/>(JWT Authorizer at Edge)"]
subgraph VPC[" VPC: cloud-design-vpc (10.0.0.0/16) "]
subgraph Public_Subnets[" Public Subnets (AZ-a & AZ-b) "]
IGW["Internet Gateway (IGW)"]
NAT["EC2 NAT Instance<br/>(fck-nat al2023 t3.micro)"]
end
subgraph Private_Subnets[" Private Subnets (AZ-a & AZ-b) "]
VPCLink["API Gateway VPC Link"]
ALB["Internal Application Load Balancer<br/>(:80 HTTP)"]
subgraph ECS_Cluster[" ECS Cluster: cloud-design-cluster "]
APIGW_App["api-gateway-app<br/>(Flask :3000)"]
Inventory_App["inventory-app<br/>(Flask :8080)"]
Billing_App["billing-app<br/>(Flask/Consumer :8080)"]
RabbitMQ["rabbit-queue<br/>(RabbitMQ 4 :5672/:15672)"]
Inventory_DB[("inventory-db<br/>(PostgreSQL 16 :5432)")]
Billing_DB[("billing-db<br/>(PostgreSQL 16 :5432)")]
end
end
end
subgraph Management_Services[" AWS Management & Observability "]
SSM["SSM Parameter Store<br/>(/app/* SecureString)"]
CW_Logs["CloudWatch Logs<br/>(/ecs/cloud-design)"]
CW_Dash["CloudWatch Dashboard<br/>(Performance & Health)"]
CloudMap["AWS Cloud Map<br/>(cloud-design.local)"]
end
end
%% Client and Auth Flows
Client -->|1. Auth Request| Cognito
Cognito -- "Returns JWT ID Token" --> Client
Client -->|2. HTTPS + Bearer JWT| APIGW
APIGW -.->|Validates Token| Cognito
%% Ingress Flow
APIGW -->|3. Forward via VPC Link| VPCLink
VPCLink --> ALB
ALB -->|4. Reverse Proxy :3000| APIGW_App
%% Synchronous & Asynchronous Microservice Flows
APIGW_App -->|5. HTTP /api/movies| CloudMap
CloudMap -.->|Resolves inventory-app-service| Inventory_App
Inventory_App -->|6. SQL TCP :5432| Inventory_DB
APIGW_App -->|7. AMQP Publish :5672| RabbitMQ
RabbitMQ -->|8. AMQP Consume| Billing_App
Billing_App -->|9. SQL TCP :5432| Billing_DB
%% Egress Flow
Private_Subnets -.->|Outbound Egress| NAT
NAT --> IGW
IGW -->|Internet Egress| Public_Internet
%% Management & Secrets
SSM -.->|Injects DB/Rabbit Passwords| ECS_Cluster
ECS_Cluster -.->|Streams Stdout/Stderr| CW_Logs
CW_Dash -.->|Visualizes Metrics & Errors| ECS_Cluster
| Container Name | Base Image | Container Port | Host Port | CPU Units | RAM | Protocol / Role |
|---|---|---|---|---|---|---|
api-gateway-app |
python:3.12-alpine |
3000 |
3000 |
600 | 600 MB | HTTP API Gateway, reverse proxy to inventory, publisher to RabbitMQ |
inventory-app |
python:3.12-alpine |
8080 |
8080 |
600 | 600 MB | CRUD REST API for movie catalogue, backed by PostgreSQL |
billing-app |
python:3.12-alpine |
8080 |
8080 |
600 | 600 MB | Asynchronous consumer for billing queue, backed by PostgreSQL |
rabbit-queue |
rabbitmq:4-management-alpine |
5672, 15672 |
5672, 15672 |
600 | 600 MB | Message broker (AMQP :5672, Management Web UI :15672) |
inventory-db |
postgres:16-alpine |
5432 |
5432 |
600 | 600 MB | Relational database storing movie inventory data |
billing-db |
postgres:16-alpine |
5432 |
5432 |
600 | 600 MB | Relational database storing billing and order records |
The solution implements a dual-layer elasticity model that dynamically adapts to traffic surges:
- ECS Service Auto-Scaling (Target Tracking):
- Configured via AWS Application Auto Scaling for stateless workloads (
api-gateway-appandinventory-app). - Policy: Target 80% average CPU utilization (
ECSServiceAverageCPUUtilization). - Scale-out cooldown: 60 seconds (rapid response to traffic bursts).
- Scale-in cooldown: 300 seconds (conservative spin-down preventing thrashing).
- Capacity range: Min 1 task, Max 2 tasks per service.
- Configured via AWS Application Auto Scaling for stateless workloads (
- EC2 Capacity Provider & Auto Scaling Group:
- The ECS Cluster uses a capacity provider bound to the EC2 Auto Scaling Group (
min_size = 3,desired_capacity = 3,max_size = 4). - Target capacity is configured at
90%, prompting the ASG to launch additional EC2 container instances before compute exhaustion occurs.
- The ECS Cluster uses a capacity provider bound to the EC2 Auto Scaling Group (
- Multi-AZ Network Architecture: Subnets are divided across two Availability Zones (
eu-west-2aandeu-west-2b). ECS tasks and EC2 host instances are distributed across both zones. - Asynchronous Decoupling: The billing pipeline utilizes RabbitMQ message persistence (
delivery_mode=2,durable=True). Ifbilling-appfails or is scaled to zero, billing requests continue to be accepted byapi-gateway-appand queued safely in RabbitMQ without dropping a single order. - Service Discovery: AWS Cloud Map uses multi-value routing with a 10-second DNS TTL, enabling zero-downtime task replacement and seamless internal name resolution (
cloud-design.local).
- Edge Authentication: AWS Cognito User Pool enforces strict password policies (minimum 8 characters, uppercase, lowercase, numbers, symbols). API Gateway validates incoming JWT tokens at the boundary, returning
401 Unauthorizedfor invalid or missing tokens. - Network Segmentation & Micro-Segmentation:
- No public IPs on private ECS hosts or container tasks.
alb-sg: Permits inbound HTTP port 80 only from the VPC CIDR (10.0.0.0/16).app-sg: Permits port 3000 only fromalb-sg, and port 8080 from other microservices sharingapp-sg.db-sg: Permits PostgreSQL port 5432 strictly from containers withapp-sg.rabbitmq-sg: Permits AMQP port 5672 strictly fromapp-sg, and management port 15672 only within the VPC CIDR.
- Least-Privilege IAM & Secret Encryption:
- Task Execution Role is scoped exclusively to read
/app/*paths in AWS SSM Parameter Store. - Passwords are encrypted at rest using AWS KMS via
SecureStringSSM parameters.
- Task Execution Role is scoped exclusively to read
- Secure Remote Administration: Remote access to private instances is managed via AWS Systems Manager (SSM) Session Manager, eliminating the need for public bastion hosts or open SSH port 22.
- Container Health Probing:
api-gateway-app,inventory-app,billing-app: Python HTTP check on/health(30s interval, 5s timeout, 3 retries).inventory-db,billing-db: PostgreSQL nativepg_isreadycheck (30s interval, 60s start period).rabbit-queue:rabbitmq-diagnostics -q pinghealth check (30s interval, 60s start period).
- ALB Health Checks: Evaluates
GET /healthon port 3000 every 30 seconds (healthy threshold = 3). - Centralized Logging: All containers stream logs to
/ecs/cloud-designin CloudWatch Logs with a 7-day retention window. - Custom Operations Dashboard (
cloud-design-performance-and-health):- Widget 1: API Gateway Request Throughput & Error Counts (4xx, 5xx).
- Widget 2: API Response Latency Percentiles (p50, p95, p99).
- Widget 3: ALB Healthy vs Unhealthy Target Container Counts.
- Widget 4 & 5: Aggregate Cluster CPU and Memory Utilization.
- Widget 6 & 7: Per-Microservice CPU and Memory Utilization metrics.
- Widget 8: CloudWatch Logs Insights Live Error and Exception Stream table.
- Modular Terraform Architecture: Clean separation into
networking,compute,security_identity, andservices. - Remote State Protection: S3 backend with AES-256 encryption and native S3 state locking (
use_lockfile = true). - Optimized Dockerfiles: Multi-stage/Alpine-based images ensuring fast build times and minimal attack surfaces.
The following estimate details continuous 24/7 operation over a full 1-month period (730 hours) in AWS Region eu-west-2 (London) under standard On-Demand pricing.
| Component | AWS Resource / Tier | Hourly / Unit Rate | Monthly Calculation | Estimated Monthly Cost |
|---|---|---|---|---|
| Virtual Private Cloud (VPC) | Subnets, Route Tables, Internet Gateway | $0.00 / hour | Included with AWS networking | $0.00 |
| NAT Instance Compute | 1 × EC2 t3.micro (fck-nat AMI) |
$0.0104 / hour | $0.0104 × 730 hours | $7.59 |
| NAT Instance Root Volume | 8 GB gp3 EBS Volume | $0.08 / GB-month | 8 GB × $0.08 | $0.64 |
| Public IPv4 Address | 1 × Public IPv4 (NAT Instance) | $0.005 / hour | $0.005 × 730 hours | $3.65 |
| ECS Container Instances | 3 × EC2 t3.small (On-Demand) |
$0.0208 / hour / instance | 3 × $0.0208 × 730 hours | $45.55 |
| ECS Hosts Storage | 3 × 30 GB gp3 EBS Volumes (AL2023) | $0.08 / GB-month | 90 GB × $0.08 | $7.20 |
| ECS Control Plane | Amazon ECS Service Scheduling | $0.00 / hour | Free with EC2 Launch Type | $0.00 |
| Application Load Balancer | 1 × Internal ALB Base Fee | $0.0225 / hour | $0.0225 × 730 hours | $16.43 |
| ALB Capacity Units (LCU) | Low traffic / Lab usage (< 1 LCU) | ~$0.008 / LCU-hour | Variable usage (~$0.001/hr avg) | $1.00 |
| API Gateway HTTP API (v2) | API Ingestion & Routing | $1.00 / million requests | Lab traffic (< 100k requests) | $0.10 |
| API Gateway VPC Link (v2) | VPC Link for HTTP APIs | $0.00 / hour | $0.00 (No hourly fee for v2 HTTP APIs) | $0.00 |
| AWS Cognito User Pool | User Authentication & JWTs | $0.00 | Free Tier covers up to 10,000 MAUs | $0.00 |
| AWS SSM Parameter Store | 3 × SecureString Parameters |
$0.00 | Standard parameters are free | $0.00 |
| AWS Cloud Map | Private DNS Namespace & Service Records | $0.00 | Private DNS records included in VPC | $0.00 |
| Application Auto Scaling | Target Tracking Scaling Service | $0.00 | Free management service | $0.00 |
| CloudWatch Logs Ingestion | Log storage with 7-day retention | $0.50 / GB ingested | Free Tier includes 5 GB / month | $0.20 |
| CloudWatch Custom Dashboard | 1 × Custom Monitoring Dashboard | $0.00 | Free Tier includes up to 3 dashboards | $0.00 |
| CloudWatch Logs Insights | Query processing for error logs | $0.005 / GB scanned | Lab volume (< 100 MB scanned) | $0.01 |
| Amazon S3 State Storage | Remote Terraform state bucket | $0.023 / GB-month | State file < 10 MB | < $0.01 |
| Data Transfer (Intra-VPC / Out) | Egress & Cross-AZ communication | Variable | Low volume lab usage | ~$0.60 |
================================================================================
LAYER ESTIMATED MONTHLY COST
================================================================================
1. Networking (VPC, Subnets, NAT EC2, Public IP, EBS) $11.88
2. Compute Capacity (3x t3.small EC2 Hosts + 90GB EBS) $52.75
3. Edge & Load Balancing (Internal ALB + LCU + HTTP API) $17.53
4. Identity, Secrets & Discovery (Cognito, SSM, Cloud Map) $0.00
5. Observability & State Storage (CloudWatch, S3 State) $0.21
6. Data Transfer & Inter-AZ Overhead $0.60
================================================================================
TOTAL ESTIMATED 1-MONTH RUNTIME (ON-DEMAND): ~$82.97 / month
(~ $2.76 / day)
================================================================================
fck-natLightweight NAT Instance vs AWS NAT Gateway: Replacing an AWS-managed NAT Gateway ($32.85/mo + $0.045/GB data processing) with at3.microfck-natinstance ($7.59/mo) reduces monthly baseline networking costs by over 70%.- HTTP API (v2) vs REST API (v1): Utilizing HTTP APIs ($1.00/million requests and $0.00 VPC Link hourly charge) rather than REST APIs ($3.50/million + $0.07/hour VPC Link charge = ~$51/mo) saves over $50 per month.
- Log Retention Limits: A 7-day retention policy on
/ecs/cloud-designprevents unbounded CloudWatch storage growth. - Standard SSM & Free Tier Cognito: Maximizes native AWS free tier benefits (Cognito up to 10,000 monthly active users and standard SSM parameters).
.
├── dockerfiles/
│ ├── api-gateway-app/ # Edge application (Flask :3000)
│ │ ├── app/
│ │ │ ├── __init__.py # Flask app factory, logging & /health
│ │ │ ├── billing_routes.py # /api/billing -> RabbitMQ publisher
│ │ │ ├── config.py # Environment configuration loader
│ │ │ └── inventory_routes.py # /api/movies -> inventory-app reverse proxy
│ │ ├── Dockerfile # Python 3.12 Alpine container
│ │ ├── requirements.txt # Flask, requests, pika, python-dotenv
│ │ └── server.py # Application entry point
│ ├── inventory-app/ # Movie CRUD service (Flask :8080)
│ │ ├── app/
│ │ │ ├── __init__.py # App factory, DB init & /health
│ │ │ ├── config.py # PostgreSQL database connection URI
│ │ │ ├── database.py # SQLAlchemy Model (Movie)
│ │ │ ├── movie_repository.py # Database CRUD operations
│ │ │ └── movie_routes.py # /api/movies REST endpoints
│ │ ├── Dockerfile # Python 3.12 Alpine container
│ │ ├── requirements.txt # Flask, SQLAlchemy, psycopg2-binary
│ │ └── server.py # Service entry point
│ └── billing-app/ # Asynchronous billing service (Flask :8080)
│ ├── app/
│ │ ├── __init__.py # App factory, DB init & /health
│ │ ├── billing_consumer.py # RabbitMQ background worker thread
│ │ ├── config.py # RabbitMQ & PostgreSQL credentials
│ │ └── database.py # SQLAlchemy Model (Order)
│ ├── Dockerfile # Python 3.12 Alpine container
│ ├── requirements.txt # Flask, SQLAlchemy, pika, psycopg2-binary
│ └── server.py # Consumer thread launcher & web server
├── docs/
│ ├── compute-foundation.md # Guide to ECS, ASG, Launch Templates & Capacity Providers
│ ├── edge-security-and-observability-foundation.md # Guide to ALB, API GW, Cognito & CloudWatch
│ ├── infra-and-networking-foundation.md # Guide to VPC, Subnets, Routes & NAT
│ ├── services-and-discovery-foundation.md # Guide to ECS Tasks, Services & Cloud Map
│ └── subject/ # Original project subject & audit guidelines
│ ├── cloud-design.md
│ └── cloud-design-audit.md
├── terraform/
│ ├── aws_init.sh # Automated helper: registers test user & gets JWT
│ ├── backend.tf # S3 remote state configuration with locking
│ ├── main.tf # Root module connecting all infrastructure layers
│ ├── outputs.tf # Root Terraform outputs (endpoints, URLs, IDs)
│ ├── provider.tf # AWS provider configuration
│ ├── terraform.tfvars.example # Sample input variables file
│ ├── variables.tf # Root variable declarations
│ └── modules/
│ ├── compute/ # ECS Cluster, Launch Template, ASG, Capacity Provider
│ ├── networking/ # VPC, Subnets, Internet Gateway, Routes, fck-nat
│ ├── security_identity/ # IAM Roles, Cognito User Pool/Client, SSM, ALB
│ └── services/ # ECS Task Definitions, Services, Cloud Map, Dashboard
├── CRUD-master.postman_collection.json# Postman test collection
└── README.md # Comprehensive project documentation
Ensure the following tools are installed and configured on your management machine:
- Terraform
>= 1.15.8(Installation Guide) - AWS CLI v2 (Installation Guide)
- Docker Engine & Docker Hub Account (Installation Guide)
- AWS Session Manager CLI Plugin (Installation Guide)
- Postman or cURL for API validation.
aws configure
aws sts get-caller-identityEnsure your IAM identity has administrative permissions to provision networking, compute, IAM, and security resources in region eu-west-2.
Before initializing Terraform, ensure the remote state S3 bucket exists:
aws s3api create-bucket \
--bucket cloud-design-remote-state \
--region eu-west-2 \
--create-bucket-configuration LocationConstraint=eu-west-2
aws s3api put-bucket-versioning \
--bucket cloud-design-remote-state \
--versioning-configuration Status=Enabled
aws s3api put-bucket-encryption \
--bucket cloud-design-remote-state \
--server-side-encryption-configuration '{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"}}]}'
aws s3api put-public-access-block \
--bucket cloud-design-remote-state \
--public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=trueCreate your terraform/terraform.tfvars from the example template:
cp terraform/terraform.tfvars.example terraform/terraform.tfvarsEdit terraform/terraform.tfvars and provide your specific credentials:
region = "eu-west-2"
vpc_cidr = "10.0.0.0/16"
public_subnet_cidrs = ["10.0.10.0/24", "10.0.11.0/24"]
private_subnet_cidrs = ["10.0.20.0/24", "10.0.21.0/24"]
azs = ["eu-west-2a", "eu-west-2b"]
project_name = "cloud-design"
dockerhub_username = "your-dockerhub-username"
billing_db_password = "StrongBillingPassword123!"
inventory_db_password = "StrongInventoryPassword123!"
rabbitmq_password = "StrongRabbitPassword123!"Log in to Docker Hub, build all three microservices, and push them to your registry:
docker login
DOCKERHUB_USERNAME="your-dockerhub-username"
docker build -t "$DOCKERHUB_USERNAME/api-gateway-app:latest" dockerfiles/api-gateway-app
docker build -t "$DOCKERHUB_USERNAME/inventory-app:latest" dockerfiles/inventory-app
docker build -t "$DOCKERHUB_USERNAME/billing-app:latest" dockerfiles/billing-app
docker push "$DOCKERHUB_USERNAME/api-gateway-app:latest"
docker push "$DOCKERHUB_USERNAME/inventory-app:latest"
docker push "$DOCKERHUB_USERNAME/billing-app:latest"Execute Terraform from the repository root using -chdir=terraform:
terraform -chdir=terraform init
terraform -chdir=terraform fmt -check -recursive
terraform -chdir=terraform validate
terraform -chdir=terraform plan
terraform -chdir=terraform applyUpon successful completion, Terraform outputs all vital endpoints and identifiers:
terraform -chdir=terraform outputaws ecs describe-services \
--region eu-west-2 \
--cluster cloud-design-cluster \
--services \
cloud-design-api-gateway-app \
cloud-design-inventory-app \
cloud-design-billing-app \
cloud-design-inventory-db \
cloud-design-billing-db \
cloud-design-rabbit-queue \
--query 'services[*].{Service:serviceName,Status:status,Desired:desiredCount,Running:runningCount}' \
--output tableAll routes exposed through API Gateway are secured by a Cognito JWT Authorizer.
Run the included initialization script to create a verified test user and extract a JWT token:
chmod +x terraform/aws_init.sh
./terraform/aws_init.sh-
Export Variables:
API_GATEWAY_URL=$(terraform -chdir=terraform output -raw api_gateway_url) USER_POOL_ID=$(terraform -chdir=terraform output -raw user_pool_id) CLIENT_ID=$(terraform -chdir=terraform output -raw user_pool_client_id) USER_EMAIL="admin@cloud.design" USER_PASS="CloudPass1234*!"
-
Register & Confirm User:
aws cognito-idp sign-up \ --region eu-west-2 \ --client-id "$CLIENT_ID" \ --username "$USER_EMAIL" \ --password "$USER_PASS" \ --user-attributes Name=email,Value="$USER_EMAIL" aws cognito-idp admin-confirm-sign-up \ --region eu-west-2 \ --user-pool-id "$USER_POOL_ID" \ --username "$USER_EMAIL"
-
Authenticate to Obtain JWT ID Token:
JWT_TOKEN=$(aws cognito-idp initiate-auth \ --region eu-west-2 \ --auth-flow USER_PASSWORD_AUTH \ --client-id "$CLIENT_ID" \ --auth-parameters USERNAME="$USER_EMAIL",PASSWORD="$USER_PASS" \ --query 'AuthenticationResult.IdToken' \ --output text) echo "Generated JWT Token: $JWT_TOKEN"
Verify that requests without a valid Bearer token are rejected at the edge:
curl -i "$API_GATEWAY_URL/api/movies"Expected Response:
HTTP/2 401
content-type: application/json
{"message":"Unauthorized"}curl -i -X POST "$API_GATEWAY_URL/api/movies" \
-H "Authorization: Bearer $JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title": "Inception", "description": "A thief who steals corporate secrets through the use of dream-sharing technology."}'Expected Response (HTTP 201 Created):
{
"id": 1,
"title": "Inception",
"description": "A thief who steals corporate secrets through the use of dream-sharing technology."
}curl -i -X POST "$API_GATEWAY_URL/api/movies" \
-H "Authorization: Bearer $JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title": "Interstellar", "description": "A team of explorers travel through a wormhole in space in an attempt to ensure humanity survival."}'curl -i "$API_GATEWAY_URL/api/movies" \
-H "Authorization: Bearer $JWT_TOKEN"curl -i "$API_GATEWAY_URL/api/movies?title=inter" \
-H "Authorization: Bearer $JWT_TOKEN"curl -i "$API_GATEWAY_URL/api/movies/1" \
-H "Authorization: Bearer $JWT_TOKEN"curl -i -X PUT "$API_GATEWAY_URL/api/movies/1" \
-H "Authorization: Bearer $JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title": "Inception (Director Cut)", "description": "Updated description with extended cut details."}'curl -i -X DELETE "$API_GATEWAY_URL/api/movies/1" \
-H "Authorization: Bearer $JWT_TOKEN"curl -i -X DELETE "$API_GATEWAY_URL/api/movies" \
-H "Authorization: Bearer $JWT_TOKEN"This test demonstrates the asynchronous fault-tolerance of the messaging queue. The API Gateway safely accepts and enqueues billing requests even when the backend consumer service is down.
aws ecs update-service \
--region eu-west-2 \
--cluster cloud-design-cluster \
--service cloud-design-billing-app \
--desired-count 0Verify that no billing tasks are running:
aws ecs list-tasks \
--region eu-west-2 \
--cluster cloud-design-cluster \
--service-name cloud-design-billing-appSend a billing order while the consumer is completely offline:
curl -i -X POST "$API_GATEWAY_URL/api/billing" \
-H "Authorization: Bearer $JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"user_id": 42, "number_of_items": 3, "total_amount": 150}'Expected Response (HTTP 200 OK):
{
"message": "Message posted"
}The request succeeds because api-gateway-app successfully enqueues the durable message into RabbitMQ.
Scale the service back to 1 desired task:
aws ecs update-service \
--region eu-west-2 \
--cluster cloud-design-cluster \
--service-name cloud-design-billing-app \
--desired-count 1Tail the CloudWatch logs to verify that the consumer processed the queued order upon starting:
aws logs tail /ecs/cloud-design \
--region eu-west-2 \
--log-stream-name-prefix billing-app \
--followConnect to private ECS EC2 container instances without opening inbound firewall ports:
INSTANCE_ID=$(aws ec2 describe-instances \
--region eu-west-2 \
--filters 'Name=instance-state-name,Values=running' 'Name=tag:Name,Values=cloud-design-ecs-host' \
--query 'Reservations[0].Instances[0].InstanceId' \
--output text)
aws ssm start-session --region eu-west-2 --target "$INSTANCE_ID"Once inside the host session, inspect containers and interact with PostgreSQL:
# View running containers on host
sudo docker ps
# Connect to Inventory Database directly
INVENTORY_CONTAINER=$(sudo docker ps -qf "name=inventory-db")
sudo docker exec -it "$INVENTORY_CONTAINER" psql -U inventory_user -d inventory_db -c "SELECT * FROM movies;"
# Connect to Billing Database directly
BILLING_CONTAINER=$(sudo docker ps -qf "name=billing-db")
sudo docker exec -it "$BILLING_CONTAINER" psql -U billing_user -d billing_db -c "SELECT * FROM orders;"# Stream all container logs simultaneously
aws logs tail /ecs/cloud-design --region eu-west-2 --follow
# Stream only API Gateway container logs
aws logs tail /ecs/cloud-design --region eu-west-2 --log-stream-name-prefix api-gateway --follow
# Stream only Inventory service logs
aws logs tail /ecs/cloud-design --region eu-west-2 --log-stream-name-prefix inventory-app --followAfter publishing updated container images to Docker Hub:
aws ecs update-service \
--region eu-west-2 \
--cluster cloud-design-cluster \
--service cloud-design-api-gateway-app \
--force-new-deploymentRotate credentials in SSM without redeploying Terraform infrastructure:
aws ssm put-parameter \
--region eu-west-2 \
--name /app/inventory_db/password \
--value "NewSuperSecurePassword999!" \
--type SecureString \
--overwrite