Spring Boot-based Microservices & Deploy on AWS
Spring Cloud Microservices Platform is a Spring Boot-based microservices app, offering a robust, scalable distributed system with high-performance concurrency and comprehensive CI/CD integration. Each microservice operates with its own dedicated database, ensuring data isolation and consistency. Eureka is employed for dynamic service discovery, while RabbitMQ ensures reliable and asynchronous messaging between services.
External requests are managed by Spring Cloud Gateway, providing intelligent routing and load balancing. Zipkin is integrated for distributed tracing, offering deep insights into the system's performance and latency. Spring Cloud Config centralizes the management of configuration properties across the microservices, enhancing consistency and flexibility.
To ensure resilience, Resilience4J introduces fault tolerance patterns, such as circuit breakers and rate limiters, safeguarding against cascading failures. Spring Security is implemented to provide robust authentication and authorization mechanisms, protecting sensitive exchange operations.
The application is containerized using Docker and deployed on AWS for scalable orchestration and management.
Docker Hub Repository: docker
Programming and Frameworks: Java, Spring Boot, Spring MVC, Microservice
Tools and Test: SQL, Hibernate, JPA, Actuator, Docker, RabbitMQ, Eureka, API Gateway, Resilience4J, Github, Zipkin, Postman, Micrometer, OpenTelemetry, AWS, EC2
Spring Boot ready: https://start.spring.io/
Recommendations:
Use latest version of Java
Remember: Spring Boot 3+ works only with Java 17+
Java Ready:
Windows - https://www.youtube.com/watch?v=I0SBRWVS0ok
Linux - https://www.youtube.com/watch?v=mHvFpyHK97A
Mac - https://www.youtube.com/watch?v=U3kTdMPlgsY
IDE ready: Intellij IDEA or Eclipse
| Application | Port |
|---|---|
| Spring Cloud Config Server | 8888 |
| Currency Exchange Service | 8000, 8001, 8002, .. |
| Currency Conversion Service | 8100, 8101, 8102, .. |
| Eureka Naming Server | 8761 |
| API Gateway Server | 8765 |
| Zipkin Distributed Tracing Server | 9411 |
| To Be Continued | ... |
Independent Scaling: Each service can be scaled independently based on its specific needs, improving resource utilization.
e.g.: We can scale up/down based on our needs.
Technology Diversity: Different microservices can be developed using different programming languages, databases, or frameworks, allowing teams to choose the best tool for each job.
e.g.: We can have one microservice wrote in Python, one in Java, other one in C++.
Isolation: If one service fails, it doesn’t necessarily bring down the entire application, as microservices are isolated.
e.g.: We can debug the one we want.
We just need to care about each service's logics
To create a place to put all the configurations for all the microservices' environments in a Git repository, as a centralized location, also can be exposed to all the microservices, for later better management and easy maintenance.
For better git control, we could create a git-localconfig-repo folder to store our configurations.
e.g.: I created different environments for limits-service:

Later we can write the configurations in the Spring Cloud Config Server's application.properties(here I'm using this, application.yaml might differ):
spring.cloud.config.server.git.uri=file:///{absolute file path for 'git-localconfig-repo folder'}
Then when you make changes to the configurations locally later, you can git this.
Distributed Tracing: To trace a request across multiple components, and from its initiation to its completion, making it easier to pinpoint failures and understand the reasons behind them. By implementing distributed tracing, we gain insights into why a request failed and potentially identify when it failed.
Prerequisite: Download Zipkin first
Add(this line of code in each application.properties in the application you want to trace), then you can trace
management.tracing.sampling.probability=1.0
! You have to run Zipkin while running your application
make sure docker is running zipKin!
docker run -p 9411:9411 openzipkin/zipkin
Registration and Discovery
All the services would registered with Eureka.

To dynamically check the available ports right now, and also better for us to look up.

Created data.sql file to put the data we want, and then write the configurations in the application.properties by adding:
It enables the logging of SQL statements generated by Hibernate to the console.
And sets up an in-memory H2 database named testdb. The database exists only while the application is running and is destroyed when the application stops. This is ideal for testing or development environments, but all data is lost when the application shuts down. It defers the initialization of the DataSource until after the JPA EntityManagerFactory is created and initialized. This enables the H2 database console, allowing you to access a web-based interface to interact with the database.
features:
- match routes on any requested attribute
- Define predicates and Filters
- Integrates with Spring Cloud Discovery Client (Load Balancing) Integration with Service Discovery: Spring Cloud Gateway integrates with service discovery systems like Eureka, allowing it to automatically route requests to available instances of a service, with client-side load balancing via Spring Cloud LoadBalancer.
- Security
Authentication and Authorization: Spring Cloud Gateway can be integrated with Spring Security to handle
authentication and authorization at the gateway level. This centralizes security concerns and simplifies the implementation of security across microservices. - Resilience and Fault Tolerance Circuit Breakers: You can integrate Spring Cloud Gateway with resilience libraries like Resilience4J to apply circuit breakers, timeouts, retries, and bulkhead patterns to routes, improving the resilience of your system.
- Path Rewriting
Resilience4j is a lightweight fault tolerance library for Java, designed to handle failures and resilience patterns in microservices-based applications. It is inspired by Netflix’s Hystrix but is more modular.

