Skip to content

Commit 0454024

Browse files
committed
feat: add AWS ECS deployment support with Hazelcast clustering
Add configuration and documentation for deploying the application on AWS ECS (Elastic Container Service) with distributed Hazelcast caching. Changes: 1. Add hazelcast-aws dependency: - Enable AWS EC2/ECS instance discovery - Support IAM role-based authentication - Compatible with Fargate and EC2 launch types 2. Create ecs Spring profile: - application-ecs.yml: ECS-specific Spring configuration - hazelcast-ecs.yml: AWS discovery configuration - CloudWatch-friendly logging format - Prometheus metrics export enabled 3. Hazelcast ECS configuration: - AWS region-based discovery - Tag-based member filtering (hazelcast-cluster) - Security group filtering support - IAM role credentials (no hardcoded keys) - Private IP addressing for VPC networking 4. Update HazelcastConfig: - Add ecs profile detection - Load hazelcast-ecs.yml when ecs profile is active - Support 3 profiles: local (default), k8s, ecs 5. Comprehensive ECS deployment guide: - IAM permissions required - Task definition configuration - Security group setup - Service discovery with Cloud Map - Health check configuration - Troubleshooting guide - Production checklist Environment Variables (ECS): - SPRING_PROFILES_ACTIVE=ecs (required) - AWS_REGION=us-east-1 - HAZELCAST_TAG_KEY=hazelcast-cluster - HAZELCAST_TAG_VALUE=spring-playground - HAZELCAST_SECURITY_GROUP=sg-xxx (optional) Benefits: - Automatic member discovery in ECS clusters - Shared cache across all ECS tasks - Works with Fargate and EC2 launch types - No manual IP configuration needed - Secure IAM-based authentication Usage: - Local: mvn spring-boot:run (default profile) - Kubernetes: SPRING_PROFILES_ACTIVE=k8s - AWS ECS: SPRING_PROFILES_ACTIVE=ecs Deployment platforms now supported: ✓ Local development (docker-compose) ✓ Kubernetes (Helm) ✓ AWS ECS (Fargate/EC2)
1 parent a6e7f83 commit 0454024

File tree

5 files changed

+470
-0
lines changed

5 files changed

+470
-0
lines changed

