This project demonstrates a microservices architecture using Docker Compose for local development and AWS CDK for deployment to ECS. It showcases a complete end-to-end solution with frontend, backend, message queue, and reverse proxy components.
The application implements a modern microservices architecture with the following components:
- Node.js Backend: Express API with RabbitMQ integration for message processing
- React Frontend: Single-page application that communicates with the backend API
- RabbitMQ: Message queue for asynchronous communication between services
- Nginx: Reverse proxy for routing requests to appropriate services
The local development environment uses Docker Compose to orchestrate all services:
┌───────────────────────────────────────────────────────────────────────────┐
│ Docker Compose │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌──────────┐ │
│ │ Nginx │ │ Frontend │ │ Backend │ │ RabbitMQ │ │
│ │ Proxy │ │ React │ │ Node.js │ │ │ │
│ │ Container │ │ Container │ │ Container │ │ Container│ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └────┬─────┘ │
│ │ │ │ │ │
│ │ │ │ │ │
│ │ ┌───────────────┐│ │ │ │
│ └──┤ / ├┘ │ │ │
│ └───────────────┘ │ │ │
│ │ │ │
│ ┌───────────────┐ │ │ │
│ ┌──┤ /api ├────────────────────┘ │ │
│ │ └───────────────┘ │ │
│ │ │ │
│ │ ┌───────────────┐ │ │
│ └──┤ /rabbitmq ├──────────────────────────────────────┘ │
│ └───────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────────┘
▲
│
│
│
▼
┌──────────┐
│ Client │
│ Browser │
└──────────┘
In this setup:
- Nginx serves as the entry point, routing traffic to appropriate services
- Frontend container serves the React application
- Backend container runs the Node.js API
- RabbitMQ container provides message queuing capabilities
- All services communicate over a Docker network
For production, the application is deployed to AWS ECS using CDK:
┌───────────────────────────────────────────────────────────────────────────────────────────────────┐
│ AWS Cloud │
│ │
│ ┌────────────────────────────────────────────────────────────────────────────────────────────┐ │
│ │ VPC │ │
│ │ │ │
│ │ ┌───────────────────┐ │ │
│ │ │ │ │ │
│ │ │ Application │ │ │
│ │ │ Load Balancer │ │ │
│ │ │ │ │ │
│ │ └─────────┬─────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ ECS Cluster │ │ │
│ │ │ │ │ │
│ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │
│ │ │ │ Fargate Service │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ │
│ │ │ │ │ Task Definition │ │ │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ │ │
│ │ │ │ │ │ Nginx │ │Frontend │ │ Backend │ │RabbitMQ │ │ │ │ │ │
│ │ │ │ │ │Container│ │Container│ │Container│ │Container│ │ │ │ │ │
│ │ │ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ ECR Repositories │ │ │
│ │ │ │ │ │
│ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │
│ │ │ │ Backend │ │ Frontend │ │ Nginx │ │ │ │
│ │ │ │ Repository │ │ Repository │ │ Repository │ │ │ │
│ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────────────────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────────────────────────────────┘
In this AWS deployment:
- Application Load Balancer distributes traffic to the ECS service
- ECS Cluster with Fargate launch type runs the containerized application
- Task Definition defines all containers (Nginx, Frontend, Backend, RabbitMQ)
- ECR Repositories store the Docker images
- All components run within a VPC with appropriate security groups
- Docker and Docker Compose
- Node.js and npm
- Clone the repository
- Start the application using Docker Compose:
docker-compose up --build- Access the application at http://localhost:80
- AWS CLI configured with appropriate credentials
- Node.js and npm
- AWS CDK installed globally (
npm install -g aws-cdk)
You can test the AWS deployment locally using LocalStack, which emulates AWS services:
-
Prerequisites for LocalStack:
- Docker and Docker Compose installed
- AWS CLI installed
-
Start LocalStack and Deploy:
chmod +x local-deploy.sh ./local-deploy.sh
-
Access the Application:
- The application will be available at http://localhost:8080
- LocalStack is available at http://localhost:4566
-
What's Being Emulated:
- CloudWatch: For logs and monitoring
- IAM: For roles and permissions
- EC2/VPC: For networking
- ELB: For load balancing
- Note: ECR services are not fully supported in LocalStack Community Edition (see Known Issues section)
-
Benefits of LocalStack Testing:
- Test AWS deployments without incurring AWS costs
- Faster development cycles
- No need for internet connectivity
- Consistent testing environment
The following issues have been identified when working with LocalStack, and fixes have been implemented in the project:
-
ECR Not Supported in LocalStack Community Edition:
Issue: The ECR API is not fully implemented in the free version of LocalStack.
Fix: The deployment script has been modified to skip ECR repository creation and instead use local Docker images. The CDK stack has been updated to handle the absence of ECR by using mock repositories when LocalStack is detected.
-
AWS Account Resolution Failure in CDK:
Issue: CDK deployment fails with "Unable to resolve AWS account to use" error.
Fix: The CDK entry point (cdk.ts) now explicitly sets a default account ID ('000000000000') when one isn't provided through environment variables. The local-deploy.sh script also sets the necessary environment variables:
export CDK_DEFAULT_ACCOUNT=000000000000 export CDK_DEFAULT_REGION=us-east-1
-
Node.js Version Compatibility:
Issue: CDK may display warnings about compatibility with newer Node.js versions.
Fix: The package.json has been updated to work with the latest Node.js versions, and dependencies have been upgraded to newer, compatible versions.
-
Docker Compose Command Format:
Issue: Newer Docker versions use
docker composeinstead ofdocker-compose.Fix: All scripts have been updated to use the newer
docker composeformat (without the hyphen).
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Docker Compose Environment │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────┐ │ │
│ │ │ Nginx │ │ Frontend │ │ Backend │ │RabbitMQ │ │ │
│ │ │ Proxy │ │ React │ │ Node.js │ │ │ │ │
│ │ │ Container │ │ Container │ │ Container │ │Container│ │ │
│ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └────┬────┘ │ │
│ │ │ │ │ │ │ │
│ │ └───────────────────┴───────────────────┴─────────────────┘ │ │
│ │ │ │ │
│ │ │ Application Services │ │
│ │ │ │ │
│ └─────────────────────────────────────┼───────────────────────────────────────────────────────┘ │
│ │ │
│ │ │
│ ┌─────────────────────────────────────┼───────────────────────────────────────────────────────┐ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ LocalStack │ │ │
│ │ │ │ │ │
│ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐│ │ │
│ │ │ │ ECR │ │ ECS │ │CloudWatch │ │ IAM │ │ EC2 / VPC ││ │ │
│ │ │ │ Emulation │ │ Emulation │ │ Emulation │ │ Emulation │ │ Emulation ││ │ │
│ │ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘│ │ │
│ │ │ │ │ │
│ │ │ ┌─────────────┐ ┌──────────────┐ ┌─────────────┐ │ │ │
│ │ │ │ ELB │ │CloudFormation│ │ S3/Other │ │ │ │
│ │ │ │ Emulation │ │ Emulation │ │ Services │ │ │ │
│ │ │ └─────────────┘ └──────────────┘ └─────────────┘ │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ AWS Services Emulation │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
In this setup:
- Application Services: The same Docker containers used for local development
- LocalStack: Emulates all required AWS services locally
- AWS CDK: Deploys to LocalStack instead of real AWS
- Unified Environment: Everything runs in a single Docker Compose network
The CDK deployment creates the following AWS resources:
-
VPC with Public and Private Subnets
- Public subnets for the load balancer
- Private subnets for the ECS tasks
- NAT Gateway for outbound internet access from private subnets
-
ECS Cluster
- Fargate launch type (serverless containers)
- Task definitions for all services
- Service auto-scaling capabilities
-
Application Load Balancer
- Routes traffic to the ECS service
- HTTP listener on port 80
- Target group for the Nginx container
-
ECR Repositories
- Stores Docker images for backend, frontend, and Nginx
- Versioned image management
-
IAM Roles and Policies
- Task execution role for pulling images and logging
- Task role for application permissions
-
CloudWatch Logs
- Log groups for each container
- Centralized logging and monitoring
- Build and push Docker images to ECR:
chmod +x build-and-push.sh
./build-and-push.sh- Deploy the infrastructure using CDK:
chmod +x deploy.sh
./deploy.sh- After deployment, the CDK will output the load balancer DNS name. Use this to access your application.
The AWS deployment provides several advantages:
- Auto Scaling: The ECS service can scale based on CPU and memory utilization
- High Availability: Tasks are distributed across multiple Availability Zones
- Managed Services: AWS manages the underlying infrastructure
- Security: Resources are protected by security groups and IAM policies
- Monitoring: CloudWatch provides metrics and logs for all components
lab8/
├── backend/ # Node.js Express API
│ ├── Dockerfile
│ ├── package.json
│ └── server.js
├── frontend/ # React application
│ ├── Dockerfile
│ ├── package.json
│ ├── public/
│ └── src/
├── nginx/ # Nginx reverse proxy
│ ├── Dockerfile
│ └── nginx.conf
├── cdk/ # AWS CDK deployment code
│ ├── bin/
│ ├── lib/
│ ├── package.json
│ └── cdk.json
├── docker-compose.yml # Local development configuration
├── build-and-push.sh # Script to build and push Docker images to ECR
├── deploy.sh # Script to deploy using CDK
└── README.md
- Client Request: Users access the application through their browser, sending HTTP requests to the Nginx proxy
- Nginx Routing:
- Requests to
/are routed to the frontend React application - Requests to
/api/*are routed to the backend Node.js API - Requests to
/rabbitmqare routed to the RabbitMQ management interface
- Requests to
- Frontend Processing:
- The React application renders the UI
- Makes API calls to the backend for data
- Updates the UI based on responses
- Backend Processing:
- Receives API requests from the frontend
- Processes business logic
- Communicates with RabbitMQ for asynchronous operations
- Returns responses to the frontend
- Message Queue Processing:
- Backend publishes messages to RabbitMQ queues
- Messages are consumed asynchronously
- Provides reliability and decoupling between services
┌──────────┐ ┌─────────────────────────────────────────────────────────────┐
│ │ │ │
│ Client │◄────┤ Frontend (React) │
│ Browser │ │ │
│ │ └───────────────────────────┬─────────────────────────────────┘
└────┬─────┘ │
│ │ HTTP/JSON
│ HTTP │
│ ▼
│ ┌─────────────────────────┐
└─────────────────────────►│ │
│ Backend (Node.js) │
│ │
└────────────┬────────────┘
│
│ AMQP
│
▼
┌─────────────────────────┐
│ │
│ RabbitMQ │
│ │
└─────────────────────────┘
- User Interaction: Users interact with the frontend application, triggering API calls
- API Communication: The frontend communicates with the backend via RESTful API calls
- Message Publishing: The backend publishes messages to RabbitMQ when asynchronous processing is needed
- Message Consumption: Messages are consumed from RabbitMQ queues for processing
To remove the AWS resources, run:
cd cdk
cdk destroyWhen you're done with the Lab8 environment, you can clean up all resources with the following commands:
For convenience, a cleanup script is provided that automates the entire cleanup process:
# Make the script executable (if not already)
chmod +x cleanup.sh
# Run the cleanup script
./cleanup.shThis script will:
- Clean up all LocalStack resources
- Stop and remove all Docker containers, networks, and volumes
- Remove Docker images created for the project
- Verify that all resources have been removed properly
If you prefer to clean up manually, follow these steps:
To stop and remove all Docker containers, networks, and volumes created by Docker Compose:
# Navigate to the lab8 directory
cd lab8
# Stop all containers and remove resources
docker compose down --volumes --remove-orphansThis will:
- Stop all running containers
- Remove all stopped containers
- Remove all networks created by Docker Compose
- Remove all volumes defined in docker-compose.yml
- Remove any orphaned containers
If you want to also remove the Docker images created for this project:
# Remove the project images
docker rmi lab8-backend lab8-frontend lab8-nginx
docker rmi localhost:4566/lab8-backend:latest localhost:4566/lab8-frontend:latest localhost:4566/lab8-nginx:latestLocalStack resources are automatically removed when you run docker compose down --volumes. However, if you want to clean up specific LocalStack resources:
# Set LocalStack environment variables
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
export AWS_DEFAULT_REGION=us-east-1
# Delete ECR repositories
aws --endpoint-url=http://localhost:4566 ecr delete-repository --repository-name lab8-backend --force || true
aws --endpoint-url=http://localhost:4566 ecr delete-repository --repository-name lab8-frontend --force || true
aws --endpoint-url=http://localhost:4566 ecr delete-repository --repository-name lab8-nginx --force || true
# Delete CloudFormation stack (if it was created)
aws --endpoint-url=http://localhost:4566 cloudformation delete-stack --stack-name Lab8Stack || trueTo verify that all resources have been cleaned up:
# Check for any running containers related to lab8
docker ps -a | grep lab8
# Check if any networks or volumes remain
docker network ls | grep lab8
docker volume ls | grep lab8When running the LocalStack example, you might encounter the following issues:
Problem: The docker-compose command is deprecated in newer Docker versions.
Solution: Use docker compose (without the hyphen) instead. Update the local-deploy.sh script:
# Change this line:
docker-compose up -d
# To this:
docker compose up -dProblem: The nginx service is configured to use port 80, which might be already in use on your system.
Solution: Modify the port mapping in docker-compose.yml:
nginx:
# ...
ports:
- "8080:80" # Change from "80:80"Also update the output message in local-deploy.sh:
echo "You can access the application at: http://localhost:8080"Problem: The LocalStack container fails with "Device or resource busy" error when trying to use the mounted volume.
Solution: Remove the volume configuration from the LocalStack service in docker-compose.yml:
localstack:
# ...
environment:
# Remove this line:
# - DATA_DIR=/tmp/localstack/data
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
# Remove this line:
# - "localstack_data:/tmp/localstack/data"Also remove localstack_data: from the volumes section at the bottom of the file.
Problem: Creating ECR repositories fails with an error indicating the API is not implemented.
Note: The free version of LocalStack doesn't support ECR services. For full AWS service emulation including ECR, consider using LocalStack Pro or deploying to an actual AWS environment.
Problem: The CDK deployment requires Node.js version 14.15.0 or newer.
Solution: Update your Node.js installation to a compatible version (18.x is recommended) before running the CDK deployment:
# Check your current Node.js version
node -v
# If needed, update to a newer version using nvm or your system's package managerProblem: The messaging functionality (WebSocket queue) fails because of a mismatch between the backend API endpoints and NGINX URL rewriting.
Explanation: The NGINX configuration rewrites URLs from /api/messages to /messages, but the backend is expecting /api/messages.
Solution: Update the endpoint paths in the backend (server.js) to match what NGINX is sending after the rewrite:
// Change this:
app.post('/api/messages', async (req, res) => { ... });
app.get('/api/messages', async (req, res) => { ... });
// To this:
app.post('/messages', async (req, res) => { ... });
app.get('/messages', async (req, res) => { ... });After making this change, rebuild and restart the backend container:
docker compose build backend
docker compose up -d backend