Skip to content

sbip-sg/ecs-microservices-test

Repository files navigation

ECS Microservices Multi-Environment Deployment

A Java Spring Boot microservices project with Service A calling Service B, deployed on AWS ECS Fargate with multi-environment support using Terraform and GitHub Actions.

Table of Contents

Project Architecture

┌─────────────┐    HTTP Call    ┌─────────────┐
│   Service A │ ───────────────► │   Service B │
│  (Port 8080) │                 │  (Port 8081) │
└─────────────┘                 └─────────────┘
       │                               │
       └───────────────┬───────────────┘
                       │
                ┌─────────────┐
                │     ALB     │
                │  (Port 80)  │
                └─────────────┘
                       │
                ┌─────────────┐
                │   Internet  │
                │   Gateway   │
                └─────────────┘

Service Flow:

  • Service A exposes /call-service-b endpoint
  • Service A calls Service B's /hello endpoint internally
  • Both services are deployed on ECS Fargate
  • Application Load Balancer distributes traffic
  • Services communicate via internal network

Network Structure

VPC Configuration

VPC CIDR: 10.0.0.0/16

├── Public Subnets (for ALB)
│   ├── 10.0.1.0/24 (AZ-a)
│   └── 10.0.2.0/24 (AZ-b)
│
├── Private Subnets (for ECS Tasks)
│   ├── 10.0.11.0/24 (AZ-a)
│   └── 10.0.12.0/24 (AZ-b)
│
├── Internet Gateway
├── NAT Gateways (for outbound traffic)
└── Security Groups
    ├── ALB Security Group (80, 443)
    ├── Service A Security Group (8080)
    └── Service B Security Group (8081)

Environment-Specific CIDR (if needed)

  • Staging: 10.0.0.0/16
  • Production: 10.1.0.0/16

Project Structure

ecs-microservices-test/
├── README.md                       # This file
├── MULTI_ENVIRONMENT.md            # Multi-environment guide
├── TERRAFORM_LEARNING.md           # Terraform learning resources
├── docker-compose.yml              # Local development
├── build.sh                        # Build script
├── deploy-env.sh                   # Environment deployment script
│
├── service-a/                      # Service A (Java Spring Boot)
│   ├── Dockerfile
│   ├── pom.xml
│   └── src/main/java/com/example/servicea/
│       ├── ServiceAApplication.java
│       └── controller/MainController.java
│
├── service-b/                      # Service B (Java Spring Boot)
│   ├── Dockerfile
│   ├── pom.xml
│   └── src/main/java/com/example/serviceb/
│       ├── ServiceBApplication.java
│       └── controller/HelloController.java
│
├── terraform/                      # Infrastructure as Code
│   ├── variables.tf                # Variable definitions
│   ├── vpc.tf                      # VPC, subnets, gateways
│   ├── ecs.tf                      # ECS cluster, services
│   ├── load_balancer.tf            # ALB configuration
│   ├── task_definitions.tf         # ECS task definitions
│   ├── outputs.tf                  # Output values
│   └── environments/               # Environment-specific configs
│       ├── staging.tfvars
│       └── production.tfvars
│
└── .github/workflows/              # CI/CD Pipeline
    ├── staging-deploy.yml
    └── production-deploy.yml

Prerequisites

Required Tools

# Install required tools
sudo apt-get update
sudo apt-get install -y docker.io maven openjdk-21-jdk

# Install Terraform
wget https://releases.hashicorp.com/terraform/1.6.0/terraform_1.6.0_linux_amd64.zip
unzip terraform_1.6.0_linux_amd64.zip
sudo mv terraform /usr/local/bin/

# Install AWS CLI
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

AWS Setup

# Configure AWS CLI
aws configure
# Enter your AWS Access Key ID, Secret Access Key, and region

Required IAM Permissions

