This repository is an example of running a simple Spring Boot Coherence application in Kubernetes.
There are a number of parts to this example, each of the modules is a separate Maven projects. The reason for this is that in the real world each of those modules could be built by a separate development team. Each module may have a different development and deployment lifecycle.
-
Domain Classes
domain-model/
directory. This is a very simple module that just contains the domain classes (Student
andAddress
) used by the example. These classes have been annotated with Coherence POF annotations. During the build the POF Maven plugin will instrument the classes to make the properly implement evolvable portable objects. The classes have also been annotated with JPA annotations so that they can be stored in a database. This module is used by the other modules that require these domain classes. -
Storage Service
storage-service
directory. This is the Coherence storage enabled cluster service. This module provides caches usingCacheStore
implementations to read from and write to the database. The cache stores use plain jdbc code to access the database. -
Spring Boot Storage Service
spring-boot-storage-service
directory. This module provides the same functionality as the Storage Service but has been written as a Spring Boot application that uses JPA in the Cache Stores. -
Student REST Service
rest-service
directory. This is a Spring Boot REST application that provides REST endpoints to perform CRUD operations on Students. This application is a Coherence Extend client application that connects to the storage service.
The examples are Maven projects and should be build with the following commands in this order:
Domain Classes
./mvnw clean install -DskipTests -f domain-model/
Storage Service
./mvnw clean install -DskipTests -f storage-service/
Spring Boot Storage Service
./mvnw clean install -DskipTests -f spring-boot-storage-service/
REST Service
./mvnw clean install spring-boot:build-image -DskipTests -f rest-service/
This example requires an Oracle database to connect to. One way to do this for testing is use the Oracle DB Docker image on Oracle Container Registry, which is simple to start and throw away afterward.
The following database details will be required: * The host name to use to connect to the database * The port to use to connect to the database * The database SID * The user name to use to connect to the database * The password to use to connect to the database
The user must have tables in schema like the ones below:
CREATE USER College IDENTIFIED BY Coherence2020; GRANT CONNECT, RESOURCE, DBA TO College; DROP TABLE COLLEGE.ADDRESS; DROP TABLE COLLEGE.STUDENT; CREATE TABLE COLLEGE.ADDRESS ( ROLL VARCHAR2(255 char) NOT NULL PRIMARY KEY, LINE_ONE VARCHAR2(255 char), LINE_TWO VARCHAR2(255 char), CITY VARCHAR2(255 char), POSTAL_CODE VARCHAR2(255 char), COUNTRY VARCHAR2(255 char) ); CREATE TABLE COLLEGE.STUDENT ( ROLL VARCHAR2(255 char) NOT NULL PRIMARY KEY, FIRST_NAME VARCHAR2(255 char), LAST_NAME VARCHAR2(255 char), CLASS_NAME VARCHAR2(255 char), ADDRESS_ROLL VARCHAR2(255 char) CONSTRAINT STUDENT_ADDRESS REFERENCES COLLEGE.ADDRESS );
The different modules can be run in Docker or in Kubernetes.
First start the Coherence storage application. Choose whether you want to run the plain JDBC storage application or the Spring Boot storage application, ONLY RUN ONE OF THEM.
Run the command below after changing the System properties oracle.db.host
, oracle.db.port
, oracle.db.sid
,
oracle.db.user
, oracle.db.pwd
to match with the values required for your database.
docker run -d -p 20000:20000 \ -e JAVA_TOOL_OPTIONS='-Doracle.db.host=192.168.1.33 -Doracle.db.port=31521 -Doracle.db.sid=ORCLPDB1 -Doracle.db.user=College -Doracle.db.pwd=Coherence2020' \ --name storage coherence-example-storage:1.0.0-SNAPSHOT
Run the command below after changing the environment variables to the values required for your database.
Now run the Spring Boot REST Service. This is a Coherence Extend client application that will connect to the storage application. When we started the storage application we exposed the Coherence Extend port 20000 to port 20000 on the host. We can configure the REST service to connect to Extend on the local machines IP address.
Run the command below after changing the extend.host
system property value to the IP address of the local machine.
docker run -it --rm -p 8080:8080 -e JAVA_TOOL_OPTIONS='-Dextend.host=192.168.1.33' rest-service:1.0.0-SNAPSHOT
We can use curl to execute REST commands. The REST service exposed the container port 8080 to port 8080 on the local machine so we can connect to that port.
curl -i -w '\n' -X GET http://127.0.0.1:8080/student/foo
which should return a 404 error something like this
HTTP/1.1 404 Content-Type: application/json Transfer-Encoding: chunked Date: Thu, 10 Sep 2020 14:00:30 GMT {"timestamp":"2020-09-10T14:00:30.416+00:00","status":404,"error":"Not Found","message":"","path":"/student/foo"}
We can do a POST command to add a Student. The API requires the request body to be a json representation of the Student.
For example:
{ "firstName":"Aamir", "lastName":"Khan", "className":"drama", "address": { "lineOne":"Freeda Apartments", "lineTwo":"Carter Road, Bandra West", "city":"Mumbai", "postalCode":"123456", "country":"India" } }
curl -i -w '\n' -X POST http://127.0.0.1:8080/student \ -H "Content-Type: application/json" \ -d '{"firstName":"Aamir","lastName":"Khan","className":"drama","address":{"lineOne":"Freeda Apartments","lineTwo":"Carter Road, Bandra West","city":"Mumbai","postalCode":"123456","country":"India"}}'
Which should return something like this:
HTTP/1.1 201 Content-Type: application/json Transfer-Encoding: chunked Date: Thu, 10 Sep 2020 14:08:45 GMT {"firstName":"Aamir","lastName":"Khan","className":"drama","address":{"lineOne":"Freeda Apartments","lineTwo":"Carter Road, Bandra West","city":"Mumbai","postalCode":"123456","country":"India","evolvableHolder":{"typeIds":[],"empty":true}},"rollNumber":"3161f377-e98c-4a19-8992-05329699088f","evolvableHolder":{"typeIds":[],"empty":true}}
The json returned will show the roll number that has been created as the Student identifier, in this case 3161f377-e98c-4a19-8992-05329699088f
.
Now we have added a Student we can execute a GET for that Student using the roll number.
curl -i -w '\n' -X GET http://127.0.0.1:8080/student/3161f377-e98c-4a19-8992-05329699088f
Which this time should return a 200 status and the json representation of the Student.
HTTP/1.1 200 Content-Type: application/json Transfer-Encoding: chunked Date: Thu, 10 Sep 2020 14:10:51 GMT {"firstName":"Aamir","lastName":"Khan","className":"drama","address":{"lineOne":"Freeda Apartments","lineTwo":"Carter Road, Bandra West","city":"Mumbai","postalCode":"123456","country":"India","evolvableHolder":{"typeIds":[],"empty":true}},"rollNumber":"3161f377-e98c-4a19-8992-05329699088f","evolvableHolder":{"typeIds":[],"empty":true}}
To run in Kubernetes you still require an Oracle Database as with the Docker example. Again, a simple solution is to run the Oracle DB image in k8s.
As before there are two choices for storage, the plain JDBC storage or the Spring Boot JPA storage, CHOOSE ONLY ONE.
First make sure the Coherence Operator is installed, as this will be required to run the storage cluster.
The following yaml can be used to create a storage cluster using the image built from this project.
apiVersion: coherence.oracle.com/v1 kind: Coherence metadata: name: student-storage spec: annotations: openshift.io/scc: anyuid image: coherence-example-storage:1.0.0-SNAPSHOT jvm: args: - -Doracle.db.host=oracledb.oracle.svc - -Doracle.db.port=1521 - -Doracle.db.sid=ORCLPDB1 - -Doracle.db.user=College - -Doracle.db.pwd=Coherence2020 coherence: metrics: enabled: true management: enabled: true ports: - name: metrics port: 9612 serviceMonitor: enabled: true - name: management port: 30000 - name: extend port: 20000
The yaml above is in the ./k8s/storage-cluster.yaml[] file. Create the storage cluster with the following command:
kubectl create -f ./k8s/storage-cluster.yaml
The following yaml can be used to create the Spring Boot storage cluster using the image built from this project.
apiVersion: coherence.oracle.com/v1 kind: Coherence metadata: name: student-storage spec: annotations: openshift.io/scc: anyuid image: coherence-example-spring-storage:1.0.0-SNAPSHOT env: - name: ORACLE_DB_HOST value: oracledb.oracle.svc - name: ORACLE_DB_PORT value: 1521 - name: ORACLE_DB_SID value: ORCLPDB1 - name: ORACLE_DB_USER value: College - name: ORACLE_DB_PASSWORD value: Coherence2020 coherence: metrics: enabled: true management: enabled: true ports: - name: metrics port: 9612 serviceMonitor: enabled: true - name: management port: 30000 - name: extend port: 20000 readinessProbe: initialDelaySeconds: 10 periodSeconds: 10
The yaml above is in the ./k8s/spring-storage-cluster.yaml[] file. Create the storage cluster with the following command:
kubectl create -f ./k8s/spring-storage-cluster.yaml
The Coherence Operator will have create a K8s Service to expose the Extend proxy, this service will be
called student-storage-extend
. The DNS name created in Kubernetes for this will
be student-storage-extend.<namespace>.svc
where <namespace>
is the name of the namespace the storage cluster
was created in. We can use this to set the extend.host
System property when we run the REST service below.
In this example we assume that the storage cluster is in a namespace called sbi
so the Extend service
name is student-storage-extend.sbi.svc
.
To start the Spring Boot REST Service use the following yaml:
apiVersion: apps/v1 kind: Deployment metadata: name: students-application labels: app: students spec: selector: matchLabels: app: students strategy: type: Recreate template: metadata: labels: app: students spec: containers: - name: students image: rest-service:1.0.0-SNAPSHOT env: - name: JAVA_TOOL_OPTIONS value: "-Dextend.host=student-storage-extend.sbi.svc" ports: - name: rest containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: students spec: type: NodePort selector: app: students ports: - name: rest protocol: TCP port: 8080 targetPort: rest nodePort: 30080
The yaml above will create a Deployment to run the application and a Service to expose the REST endpoint.
This example uses a Service with a type of NodePort, which works well locally in Docker.
If you want to expose the port externally change the Service type from NodePort
to LoadBalancer
.
Create the REST service with the following command:
kubectl create -f ./k8s/rest-service.yaml
When the service starts the REST endpoint will be reachable on port 30080 on the node or load balancer.
The same curl commands can now be executed against this host and port.