-
As we can see in this pic, this is a chain of Microservices. If one of these is down or slow, it will impact the entire chain!! Here comes Resilience4J: We can return a fallback response if a service is down, or we can implement a Circuit Breaker pattern to reduce load, or we can do retry requests in case of temporary failures, or we can do rate limiting. These above are features of Resilience4J.
-
Here we just use two of them:
-
Circuit Breaker:
- Implements the circuit breaker pattern to prevent repetitive calls to a failing service. It monitors the number of failures and, once a threshold is reached, opens the circuit to stop further calls until the service recovers.
-
Rate Limiter:
- Controls the rate at which requests are allowed to be processed, preventing system overload by limiting the number of calls within a specified time period.
Run the Maven command to build the docker
./mvnw spring-boot:build-image -DskipTests
it is a convenient way to build a Docker image for your Spring Boot application using the built-in support provided by Spring Boot. This command leverages Cloud Native Buildpacks to create a Docker image without needing a Dockerfile.
if you use this command to build a docker image, then when you want to use it in other devices/networks, you have to tag this docker image into your docker hub; otherwise, this local docker image can’t be accessed.
When we want to manage multi-container Docker applications, we need this tool. It simplifies the process of orchestrating multiple Docker containers, services, networks, and volumes by specifying everything in a single YAML file. Docker Compose takes care of starting, stopping, and managing the lifecycle of these containers together.

First, create a FREE AWS account -1. Create a VPC -2. Create EC2
- Before creating EC2, I will upload the SSH key to AWS. This will let me connect to my EC2 instance later via SSH.
- **Create Security Group ** (to block ports I don’t need)
To access my instance, I need port 22 for an SSH connection and port 8080 as the default port of my Spring Boot application.
On the other way, I have no restrictions for the outgoing connections. I let my instance connect to any external port, all the internet.
The insurance type will impact the cost of my instance. I want to stay on the free tier, so I choose the t2.micro.
- **Create Security Group ** (to block ports I don’t need)
-
install Java (Java —version to check if it’s downloaded)
download file on local machine(mac) to EC2 -
get your own ssh key(remember to go to the file to see the key
<br>cat ~/.ssh/id_rsa.pub -
Launch an EC2 Instance: If you haven’t already, launch an EC2 instance with an appropriate Amazon Machine Image (AMI) such as Amazon Linux 2 or Ubuntu.
-
Connect to Your EC2 Instance: Use SSH to connect to your instance.
ssh -i your-key.pem ec2-user@your-ec2-instance-ip -
install Docker
sudo amazon-linux-extras install docker sudo service docker start sudo usermod -aG docker ec2-user -
Install Docker Compose:
sudo curl -L "<https://github.com/docker/compose/releases/download/$>(curl -s <https://api.github.com/repos/docker/compose/releases/latest> | grep tag_name | cut -d '"' -f 4)/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose
Verify Docker Compose installation:
docker-compose --version
-
Use SCP to Transfer Files:
If you have a local directory with your applications and docker-compose.yaml, use scp to transfer it to your EC2 instance.
***error happens: Warning: Identity file /home/ec2-user/Users/caipeng/.ssh/id_rsa not accessible: No such file or directory.
Solutions:
Need to copy local project to EC2
scp -i your-key.pem -r /path/to/your/folder ec2-user@your-ec2-instance-ip:/home/ec2-user/r -
Deploy Using Docker Compose Navigate to the Directory:
cd /home/ec2-user/your-folder -
Start Your Applications:
bash docker-compose up -dThis will start all the services defined in your
docker-compose.yamlin detached mode. -
Verify Deployment Check Running Containers:
docker ps
Your application should run successfully on EC2!
Here comes S3 bucket(Simple Storage Service): (where to upload all the new versions)
when a new version of the application is ready, tested, and packaged, I want it to be automatically deployed in a new EC2 instance.
I will use User Data Script in EC2 instance
I will add a script which will download the new version of the application and start it.
e.g.: So at the end of my pipeline, when my package is created, I will upload it to S3. Then start EC2 instance
The bucket name must be unique all around the world.
So, create a download script: (which downloads the latest version from S3 and starts the application)

use ASW command(AWS-CLI)
#!/bin/bash cd /home/ec2-user aws s3 cp s3://my-bucket/demo-0.0.1-SNAPTHOT.jar. java -jar demo-0.0.1-SNAPTHOT.jar
give this authority
chmod +x startup.sh
compile the project to obtain a new package:
mvn clean package
upload package to S3:
aws s3 cp target/demo-0.0.1-SNAPTHOT.jar s3://my-bucket/demo-0.0.1-SNAPTHOT.jar
IMAGE_ID="ami-0abcdef1234567890" # Replace with your AMI ID
INSTANCE_TYPE="t2.micro" # Replace with your instance type
KEY_NAME="your-key-pair" # Replace with your key pair name
SECURITY_GROUP="sg-0123456789abcdef0" # Replace with your security group ID
SUBNET_ID="subnet-0abc1234" # Replace with your subnet ID
COUNT=1 # Number of instances to launch
aws ec2 run-instances --image-id {your AMI ID} --instance-type {your instance type} --key-name {key pair name} \ --security-group-ids {your security group ID} --subnet-id {your subnet ID} --count {count you want to initialize} \ --user-data {place the startup.sh is} --associate-public-ip-address {}
Finally, from CI/CD runner, I’ve packaged my application, uploaded it to S3, and started a new EC2 instance