The GitHub Actions workflow requires an IAM role with the following permissions:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecr:GetAuthorizationToken",
                "ecr:BatchCheckLayerAvailability",
                "ecr:GetDownloadUrlForLayer",
                "ecr:BatchGetImage",
                "ecr:DescribeRepositories",
                "ecr:CreateRepository",
                "ecr:PutImage",
                "ecr:InitiateLayerUpload",
                "ecr:UploadLayerPart",
                "ecr:CompleteLayerUpload"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ecs:*",
                "ec2:*",
                "elasticloadbalancing:*",
                "logs:*",
                "iam:PassRole",
                "iam:CreateRole",
                "iam:DeleteRole",
                "iam:AttachRolePolicy",
                "iam:DetachRolePolicy",
                "iam:GetRole",
                "iam:ListAttachedRolePolicies"
            ],
            "Resource": "*"
        }
    ]
}

GitHub Secrets Required

AWS_REGION=ap-southeast-1
AWS_ROLE_ARN=arn:aws:iam::YOUR_ACCOUNT:role/GitHubActionsRole

Multi-Environment Setup

Environment Configuration Files

Create environment-specific variable files:

terraform/environments/staging.tfvars

environment = "staging"
aws_region = "ap-southeast-1"
ecs_task_cpu = "256"
ecs_task_memory = "512"
ecs_desired_count = 1
enable_autoscaling = false
log_retention_days = 7
alb_deletion_protection = false
vpc_cidr = "10.0.0.0/16"

terraform/environments/production.tfvars

environment = "production"
aws_region = "ap-southeast-1"
ecs_task_cpu = "512"
ecs_task_memory = "1024"
ecs_desired_count = 2
enable_autoscaling = true
log_retention_days = 30
alb_deletion_protection = true
vpc_cidr = "10.1.0.0/16"

Local Development

Build and Test Locally

# Build both services
./build.sh

# Run locally with Docker Compose
docker-compose up -d

# Test the services
curl "http://localhost:8080/service-a/api/health"
curl "http://localhost:8080/service-a/api/greeting?name=John"
curl "http://localhost:8080/service-a/api/status"
curl "http://localhost:8081/service-b/api/health"
curl "http://localhost:8081/service-b/api/hello?name=World"

# Stop local services
docker-compose down

Individual Service Testing

# Build Service A
cd service-a
mvn clean package
java -jar target/service-a-0.0.1-SNAPSHOT.jar

# Build Service B
cd service-b
mvn clean package
java -jar target/service-b-0.0.1-SNAPSHOT.jar

Deployment

Manual Deployment

Deploy to Staging

cd terraform

# Initialize Terraform
terraform init

# Plan staging deployment
terraform plan -var-file="environments/staging.tfvars"

# Apply staging deployment
terraform apply -var-file="environments/staging.tfvars"

# Get ALB URL
terraform output alb_dns_name

Deploy to Production

cd terraform

# Plan production deployment
terraform plan -var-file="environments/production.tfvars"

# Apply production deployment
terraform apply -var-file="environments/production.tfvars"

# Get ALB URL
terraform output alb_dns_name

Environment-Specific Deployment Script

# Deploy to staging
./deploy-env.sh staging

# Deploy to production
./deploy-env.sh production

GitHub Actions Deployment

Push to respective branches to trigger deployment:

  • Push to staging branch → deploys to staging environment
  • Push to main branch → deploys to production environment

Accessing the Application

After deployment, get the ALB URL:

cd terraform
terraform output alb_dns_name

Test the endpoints:

# Service A greeting endpoint (calls Service B internally)
curl "http://YOUR_ALB_URL/service-a/api/greeting?name=John"

# Service A health check
curl http://YOUR_ALB_URL/service-a/api/health

# Service A status (checks both services)
curl http://YOUR_ALB_URL/service-a/api/status

# Service B hello endpoint directly  
curl "http://YOUR_ALB_URL/service-b/api/hello?name=World"

# Service B health check
curl http://YOUR_ALB_URL/service-b/api/health