docs/ECS_DEPLOYMENT.md

Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
# AWS ECS Deployment Guide
2+
3+
This guide explains how to deploy the Spring Playground application on AWS ECS (Elastic Container Service) with Hazelcast clustering.
4+
5+
## Prerequisites
6+
7+
1. **AWS Account** with appropriate permissions
8+
2. **ECS Cluster** created
9+
3. **RDS PostgreSQL** instance (or use Amazon Aurora)
10+
4. **ECR Repository** for Docker images
11+
5. **IAM Role** for ECS Task with required permissions
12+
13+
## IAM Permissions Required
14+
15+
The ECS Task Role needs the following permissions for Hazelcast discovery:
16+
17+
```json
18+
{
19+
"Version": "2012-10-17",
20+
"Statement": [
21+
{
22+
"Effect": "Allow",
23+
"Action": [
24+
"ec2:DescribeInstances",
25+
"ec2:DescribeNetworkInterfaces",
26+
"ec2:DescribeTags",
27+
"ecs:ListTasks",
28+
"ecs:DescribeTasks",
29+
"ecs:DescribeContainerInstances"
30+
],
31+
"Resource": "*"
32+
}
33+
]
34+
}
35+
```
36+
37+
## Environment Variables
38+
39+
Configure these environment variables in your ECS Task Definition:
40+
41+
### Required Variables
42+
43+
```bash
44+
# Spring Profile
45+
SPRING_PROFILES_ACTIVE=ecs
46+
47+
# Database Configuration
48+
SPRING_DATASOURCE_URL=jdbc:postgresql://your-rds-endpoint:5432/playground
49+
SPRING_DATASOURCE_USERNAME=postgres
50+
SPRING_DATASOURCE_PASSWORD=your-secure-password
51+
52+
# Cache Configuration
53+
SPRING_CACHE_TYPE=hazelcast
54+
55+
# AWS Configuration
56+
AWS_REGION=us-east-1
57+
```
58+
59+
### Optional Hazelcast Variables
60+
61+
```bash
62+
# Hazelcast Cluster Configuration
63+
HAZELCAST_TAG_KEY=hazelcast-cluster
64+
HAZELCAST_TAG_VALUE=spring-playground
65+
66+
# Security Group for member filtering (optional)
67+
HAZELCAST_SECURITY_GROUP=sg-xxxxxxxxx
68+
```
69+
70+
## Task Definition Configuration
71+
72+
### Port Mappings
73+
74+
Ensure both ports are exposed in your task definition:
75+
76+
```json
77+
{
78+
"portMappings": [
79+
{
80+
"containerPort": 8080,
81+
"hostPort": 8080,
82+
"protocol": "tcp"
83+
},
84+
{
85+
"containerPort": 5701,
86+
"hostPort": 5701,
87+
"protocol": "tcp",
88+
"name": "hazelcast"
89+
}
90+
]
91+
}
92+
```
93+
94+
### Health Check
95+
96+
```json
97+
{
98+
"healthCheck": {
99+
"command": [
100+
"CMD-SHELL",
101+
"curl -f http://localhost:8080/actuator/health || exit 1"
102+
],
103+
"interval": 30,
104+
"timeout": 5,
105+
"retries": 3,
106+
"startPeriod": 60
107+
}
108+
}
109+
```
110+
111+
### Resource Requirements
112+
113+
Recommended resource allocation:
114+
115+
```json
116+
{
117+
"cpu": "512",
118+
"memory": "1024",
119+
"requiresCompatibilities": ["FARGATE"]
120+
}
121+
```
122+
123+
## Security Group Configuration
124+
125+
Your ECS tasks need a security group that allows:
126+
127+
1. **Inbound Rules:**
128+
- Port 8080 (HTTP) - from ALB/NLB
129+
- Port 5701 (Hazelcast) - from same security group (for clustering)
130+
131+
2. **Outbound Rules:**
132+
- All traffic (for database, AWS API calls)
133+
134+
Example Security Group Rules:
135+
136+
```bash
137+
# Inbound
138+
Type: HTTP
139+
Protocol: TCP
140+
Port: 8080
141+
Source: sg-alb-xxxxxxxxx (Load Balancer SG)
142+
143+
Type: Custom TCP
144+
Protocol: TCP
145+
Port: 5701
146+
Source: sg-ecs-xxxxxxxxx (Same ECS Task SG)
147+
148+
# Outbound
149+
Type: All traffic
150+
Protocol: All
151+
Port Range: All
152+
Destination: 0.0.0.0/0
153+
```
154+
155+
## ECS Service Configuration
156+
157+
### Service Discovery (Optional but Recommended)
158+
159+
Enable AWS Cloud Map for service discovery:
160+
161+
```bash
162+
aws servicediscovery create-service \
163+
--name spring-playground \
164+
--dns-config 'NamespaceId="ns-xxxxxxxxx",DnsRecords=[{Type="A",TTL="10"}]' \
165+
--health-check-custom-config FailureThreshold=1
166+
```
167+
168+
### Desired Count
169+
170+
For Hazelcast clustering, run at least 2 tasks:
171+
172+
```json
173+
{
174+
"desiredCount": 2,
175+
"deploymentConfiguration": {
176+
"maximumPercent": 200,
177+
"minimumHealthyPercent": 100
178+
}
179+
}
180+
```
181+
182+
## Tagging for Hazelcast Discovery
183+
184+
Tag your ECS tasks for Hazelcast member discovery:
185+
186+
```bash
187+
# In your ECS Task Definition
188+
"tags": [
189+
{
190+
"key": "hazelcast-cluster",
191+
"value": "spring-playground"
192+
}
193+
]
194+
```
195+
196+
These tags must match the values in `hazelcast-ecs.yml`:
197+
- `HAZELCAST_TAG_KEY=hazelcast-cluster`
198+
- `HAZELCAST_TAG_VALUE=spring-playground`
199+
200+
## Deployment Steps
201+
202+
### 1. Build and Push Docker Image
203+
204+
```bash
205+
# Build the application
206+
mvn clean package -DskipTests
207+
208+
# Build Docker image
209+
docker build -t spring-playground:latest .
210+
211+
# Tag for ECR
212+
docker tag spring-playground:latest \
213+
123456789012.dkr.ecr.us-east-1.amazonaws.com/spring-playground:latest
214+
215+
# Login to ECR
216+
aws ecr get-login-password --region us-east-1 | \
217+
docker login --username AWS --password-stdin \
218+
123456789012.dkr.ecr.us-east-1.amazonaws.com
219+
220+
# Push to ECR
221+
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/spring-playground:latest
222+
```
223+
224+
### 2. Create Task Definition
225+
226+
```bash
227+
aws ecs register-task-definition \
228+
--cli-input-json file://task-definition.json
229+
```
230+
231+
### 3. Create ECS Service
232+
233+
```bash
234+
aws ecs create-service \
235+
--cluster spring-playground-cluster \
236+
--service-name spring-playground \
237+
--task-definition spring-playground:1 \
238+
--desired-count 2 \
239+
--launch-type FARGATE \
240+
--network-configuration "awsvpcConfiguration={subnets=[subnet-xxx,subnet-yyy],securityGroups=[sg-xxxxxxxxx],assignPublicIp=ENABLED}" \
241+
--load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:...,containerName=spring-playground,containerPort=8080"
242+
```
243+
244+
## Verify Hazelcast Clustering
245+
246+
### 1. Check Application Logs
247+
248+
```bash
249+
aws logs tail /ecs/spring-playground --follow
250+
251+
# Look for:
252+
# Loading Hazelcast configuration from: hazelcast-ecs.yml
253+
# Members {size:2, ver:2} [
254+
# Member [10.0.1.100]:5701 - this
255+
# Member [10.0.1.101]:5701
256+
# ]
257+
```
258+
259+
### 2. Test Idempotency Across Instances
260+
261+
```bash
262+
# First request - creates cache on instance 1
263+
curl -X GET "http://your-alb-dns/api/test/random?requestId=test-123"
264+
# {"randomNumber": 456}
265+
266+
# Second request - should hit instance 2 but return cached value
267+
curl -X GET "http://your-alb-dns/api/test/random?requestId=test-123"
268+
# {"randomNumber": 456} ← Same number!
269+
```
270+
271+
### 3. Check Actuator Endpoints
272+
273+
```bash
274+
# Health check
275+
curl http://your-alb-dns/actuator/health
276+
277+
# Hazelcast cluster info
278+
curl http://your-alb-dns/actuator/hazelcast
279+
```
280+
281+
## Troubleshooting
282+
283+
### Issue: Tasks can't discover each other
284+
285+
**Solution:**
286+
1. Check IAM role has `ec2:DescribeInstances` permission
287+
2. Verify security group allows port 5701 between tasks
288+
3. Check tags are correctly set on ECS tasks
289+
4. Enable DEBUG logging: `com.hazelcast.aws: DEBUG`
290+
291+
### Issue: Hazelcast members keep leaving/joining
292+
293+
**Solution:**
294+
1. Increase health check `startPeriod` to 90 seconds
295+
2. Check network connectivity between tasks
296+
3. Verify task resources (CPU/Memory) are sufficient
297+
4. Check CloudWatch logs for errors
298+
299+
### Issue: Can't connect to RDS
300+
301+
**Solution:**
302+
1. Ensure ECS tasks are in same VPC as RDS
303+
2. Check RDS security group allows connections from ECS SG
304+
3. Verify connection string is correct
305+
4. Check IAM database authentication if enabled
306+
307+
## Cost Optimization
308+
309+
1. **Use Fargate Spot** for non-production environments
310+
2. **Right-size resources**: Start with 512 CPU / 1024 MB RAM
311+
3. **Enable Auto Scaling**: Scale based on CPU/Memory metrics
312+
4. **Use Aurora Serverless** for database to reduce costs
313+
314+
## Monitoring
315+
316+
### CloudWatch Metrics
317+
318+
Important metrics to monitor:
319+
- `CPUUtilization`
320+
- `MemoryUtilization`
321+
- `TargetResponseTime` (ALB)
322+
- Custom metrics via Micrometer/Prometheus
323+
324+
### CloudWatch Logs
325+
326+
Enable log streaming:
327+
328+
```json
329+
{
330+
"logConfiguration": {
331+
"logDriver": "awslogs",
332+
"options": {
333+
"awslogs-group": "/ecs/spring-playground",
334+
"awslogs-region": "us-east-1",
335+
"awslogs-stream-prefix": "ecs"
336+
}
337+
}
338+
}
339+
```
340+
341+
## Production Checklist
342+
343+
- [ ] IAM roles configured with least privilege
344+
- [ ] Secrets stored in AWS Secrets Manager (not env vars)
345+
- [ ] Multi-AZ deployment for high availability
346+
- [ ] Auto Scaling configured
347+
- [ ] CloudWatch alarms set up
348+
- [ ] Backup strategy for RDS
349+
- [ ] SSL/TLS certificates configured on ALB
350+
- [ ] WAF enabled on ALB
351+
- [ ] VPC Flow Logs enabled
352+
- [ ] Container insights enabled
353+
354+
## References
355+
356+
- [AWS ECS Documentation](https://docs.aws.amazon.com/ecs/)
357+
- [Hazelcast AWS Plugin](https://github.com/hazelcast/hazelcast-aws)
358+
- [Spring Boot on AWS](https://spring.io/guides/gs/spring-boot-on-aws/)

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@
7070
<version>${hazelcast.version}</version>
7171
</dependency>
7272

73+
<dependency>
74+
<groupId>com.hazelcast</groupId>
75+
<artifactId>hazelcast-aws</artifactId>
76+
<version>${hazelcast.version}</version>
77+
</dependency>
78+
7379
<!-- Spring Boot Cache Support -->
7480
<dependency>
7581
<groupId>org.springframework.boot</groupId>

src/main/java/com/khoa/spring/playground/config/HazelcastConfig.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public HazelcastInstance hazelcastInstance(Config hazelcastConfiguration) {
4545
/**
4646
* Determines which Hazelcast configuration file to load based on active Spring profiles.
4747
* - k8s profile: hazelcast-k8s.yml (Kubernetes discovery)
48+
* - ecs profile: hazelcast-ecs.yml (AWS ECS/EC2 discovery)
4849
* - default: hazelcast.yml (TCP-IP for local development)
4950
*/
5051
private String getHazelcastConfigFile() {
@@ -54,6 +55,9 @@ private String getHazelcastConfigFile() {
5455
if (Arrays.asList(activeProfiles).contains("k8s")) {
5556
return "hazelcast-k8s.yml";
5657
}
58+
else if (Arrays.asList(activeProfiles).contains("ecs")) {
59+
return "hazelcast-ecs.yml";
60+
}
5761
return "hazelcast.yml";
5862
}
5963

0 commit comments

Comments
 (0)