- 1. Start the app
- 2. Microservices
- 3. Full duplex asynchronous exchange via gRPC
- 4. Data consistency / distributed transaction
- 5. Continuous Integration with Github Actions
- 6. Kubernetes
- 7. Service mesh
- 8. Composition of services via GraphQL
- 9. Delete resources and stop the cluster
Start Docker
minikube start --cpus=2 --memory=5000 --driver=docker
Set the Kubernetes config
kubectl apply -f configmap.yaml
Set the Kubernetes secret
kubectl apply -f secret.yaml
Launch the Kubernetes deployment and service for PostgreSQL, and the Ingress gataway:
kubectl apply -f infrastructure.yaml
kubectl apply -f microservices.yaml
kubectl -n istio-system port-forward deployment/istio-ingressgateway 31380:8080
Get the list of cars to be rented:
http://localhost:31380/carservice/cars
Rent 2 cars:
curl --header "Content-Type: application/json" --request POST --data '{"customerId":1,"numberOfCars":2}' http://localhost:31380/carservice/cars
https://github.com/charroux/servicemesh/tree/main/carservice/src/main/java/com/charroux/carservice
https://github.com/charroux/servicemesh/blob/main/carservice/src/main/proto/carservice.proto
kubectl get pods
kubectl get services
https://github.com/charroux/servicemesh/blob/main/infrastructure.yaml
Enter inside the Docker containers:
kubectl exec -it [pod name] -- /bin/sh
ls
You should view the java jar file.
exit
Use Minikube to reach the carservice:
minikube service carservice --url
http://127.0.0.1:[port]/cars
How many instance are actually running:
kubectl get pods
kubectl get deployments
Start a second instance:
kubectl scale --replicas=2 deployment/[deployment name]
kubectl get pods
kubectl delete pods [pod name]
A probe is a mechanism used to determine the health of an application running in a container. Kubernetes uses probes to know when a container will be restarted, ready, and started.
Checks whether the application running inside the container is still alive and functioning properly. If the liveness probe fails, Kubernetes considers the container to be in a failed state and restarts it.
Determines whether the application inside the container is ready to accept traffic or requests. When a readiness probe fails, Kubernetes stops sending traffic to the container until it passes the probe. This is useful during application startup or when the application needs some time to initialize before serving traffic.
Health probes must be enabled by the app. See
management.health.probes.enabled=true
Actuator must be added as library. See
implementation 'org.springframework.boot:spring-boot-starter-actuator'
In https://github.com/charroux/servicemesh/blob/main/carservice/build.gradle
Liveness and Readiness must be configured by kubernetes. See
livenessProbe:
initialDelaySeconds: 180
httpGet:
path: /actuator/health
port: 8080
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
In https://github.com/charroux/servicemesh/blob/main/microservices.yaml
On the services are launched, they display the probes at (throw the gateway): http://localhost:31380/carservice/actuator/health
GitHub Actions allows to automate, customize, and execute development workflows right in a repository.
https://github.com/charroux/servicemesh/blob/main/.github/workflows/actions.yml
Kubernetes configuration to test a service in isolation: https://github.com/charroux/servicemesh/blob/main/deploymentaction.yaml
In memory database for unit testing in order to test a service in isolation: https://github.com/charroux/servicemesh/blob/main/carservice/src/test/resources/application.properties
Entities test: https://github.com/charroux/servicemesh/blob/main/carservice/src/test/java/com/charroux/carservice/entity/CarRepositoryTests.java
Use of mocks to test service in isolation: https://github.com/charroux/servicemesh/blob/main/carservice/src/test/java/com/charroux/carservice/service/RentalServiceTests.java
Microservice tests: https://github.com/charroux/servicemesh/blob/main/carservice/src/test/java/com/charroux/carservice/web/CarRentalRestServiceTests.java
Le projet utilise protobuf pour définir les contracts gRPC. Le plugin Gradle génère les sources Java à partir des fichiers .proto.
Commandes utiles :
- Générer les sources protobuf pour le module
agreementService:
./gradlew :agreementService:generateProto- Générer puis compiler le module serveur (génère les sources et compile) :
./gradlew :agreementService:generateProto :agreementServiceServer:compileJava- Générer et tout compiler (multi-module) :
./gradlew clean buildOù trouver les fichiers générés :
- Par défaut dans ce projet les fichiers générés sont placés sous
agreementService/src/generated(configuré dansagreementService/build.gradle).
Conseils IDE / CI :
- Après génération, rafraîchissez le projet Gradle dans votre IDE (IntelliJ/VS Code) pour que le répertoire
src/generatedsoit reconnu comme source Java. - Le plugin télécharge automatiquement
protocet le plugin gRPC si nécessaire ; pas besoin d'installerprotocmanuellement pour la compilation Gradle. - Si vous modifiez les fichiers
.proto, regénérez les sources puis reconstruisez les modules consommateurs (ex.agreementServiceServer,carRental).
Check an existing workflow: https://github.com/charroux/servicemesh/actions
Create a new branch:
git branch newcarservice
Move to the new branch:
git checkout newcarservice
Update the code and commit changes:
git commit -a -m "newcarservice"
Push the changes to GitHub:
git push -u origin newcarservice
Create a Pull request on GitHub and follow the workflow.
Delete the branch:
git checkout main
git branch -D newcarservice
git push origin --delete newcarservice
kubectl apply -f infrastructure.yaml
https://github.com/charroux/servicemesh/blob/main/infrastructure.yaml
kubectl apply -f microservices.yaml
https://github.com/charroux/servicemesh/blob/main/microservices.yaml
./ingress-forward.sh
Ask carservice the list of cars:
http://localhost:31380/carservice/cars
Adding a circuit breaker to carservice:
kubectl apply -f circuit-breaker.yaml
Test the circuit breaker: http://localhost:31380/carservice/cars
Disable the circuit breaker using:
kubectl delete -f circuit-breaker.yaml
Kiali is a console for Istio service mesh.
kubectl -n istio-system port-forward deployment/kiali 20001:20001
Launch the console: http://localhost:20001/
Active again carservice:
http://localhost:31380/carservice/cars
Then inspect the cluster in Kiali.
kubectl -n istio-system port-forward deployment/grafana 3000:3000
kubectl port-forward -n istio-system $(kubectl get pod -n istio-system -l app=jaeger -o jsonpath='{.items[0].metadata.name}') 16686:16686
Launch the console: http://localhost:16686/
Server side coding: https://github.com/charroux/servicemesh/blob/main/rentalservice/src/main/java/com/charroux/rentalservice/agreements/RentalController.java
kubectl delete -f infrastructure.yaml
kubectl delete -f microservices.yaml
minikube stop
https://www.docker.com/get-started/
https://minikube.sigs.k8s.io/docs/start/
Then start the Kubernetes cluster:
minikube start --cpus=2 --memory=5000 --driver=docker
https://istio.io/latest/docs/setup/getting-started/
cd istio-1.17.0
export PATH=$PWD/bin:$PATH
istioctl install --set profile=demo -y
cd ..
Enable auto-injection of the Istio side-cars when the pods are started:
kubectl label namespace default istio-injection=enabled
Install the Istio addons (Kiali, Prometheus, Jaeger, Grafana):
kubectl apply -f samples/addons
Enable auto-injection of the Istio side-cars when the pods are started:
kubectl label namespace default istio-injection=enabled
Configure Docker so that it uses the Kubernetes cluster:
minikube docker-env
eval $(minikube -p minikube docker-env)
eval $(minikube docker-env)
Docker images for the services can be built locally. New Dockerfiles have been added to the repository:
carRental/Dockerfile— multi-stage builder (Gradle + JDK 21) and runtime (Temurin JRE).agreementService/Dockerfile— builds the agreement service artifacts and packages them.agreementServiceServer/Dockerfile— builds the gRPC server service.Dockerfile.multi— multi-target Dockerfile at repository root with targetscarRentalandpostgres.
You can also find some images published to Docker Hub under the charroux namespace; if you prefer to use those, update the manifests in k8s/ accordingly.
Examples — build and push to your registry (replace charroux with your registry/namespace):
Build the car-rental image (single-service Dockerfile):
docker build -f carRental/Dockerfile -t charroux/car-rental:latest .
docker push charroux/car-rental:latestBuild the agreement-service image:
docker build -f agreementService/Dockerfile -t charroux/agreement-service:latest .
docker push charroux/agreement-service:latestBuild the agreement-service-server image:
docker build -f agreementServiceServer/Dockerfile -t charroux/agreement-service-server:latest .
docker push charroux/agreement-service-server:latestBuild the car-rental image using the multi-target root Dockerfile (alternate):
# build only the carRental target from Dockerfile.multi
docker build -f Dockerfile.multi --target carRental -t charroux/car-rental:latest .
docker push charroux/car-rental:latestBuild a custom Postgres image (optional, includes init scripts if present):
docker build -f Dockerfile.multi --target postgres -t charroux/postgres:15 .
docker push charroux/postgres:15A ready-to-use Docker Compose file for development has been added: docker-compose.dev.yml and an environment file .env.dev.
Basic workflow
- Copy or edit the example environment file (optional):
cp .env.dev .env
# edit .env to change POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_DB if needed- Start the stack (build images and run all services):
docker compose -f docker-compose.dev.yml --env-file .env.dev upThis command:
- Uses development environment variables from
.env.dev - Starts PostgreSQL database
- Starts car-rental service (REST API)
- Starts agreement-service (gRPC)
This will:
- Build and start the Postgres database
- Build and start the car-rental service on port 8080
- Build and start the agreement-service (gRPC) on port 9090
- Run in detached mode:
docker compose -f docker-compose.dev.yml --env-file .env.dev up -d- View logs:
docker compose -f docker-compose.dev.yml logs -f car-rental- Stop and remove containers:
docker compose -f docker-compose.dev.yml --env-file .env.dev downNote: Add -v flag to also remove volumes (this will delete persistent data):
docker compose -f docker-compose.dev.yml --env-file .env.dev down -vRebuild specific services:
# Rebuild single service
docker compose -f docker-compose.dev.yml --env-file .env.dev build car-rental
docker compose -f docker-compose.dev.yml --env-file .env.dev up -d
# Rebuild all services
docker compose -f docker-compose.dev.yml --env-file .env.dev build
docker compose -f docker-compose.dev.yml --env-file .env.dev up -dWhat this compose file does
- Starts a Postgres container (
postgres:15) with a persistent volumepostgres-data. - Builds the
car-rentalimage fromcarRental/Dockerfileand runs it withSPRING_PROFILES_ACTIVE=prodso the app will read Postgres connection settings from theSPRING_DATASOURCE_*env vars provided by compose. - Builds and starts the
agreement-servicegRPC server fromagreementServiceServer/Dockerfile. - Exposes the following ports on the host:
8080: car-rental REST API9090: agreement-service gRPC endpoint
Healthchecks and caveats
- The
docker-compose.dev.ymlincludes healthchecks for Postgres and thecar-rentalservice. The car-rental healthcheck calls the actuator/actuator/healthendpoint. If your runtime image does not includecurl, the healthcheck may fail — in that case either:- install
curlin the runtime image, or - change the healthcheck to use a simple TCP check (nc) or remove it for local development, or
- run the app with a mounted development image that includes curl.
- install
Environment overrides
- You can override any Spring property by passing environment variables in the compose file (already wired for the datasource). For production, prefer using a secret manager or Kubernetes Secrets instead of plain env files.
Docker Compose is suitable for quick local development. For CI and production use Kubernetes manifests in k8s/.
After building and pushing images, update k8s/ manifests if you used different image names/tags and apply them:
kubectl apply -k k8s/Notes
- The Dockerfiles are multi-stage and run the Gradle build inside the builder image. For CI you may prefer building the jar artifact in the CI runner and then creating a smaller runtime image from it.
- The builder images (e.g.
gradle:8.6-jdk21) may trigger vulnerability warnings in scanners; it's recommended to scan images in your CI and choose minimal runtime images for production (distroless, slim, or jlink-based images). - Replace any example credentials or base64 secrets before deploying to production.