Expected Responses:

  • /api/greeting?name=John → "ServiceA received: Hello John from Service B!"
  • /api/health → "Service A is healthy!" or "Service B is healthy!"
  • /api/status → "Service A: OK | Service B: Service B is healthy!"
  • /api/hello?name=World → "Hello World from Service B!"

Monitoring and Logs

CloudWatch Logs

# View Service A logs
aws logs tail /ecs/service-a-${ENVIRONMENT} --follow

# View Service B logs
aws logs tail /ecs/service-b-${ENVIRONMENT} --follow

ECS Service Status

# Check ECS services
aws ecs list-services --cluster microservices-${ENVIRONMENT}

# Describe specific service
aws ecs describe-services --cluster microservices-${ENVIRONMENT} --services service-a-${ENVIRONMENT}

Cleanup/Destroy

Destroy Staging Environment

cd terraform
terraform destroy -var-file="environments/staging.tfvars"

Destroy Production Environment

cd terraform
terraform destroy -var-file="environments/production.tfvars"

Complete Cleanup

# Destroy all environments
cd terraform

# Staging
terraform destroy -var-file="environments/staging.tfvars" -auto-approve

# Production
terraform destroy -var-file="environments/production.tfvars" -auto-approve

# Clean up ECR repositories (if needed)
aws ecr delete-repository --repository-name service-a --force
aws ecr delete-repository --repository-name service-b --force

Emergency Cleanup

If Terraform state is corrupted or you need to force cleanup:

# Remove Terraform state (use with caution)
rm terraform.tfstate*

# Manually delete resources via AWS Console or CLI
aws ecs delete-cluster --cluster microservices-staging
aws ecs delete-cluster --cluster microservices-production

Terraform Learning Resources

Key Terraform Concepts

  1. Providers: AWS provider for infrastructure management
  2. Resources: VPC, ECS, ALB, etc.
  3. Variables: Environment-specific configurations
  4. Modules: Reusable infrastructure components
  5. State: Current infrastructure state tracking

Terraform Best Practices

  1. Use version constraints for providers
  2. Implement proper tagging strategy
  3. Use tfvars files for environment configurations
  4. Store state remotely (S3 + DynamoDB)
  5. Implement proper IAM permissions

Useful Terraform Commands

# Format code
terraform fmt

# Validate configuration
terraform validate

# Show current state
terraform show

# List resources
terraform state list

# Import existing resource
terraform import aws_vpc.main vpc-12345678

# Target specific resource
terraform apply -target=aws_ecs_service.service_a

Troubleshooting

Common Issues

  1. ECR Login Issues

    aws ecr get-login-password --region ap-southeast-1 | docker login --username AWS --password-stdin YOUR_ACCOUNT.dkr.ecr.ap-southeast-1.amazonaws.com
  2. Service Discovery Issues

    • Check security group rules
    • Verify service mesh configuration
    • Check ECS service logs
  3. Terraform State Lock

    terraform force-unlock LOCK_ID
  4. Port Conflicts

    • Service A: 8080
    • Service B: 8081
    • ALB: 80, 443

Support

For issues or questions, check:

  • AWS ECS documentation
  • Terraform AWS provider documentation
  • CloudWatch logs for application errors
  • GitHub Actions workflow logs for CI/CD issues

Terraform useful commands

sudo apt update && sudo apt install terraform
terraform version
terraform init
terraform validate
terraform plan -var-file=environments/staging.tfvars
terraform apply -var-file=environments/staging.tfvars
terraform output alb_dns_name
terraform state list
terraform show
terraform fmt
terraform destroy -var-file=environments/staging.tfvars
terraform workspace new staging
terraform workspace list
terraform workspace select staging
terraform workspace show
# (partial) update command for ECR repositories only
terraform apply -var-file=environments/staging.tfvars -target=aws_ecr_repository.service_a -target=aws_ecr_repository.service_b -auto-approve


Note: Never commit terraform.tfstate files to version control. Use remote state storage for production environments.

About

Microservices with ServiceA, ServiceB, and AWS ECS Fargate deployment

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors