The previous post, explained how to run a simple REST service with the Docker environment. Deploying a single entity in the Docker environment is not the purpose of using docker. Docker is a container orchestration platform that can address auto-scalable, fault-tolerant features that cannot be addressed in monolithic models. Bellow diagram includes spring boot based microservice components including Spring cloud gateway, eureka service, config service, and app service. Docker will expose 8080 port to external connectivity so the client can send requests on this port.
Cloud components in bellow diagram
1. Docker - Container orchestration framework
2. Host - Machine that installed docker
3. Eureka service - Keeps service information deployed services including IP, Ports, scaling information
4. Config service - Connects with config repositories
5. App service - This has custom logic that serves external requests came from cloud gateway
6. Spring Cloud Gateway - This receives requests from the host's client app and route it and load balance to each app service. This service will expose host system via 8080 port
Figure 1 - Scaled microservice with API Gateway
In order to set up and deploy the above solution, we required setup the Docker compose file.
What is the Docker compose?
1. It is a YAML file
2. Includes a group of Docker files of each service
3. Has service dependencies ( service A depends on Eureka service and Config service )
4. Has startup order ( service A will start after Eureka and Config service )
5. Has an internal network ( How each service links )
6. Has scaleling port ranges of each service
List of files to setup above services
Spring BOOT Jar Files
1. config-service.jar
Code
@SpringBootApplication
@EnableConfigServer
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}
Major Dependencies
spring-boot-starter-web
spring-boot-starter-actuator
spring-cloud-config-server
2. eureka-service.jar
Code
@SpringBootApplication
@EnableEurekaServer
public class EurekaApp {
public static void main(String[] args) {
SpringApplication.run(EurekaApp.class, args);
}
}
Major Dependencies
spring-boot-starter-web
spring-boot-starter-actuator
spring-cloud-netflix-eureka-server
3. app-service.jar
Code ( It has the main method and rest endpoint return the node's port. So we can check load balancing)
@SpringBootApplication
@EnableDiscoveryClient
@EnableAutoConfiguration
public class AppService {
public static void main(String[] args) {
SpringApplication.run(AppService.class, args);
}
}
@Controller
@RestController
@ControllerAdvice
public class RestService{
@Data
public class CommonResponse {
private String statusCode;
private String statusDesc;
}
Major Dependencies
spring-boot-starter-web
spring-cloud-starter-netflix-eureka-client
spring-boot-starter-actuator
spring-cloud-starter-config
4. spring-gateway-client-service.jar
Code( This has router and load balancer, lb is for load balancing to app-service which is mentioned in the docker compose )
Major Dependencies
spring-cloud-starter-gateway
spring-cloud-starter-netflix-eureka-client
spring-cloud-starter-config
Docker Files
1. Dockerfile.configservice
FROM openjdk:13-alpine
EXPOSE 8888
ADD config-service.jar ./config-service.jar
ENTRYPOINT ["java","-jar","-Duser.timezone=GMT+0530","config-service.jar"]
2. Dockerfile.eurekaservice
FROM openjdk:13-alpine
EXPOSE 8080
RUN apk --no-cache add netcat-openbsd
COPY check-entrypoint-eureka.sh ./check-entrypoint-eureka.sh
ADD eureka-service.jar ./eureka-service.jar
RUN chmod 755 check-entrypoint-eureka.sh
We include check-entrypoint-eureka.sh checks whether config service is up and running. Eureka service will be started
3. Dockerfile.appservice
FROM openjdk:13-alpine
EXPOSE 8080
RUN apk --no-cache add netcat-openbsd
COPY check-entrypoint-app-service.sh ./check-entrypoint-app-service.sh
ADD app-service.jar ./app-service.jar
RUN chmod 755 check-entrypoint-app-service.sh
We include check-entrypoint-app-service.sh checks whether config service and eureka services are up and running. Then only app-service will be started
4. Dockerfile.gatewayclient
FROM openjdk:13-alpine
EXPOSE 8090
RUN apk --no-cache add netcat-openbsd
COPY check-entrypoint-gatewayclient.sh ./check-entrypoint-gatewayclient.sh
ADD spring-gateway-client-service.jar ./spring-gateway-client-service.jar
RUN chmod 755 check-entrypoint-gatewayclient.sh
We include check-entrypoint-gatewayclient.sh checks whether config service and eureka service, and app services are up and running. Then gateway client will be started
Linux script files ( This is to check dependent services are up and running )
These Linux scripts run from the container itself. Container build by docker container knows how to access other services by service name and port
check-entrypoint-eureka.sh
#!/bin/sh
while ! nc -z config-service 8888 ; do
echo "<------------ span="" style="background-color: #ffd966;">Euerka service is waiting for upcoming Config service------------>
---------------->" sleep 2Cloud components in bellow diagram
1. Docker - Container orchestration framework
2. Host - Machine that installed docker
3. Eureka service - Keeps service information deployed services including IP, Ports, scaling information
4. Config service - Connects with config repositories
5. App service - This has custom logic that serves external requests came from cloud gateway
6. Spring Cloud Gateway - This receives requests from the host's client app and route it and load balance to each app service. This service will expose host system via 8080 port
Figure 1 - Scaled microservice with API Gateway
In order to set up and deploy the above solution, we required setup the Docker compose file.
What is the Docker compose?
1. It is a YAML file
2. Includes a group of Docker files of each service
3. Has service dependencies ( service A depends on Eureka service and Config service )
4. Has startup order ( service A will start after Eureka and Config service )
5. Has an internal network ( How each service links )
6. Has scaleling port ranges of each service
List of files to setup above services
Spring BOOT Jar Files
1. config-service.jar
Code
@SpringBootApplication
@EnableConfigServer
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}
Major Dependencies
spring-boot-starter-web
spring-boot-starter-actuator
spring-cloud-config-server
2. eureka-service.jar
Code
@SpringBootApplication
@EnableEurekaServer
public class EurekaApp {
public static void main(String[] args) {
SpringApplication.run(EurekaApp.class, args);
}
}
Major Dependencies
spring-boot-starter-web
spring-boot-starter-actuator
spring-cloud-netflix-eureka-server
3. app-service.jar
Code ( It has the main method and rest endpoint return the node's port. So we can check load balancing)
@SpringBootApplication
@EnableDiscoveryClient
@EnableAutoConfiguration
public class AppService {
public static void main(String[] args) {
SpringApplication.run(AppService.class, args);
}
}
@Controller
@RestController
@ControllerAdvice
public class RestService{
@GetMapping(value = "/testAPI", produces = "application/json")
public ResponseEntity testAPI(@RequestParam Map receiptParams,HttpServletRequest request) {
CommonResponse response = new CommonResponse();
response.setStatusCode("1");
response.setStatusDesc("Success. Request received. Node details : port="+env.getProperty("local.server.port"));
ResponseEntity responseEntity = new ResponseEntity<>(response, HttpStatus.OK);
return responseEntity;
}
}
}
@Data
public class CommonResponse {
private String statusCode;
private String statusDesc;
}
Major Dependencies
spring-boot-starter-web
spring-cloud-starter-netflix-eureka-client
spring-boot-starter-actuator
spring-cloud-starter-config
4. spring-gateway-client-service.jar
Code( This has router and load balancer, lb is for load balancing to app-service which is mentioned in the docker compose )
@SpringBootApplication
@RestController
@EnableDiscoveryClient
@EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes().route(p -> p.path("/testAPI/**").filters(f -> f.stripPrefix(1)).uri("lb://app-service/")).build();
}
}
spring-cloud-starter-gateway
spring-cloud-starter-netflix-eureka-client
spring-cloud-starter-config
Docker Files
1. Dockerfile.configservice
FROM openjdk:13-alpine
EXPOSE 8888
ADD config-service.jar ./config-service.jar
ENTRYPOINT ["java","-jar","-Duser.timezone=GMT+0530","config-service.jar"]
2. Dockerfile.eurekaservice
FROM openjdk:13-alpine
EXPOSE 8080
RUN apk --no-cache add netcat-openbsd
COPY check-entrypoint-eureka.sh ./check-entrypoint-eureka.sh
ADD eureka-service.jar ./eureka-service.jar
RUN chmod 755 check-entrypoint-eureka.sh
We include check-entrypoint-eureka.sh checks whether config service is up and running. Eureka service will be started
3. Dockerfile.appservice
FROM openjdk:13-alpine
EXPOSE 8080
RUN apk --no-cache add netcat-openbsd
COPY check-entrypoint-app-service.sh ./check-entrypoint-app-service.sh
ADD app-service.jar ./app-service.jar
RUN chmod 755 check-entrypoint-app-service.sh
We include check-entrypoint-app-service.sh checks whether config service and eureka services are up and running. Then only app-service will be started
4. Dockerfile.gatewayclient
FROM openjdk:13-alpine
EXPOSE 8090
RUN apk --no-cache add netcat-openbsd
COPY check-entrypoint-gatewayclient.sh ./check-entrypoint-gatewayclient.sh
ADD spring-gateway-client-service.jar ./spring-gateway-client-service.jar
RUN chmod 755 check-entrypoint-gatewayclient.sh
We include check-entrypoint-gatewayclient.sh checks whether config service and eureka service, and app services are up and running. Then gateway client will be started
Linux script files ( This is to check dependent services are up and running )
These Linux scripts run from the container itself. Container build by docker container knows how to access other services by service name and port
check-entrypoint-eureka.sh
#!/bin/sh
while ! nc -z config-service 8888 ; do
echo "<------------ span="" style="background-color: #ffd966;">Euerka service is waiting for upcoming Config service------------>
done
java -jar eureka-service.jar
check-entrypoint-app-service.sh
#!/bin/sh
while ! nc -z config-service 8888 ; do
echo "<------------ span="" style="background-color: #ffd966;">App service is waiting for upcoming Config service------------>
---------------->" sleep 2
done
while ! nc -z eureka-service 8761 ; do
echo "<------------ span="" style="background-color: #ffd966;">App service is waiting for upcoming Eureka service------------>
---------------->" sleep 2
done
java -jar app-service.jar
check-entrypoint-gatewayclient.sh
#!/bin/sh
while ! nc -z config-service 8888 ; do
echo "<------------ span="" style="background-color: #ffd966;">App service is waiting for upcoming Config service------------>
---------------->" sleep 2
done
while ! nc -z eureka-service 8761 ; do
echo "<------------ span="" style="background-color: #ffd966;">App service is waiting for upcoming Eureka service------------>
---------------->" sleep 2
done
while ! nc -z app-service 8080 ; do
echo "<------------ span="" style="background-color: #ffd966;">App service is waiting for upcoming App service------------>
---------------->" sleep 2
done
java -jar spring-gateway-client-service.jar
Docker Compose YML
version: '2'
services:
config-service:
container_name: config-service
build:
context: .
dockerfile: Dockerfile.configservice
image: config-service:latest
expose:
- 8888
ports:
- 8888:8888
networks:
- spring-cloud-network
volumes:
- spring-cloud-config-repo:/var/lib/spring-cloud/config-repo
logging:
driver: json-file
eureka-service:
container_name: eureka-service
build:
context: .
dockerfile: Dockerfile.eurekaservice
image: eureka-service:latest
expose:
- 8761
ports:
- 8761:8761
networks:
- spring-cloud-network
links:
- config-service:config-service
depends_on:
- config-service
command: './check-entrypoint-eureka.sh'
logging:
driver: json-file
app-service:
build:
context: .
dockerfile: Dockerfile.appservice
image: app-service:latest
ports:
- "8080"
networks:
- spring-cloud-network
links:
- config-service:config-service
- eureka-service:eureka-service
depends_on:
- config-service
- eureka-service
command: './check-entrypoint-app-service.sh'
logging:
driver: json-file
spring-gateway-client-service:
build:
context: .
dockerfile: Dockerfile.gatewayclient
image: spring-gateway-client-service:latest
expose:
- 8090
ports:
- 8090:8090
networks:
- spring-cloud-network
links:
- eureka-service:eureka-service
- app-service:app-service
depends_on:
- eureka-service
- app-service
command: './check-entrypoint-gatewayclient.sh'
logging:
driver: json-file
networks:
spring-cloud-network:
driver: bridge
volumes:
spring-cloud-config-repo:
external: true
How to build docker compose
command: sudo docker-compose build
output :
Building config-service
Step 1/4 : FROM openjdk:13-alpine
---> c4b0433a01ac
Step 2/4 : EXPOSE 8888
---> Using cache
---> 326abacc404e
Step 3/4 : ADD config-service.jar ./config-service.jar
---> Using cache
---> 3db14ff99098
Step 4/4 : ENTRYPOINT ["java","-jar","-Duser.timezone=GMT+0530","config-service.jar"]
---> Using cache
---> c566948166a0
Successfully built c566948166a0
Successfully tagged config-service:latest
Building eureka-service
Step 1/6 : FROM openjdk:13-alpine
---> c4b0433a01ac
Step 2/6 : EXPOSE 8080
---> Using cache
---> e638ee32b31e
Step 3/6 : RUN apk --no-cache add netcat-openbsd
---> Using cache
---> 879e12e2b629
Step 4/6 : COPY check-entrypoint-eureka.sh ./check-entrypoint-eureka.sh
---> Using cache
---> dfcd25b502ec
Step 5/6 : ADD eureka-service.jar ./eureka-service.jar
---> Using cache
---> 44c826f61f35
Step 6/6 : RUN chmod 755 check-entrypoint-eureka.sh
---> Using cache
---> 660062ba9463
Successfully built 660062ba9463
Successfully tagged eureka-service:latest
Building app-service
Step 1/6 : FROM openjdk:13-alpine
---> c4b0433a01ac
Step 2/6 : EXPOSE 8080
---> Using cache
---> e638ee32b31e
Step 3/6 : RUN apk --no-cache add netcat-openbsd
---> Using cache
---> 879e12e2b629
Step 4/6 : COPY check-entrypoint-app-service.sh ./check-entrypoint-app-service.sh
---> Using cache
---> 638c51bfdba2
Step 5/6 : ADD app-service.jar ./app-service.jar
---> Using cache
---> 1f9710fefe1e
Step 6/6 : RUN chmod 755 check-entrypoint-app-service.sh
---> Using cache
---> 0557299d4fcf
Successfully built 0557299d4fcf
Successfully tagged app-service:latest
Building spring-gateway-client-service
Step 1/6 : FROM openjdk:13-alpine
---> c4b0433a01ac
Step 2/6 : EXPOSE 8090
---> Using cache
---> eab248ba6443
Step 3/6 : RUN apk --no-cache add netcat-openbsd
---> Using cache
---> 72051b97ed40
Step 4/6 : COPY check-entrypoint-gatewayclient.sh ./check-entrypoint-gatewayclient.sh
---> Using cache
---> 00dfde72cadf
Step 5/6 : ADD spring-gateway-client-service.jar ./spring-gateway-client-service.jar
---> Using cache
---> 2fb78b235394
Step 6/6 : RUN chmod 755 check-entrypoint-gatewayclient.sh
---> Using cache
---> c7126edc4109
Successfully built c7126edc4109
Successfully tagged spring-gateway-client-service:latest
How to start services with scaling to app-service
command : sudo docker-compose up -d --scale app-service=4 --remove-orphans
output: ( You can see services are running based on dependency first approach, Green colored areas show how scale nodes are running )
Starting config-service ...
Starting config-service ... done
Starting eureka-service ...
Starting eureka-service ... done
Starting tmp_app-service_1 ...
Starting tmp_app-service_2 ...
Starting tmp_app-service_3 ...
Starting tmp_app-service_4 ...
Starting tmp_app-service_1 ... done
Starting tmp_app-service_2 ... done
Starting tmp_app-service_3 ... done
Starting tmp_app-service_4 ... done
Starting tmp_spring-gateway-client-service_1 ...
Starting tmp_spring-gateway-client-service_1 ... done
Eureka service registration
You can see there are 4 nodes are up and running from app-service. It shows the port is 8080 for all nodes. Docker can differentiate these nodes as separate container ids are assigned.
Sending multiple requests
command:
curl http://localhost:8090/testAPI/get/
curl http://localhost:8090/testAPI/get/
curl http://localhost:8090/testAPI/get/
curl http://localhost:8090/testAPI/get/
curl http://localhost:8090/testAPI/get/
curl http://localhost:8090/testAPI/get/
Success. Request received. Node details : port=8080
Success. Request received. Node details : port=8081
Success. Request received. Node details : port=8082
Success. Request received. Node details : port=8083
Success. Request received. Node details : port=8080
Success. Request received. Node details : port=8081
How to stop services
command: sudo docker-compose down
output : ( Shutting down started from service has no external dependency, config-service is providing dependency for all modules, so it will be shut down in the last stage )
Stopping tmp_spring-gateway-client-service_1 ... done
Stopping tmp_app-service_4 ... done
Stopping tmp_app-service_3 ... done
Stopping tmp_app-service_2 ... done
Stopping tmp_app-service_1 ... done
Stopping eureka-service ... done
Stopping config-service ... done
Removing tmp_spring-gateway-client-service_1 ... done
Removing tmp_app-service_4 ... done
Removing tmp_app-service_3 ... done
Removing tmp_app-service_2 ... done
Removing tmp_app-service_1 ... done
Removing eureka-service ... done
Removing config-service ... done
Removing network tmp_spring-cloud-network
No comments:
Post a Comment