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

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors