Illustration Image

Cassandra.Link

The best knowledge base on Apache Cassandra®

Helping platform leaders, architects, engineers, and operators build scalable real time data platforms.

10/23/2020

Reading time:7 min

DataStax-Examples/spring-k8s-cassandra-microservices

by DataStax-Examples

This repository contains sample inventory microservices to demonstrate how to use Spring, Kubernetes and Cassandra together a single stack.Contributors:Modules:microservice-spring-boot: Service for ProductsPersistence Layer : uses Cassandra Java driver's CqlSession directly for queries to products tableExposition Layer : uses spring-web @Controllermicroservice-spring-data: Service for OrdersPersistence Layer : uses Spring Data Cassandra for data access to orders tableExposition Layer : uses Spring Data REST for API generationgateway-service: Spring Cloud Gateway to route to the microservices1. ObjectivesShow a working set of microservices illustrating how to build Spring microservices with Kubernetes and Cassandra.This repo leverages Spring modules:spring-dataspring-bootspring-data-restspring-webspring-cloud-kubernetesspring-cloud-gateway2. How this WorksThe primary mode of deployment is on a local Kubernetes cluster, though each service can be run standalone or in Docker.The purpose is to show the many utilities of Spring in Kubernetes with Cassandra as the backing storage tier.The business domain is an inventory / ecommerce application.3. Setup and Running3.a - PrerequisitesThe prerequisites required for this application to runDockerKubernetesJDK 11+Maven3.b - SetupClone the current repositorygit clone https://github.com/DataStax-Examples/spring-k8s-cassandra-microservices.gitStart minikube# use docker as the virtualization driverminikube start --driver=docker --extra-config=apiserver.authorization-mode=RBAC,Node# tell minikube to use local docker registryeval `minikube docker-env`Build the services# from the spring-k8s-cassandra-microservices directorymvn packageBuild the docker imagescd microservice-spring-boot; docker build -t <your-docker-username>/spring-boot-service:1.0.0-SNAPSHOT .cd microservice-spring-data; docker build -t <your-docker-username>/spring-data-service:1.0.0-SNAPSHOT .cd gateway-service; docker build -t <your-docker-username>/gateway-service:1.0.0-SNAPSHOT .Alter deployment.yml files with your docker username# replace image name in deploy/spring-boot/spring-boot-deployment.yml# replace image name in deploy/spring-data/spring-data-deployment.yml# replace image name in deploy/gateway/gateway-deployment.ymlCreate namespaceskubectl create ns cass-operatorkubectl create ns spring-boot-servicekubectl create ns spring-data-servicekubectl create ns gateway-service3.c - Setup DataStax Astra or Cassandra Kubernetes OperatorDataStax AstraCreate a free tier database in DataStax Astra with keyspace name betterbotzDownload the secure connect bundle from the Astra UI (docs)Create secrets for the Astra username/password and secure connect bundleDB_USER=<astra-db-user>DB_PASSWORD=<astra-db-password>SECURE_CONNECT_BUNDLE_PATH=<path-to-secure-connect-bundle>kubectl -n spring-boot-service create secret generic db-secret --from-literal=username=$DB_USER --from-literal=password=$DB_PASSWORDkubectl -n spring-boot-service create secret generic astracreds --from-file=secure-connect-bundle=$SECURE_CONNECT_BUNDLE_PATHkubectl -n spring-data-service create secret generic db-secret --from-literal=username=$DB_USER --from-literal=password=$DB_PASSWORDkubectl -n spring-data-service create secret generic astracreds --from-file=secure-connect-bundle=$SECURE_CONNECT_BUNDLE_PATHChange Spring Boot ConfigMap to use secure connect bundleapiVersion: v1kind: ConfigMapmetadata: name: spring-boot-servicedata: application.yml: |- astra.secure-connect-bundle: /app/astra/credsChange Spring Data ConfigMap to use secure connect bundleapiVersion: v1kind: ConfigMapmetadata: name: spring-data-servicedata: application.yml: |- astra.secure-connect-bundle: /app/astra/credsUncomment the following lines in Spring Boot Deployment.yml and Spring Data Deployment.ymlvolumes: - name: astravol secret: secretName: astracreds items: - key: secure-connect-bundle path: creds...volumeMounts: - name: astravol mountPath: "/app/astra" readOnly: trueYou're ready to go!Cassandra Kubernetes OperatorStart the Cassandra operator# create the storage class for the databasekubectl -n cass-operator apply -f deploy/storage-class.yml# apply the operator manifestkubectl -n cass-operator apply -f https://raw.githubusercontent.com/DataStax-Academy/kubernetes-workshop-online/master/1-cassandra/11-install-cass-operator-v1.1.yaml# start a single C* 4.0 nodekubectl -n cass-operator apply -f deploy/cassandra-4.0.0-1node.ymlCreate the Kubernetes Secrets for database username and password# get the username and password from the secretDB_USER=$(kubectl -n cass-operator get secret cluster1-superuser -o yaml | grep username | cut -d " " -f 4 | base64 -d)DB_PASSWORD=$(kubectl -n cass-operator get secret cluster1-superuser -o yaml | grep password | cut -d " " -f 4 | base64 -d)# create k8s secrets for the services (skip cmd for Spring Boot service if using Astra)kubectl -n spring-boot-service create secret generic db-secret --from-literal=username=$DB_USER --from-literal=password=$DB_PASSWORDkubectl -n spring-data-service create secret generic db-secret --from-literal=username=$DB_USER --from-literal=password=$DB_PASSWORDRunningStart the services# from the spring-k8s-cassandra-microservices directorykubectl -n spring-boot-service apply -f deploy/spring-bootkubectl -n spring-data-service apply -f deploy/spring-datakubectl -n gateway-service apply -f deploy/gatewayExpose the Gateway endpoint# get the gateway-service podGATEWAY_POD=$(kubectl -n gateway-service get pods | tail -n 1 | cut -f 1 -d ' ')# forward the portkubectl -n gateway-service port-forward $GATEWAY_POD 8080:8080Optionally expose the Spring Boot service endpoints (useful for testing)# get the spring-boot-service podBOOT_SERVICE_POD=$(kubectl -n spring-boot-service get pods | tail -n 1 | cut -f 1 -d ' ')# forward the portkubectl -n spring-boot-service port-forward $BOOT_SERVICE_POD 8083:8083Optionally expose the Spring Data service endpoints (useful for testing)# get the spring-data-service podDATA_SERVICE_POD=$(kubectl -n spring-data-service get pods | tail -n 1 | cut -f 1 -d ' ')# forward the portkubectl -n spring-data-service port-forward $DATA_SERVICE_POD 8081:8081Gateway Service endpointsThe Spring Cloud Gateway is running on port 8080 and forwards requeststo the Spring Boot and Spring Data endpoints below. To test that this isworking, you can replace the URLs below with localhost:8080 with the samecurl commands.Spring Boot service endpointsExplore the endpoints with Swagger (only works if endpoints exposed above): http://localhost:8083/swagger-ui.htmlAdd productscurl -X POST -H "Content-Type: application/json" -d '{"name": "mobile", "id":"123e4567-e89b-12d3-a456-556642440000", "description":"iPhone", "price":"500.00"}' http://localhost:8083/api/products/addcurl -X POST -H "Content-Type: application/json" -d '{"name": "mobile", "id":"123e4567-e89b-12d3-a456-556642440001", "description":"Android", "price":"600.00"}' http://localhost:8083/api/products/addGet products with name = mobilecurl http://localhost:8083/api/products/search/mobileGet products with name = mobile and id = 123e4567-e89b-12d3-a456-556642440001curl http://localhost:8083/api/products/search/mobile/123e4567-e89b-12d3-a456-556642440001Delete product with name = mobile and id = 123e4567-e89b-12d3-a456-556642440001curl -X DELETE http://localhost:8083/api/products/delete/mobile/123e4567-e89b-12d3-a456-556642440001Spring Data service endpointsAdd orderscurl -H "Content-Type: application/json" -d '{"key": {"orderId":"123e4567-e89b-12d3-a456-556642440000", "productId":"123e4567-e89b-12d3-a456-556642440000"}, "productName":"iPhone", "productPrice":"500.00", "productQuantity":1, "addedToOrderTimestamp": "2020-04-12T11:21:59.001+0000"}' http://localhost:8081/api/orders/addcurl -H "Content-Type: application/json" -d '{"key": {"orderId":"123e4567-e89b-12d3-a456-556642440000", "productId":"123e4567-e89b-12d3-a456-556642440001"}, "productName":"Android", "productPrice":"600.00", "productQuantity":1, "addedToOrderTimestamp": "2020-04-12T11:22:59.001+0000"}' http://localhost:8081/api/orders/addGet orders with order_id = 123e4567-e89b-12d3-a456-556642440000curl http://localhost:8081/api/orders/search/order-by-id?orderId=123e4567-e89b-12d3-a456-556642440000Get order with order_id = 123e4567-e89b-12d3-a456-556642440000 and product_id = 123e4567-e89b-12d3-a456-556642440000curl "http://localhost:8081/api/orders/search/order-by-product-id?orderId=123e4567-e89b-12d3-a456-556642440000&productId=123e4567-e89b-12d3-a456-556642440000"Get only the product name and price of order_id = 123e4567-e89b-12d3-a456-556642440000curl http://localhost:8081/api/orders/search/name-and-price-only?orderId=123e4567-e89b-12d3-a456-556642440000Shows how to use a projection with Spring Data RESTcurl "http://localhost:8081/api/orders/search/name-and-price-only?orderId=123e4567-e89b-12d3-a456-556642440000&projection=product-name-and-price"Delete order with order_id = 123e4567-e89b-12d3-a456-556642440000 and product_id = 123e4567-e89b-12d3-a456-556642440000curl -X DELETE "http://localhost:8081/api/orders/delete/product-from-order?orderId=123e4567-e89b-12d3-a456-556642440000&productId=123e4567-e89b-12d3-a456-556642440000"Delete order with order_id = 123e4567-e89b-12d3-a456-556642440000curl -X DELETE "http://localhost:8081/api/orders/delete/order?orderId=123e4567-e89b-12d3-a456-556642440000"

Illustration Image

This repository contains sample inventory microservices to demonstrate how to use Spring, Kubernetes and Cassandra together a single stack.

Arch link

Contributors:

Modules:

  • microservice-spring-boot: Service for Products
    • Persistence Layer : uses Cassandra Java driver's CqlSession directly for queries to products table
    • Exposition Layer : uses spring-web @Controller
  • microservice-spring-data: Service for Orders
    • Persistence Layer : uses Spring Data Cassandra for data access to orders table
    • Exposition Layer : uses Spring Data REST for API generation
  • gateway-service: Spring Cloud Gateway to route to the microservices

1. Objectives

Show a working set of microservices illustrating how to build Spring microservices with Kubernetes and Cassandra. This repo leverages Spring modules:

  • spring-data
  • spring-boot
  • spring-data-rest
  • spring-web
  • spring-cloud-kubernetes
  • spring-cloud-gateway

2. How this Works

The primary mode of deployment is on a local Kubernetes cluster, though each service can be run standalone or in Docker.

The purpose is to show the many utilities of Spring in Kubernetes with Cassandra as the backing storage tier.

The business domain is an inventory / ecommerce application.

3. Setup and Running

3.a - Prerequisites

The prerequisites required for this application to run

  • Docker
  • Kubernetes
  • JDK 11+
  • Maven

3.b - Setup

Clone the current repository

git clone https://github.com/DataStax-Examples/spring-k8s-cassandra-microservices.git

Start minikube

# use docker as the virtualization driver
minikube start --driver=docker --extra-config=apiserver.authorization-mode=RBAC,Node
# tell minikube to use local docker registry
eval `minikube docker-env`

Build the services

# from the spring-k8s-cassandra-microservices directory
mvn package

Build the docker images

cd microservice-spring-boot; docker build -t <your-docker-username>/spring-boot-service:1.0.0-SNAPSHOT .
cd microservice-spring-data; docker build -t <your-docker-username>/spring-data-service:1.0.0-SNAPSHOT .
cd gateway-service; docker build -t <your-docker-username>/gateway-service:1.0.0-SNAPSHOT .

Alter deployment.yml files with your docker username

# replace image name in deploy/spring-boot/spring-boot-deployment.yml
# replace image name in deploy/spring-data/spring-data-deployment.yml
# replace image name in deploy/gateway/gateway-deployment.yml

Create namespaces

kubectl create ns cass-operator
kubectl create ns spring-boot-service
kubectl create ns spring-data-service
kubectl create ns gateway-service

3.c - Setup DataStax Astra or Cassandra Kubernetes Operator

DataStax Astra

Create a free tier database in DataStax Astra with keyspace name betterbotz

Download the secure connect bundle from the Astra UI (docs)

Create secrets for the Astra username/password and secure connect bundle

DB_USER=<astra-db-user>
DB_PASSWORD=<astra-db-password>
SECURE_CONNECT_BUNDLE_PATH=<path-to-secure-connect-bundle>
kubectl -n spring-boot-service create secret generic db-secret --from-literal=username=$DB_USER --from-literal=password=$DB_PASSWORD
kubectl -n spring-boot-service create secret generic astracreds --from-file=secure-connect-bundle=$SECURE_CONNECT_BUNDLE_PATH
kubectl -n spring-data-service create secret generic db-secret --from-literal=username=$DB_USER --from-literal=password=$DB_PASSWORD
kubectl -n spring-data-service create secret generic astracreds --from-file=secure-connect-bundle=$SECURE_CONNECT_BUNDLE_PATH

Change Spring Boot ConfigMap to use secure connect bundle

apiVersion: v1
kind: ConfigMap
metadata:
  name: spring-boot-service
data:
  application.yml: |-
    astra.secure-connect-bundle: /app/astra/creds

Change Spring Data ConfigMap to use secure connect bundle

apiVersion: v1
kind: ConfigMap
metadata:
  name: spring-data-service
data:
  application.yml: |-
    astra.secure-connect-bundle: /app/astra/creds

Uncomment the following lines in Spring Boot Deployment.yml and Spring Data Deployment.yml

volumes:
  - name: astravol
    secret:
      secretName: astracreds
      items:
        - key: secure-connect-bundle
          path: creds
...
volumeMounts:
  - name: astravol
    mountPath: "/app/astra"
    readOnly: true

You're ready to go!

Cassandra Kubernetes Operator

Start the Cassandra operator

# create the storage class for the database
kubectl -n cass-operator apply -f deploy/storage-class.yml
# apply the operator manifest
kubectl -n cass-operator apply -f https://raw.githubusercontent.com/DataStax-Academy/kubernetes-workshop-online/master/1-cassandra/11-install-cass-operator-v1.1.yaml
# start a single C* 4.0 node
kubectl -n cass-operator apply -f deploy/cassandra-4.0.0-1node.yml

Create the Kubernetes Secrets for database username and password

# get the username and password from the secret
DB_USER=$(kubectl -n cass-operator get secret cluster1-superuser -o yaml | grep username | cut -d " " -f 4 | base64 -d)
DB_PASSWORD=$(kubectl -n cass-operator get secret cluster1-superuser -o yaml | grep password | cut -d " " -f 4 | base64 -d)
# create k8s secrets for the services (skip cmd for Spring Boot service if using Astra)
kubectl -n spring-boot-service create secret generic db-secret --from-literal=username=$DB_USER --from-literal=password=$DB_PASSWORD
kubectl -n spring-data-service create secret generic db-secret --from-literal=username=$DB_USER --from-literal=password=$DB_PASSWORD

Running

Start the services

# from the spring-k8s-cassandra-microservices directory
kubectl -n spring-boot-service apply -f deploy/spring-boot
kubectl -n spring-data-service apply -f deploy/spring-data
kubectl -n gateway-service apply -f deploy/gateway

Expose the Gateway endpoint

# get the gateway-service pod
GATEWAY_POD=$(kubectl -n gateway-service get pods | tail -n 1 | cut -f 1 -d ' ')
# forward the port
kubectl -n gateway-service port-forward $GATEWAY_POD 8080:8080

Optionally expose the Spring Boot service endpoints (useful for testing)

# get the spring-boot-service pod
BOOT_SERVICE_POD=$(kubectl -n spring-boot-service get pods | tail -n 1 | cut -f 1 -d ' ')
# forward the port
kubectl -n spring-boot-service port-forward $BOOT_SERVICE_POD 8083:8083

Optionally expose the Spring Data service endpoints (useful for testing)

# get the spring-data-service pod
DATA_SERVICE_POD=$(kubectl -n spring-data-service get pods | tail -n 1 | cut -f 1 -d ' ')
# forward the port
kubectl -n spring-data-service port-forward $DATA_SERVICE_POD 8081:8081

Gateway Service endpoints

The Spring Cloud Gateway is running on port 8080 and forwards requests to the Spring Boot and Spring Data endpoints below. To test that this is working, you can replace the URLs below with localhost:8080 with the same curl commands.

Spring Boot service endpoints

Explore the endpoints with Swagger (only works if endpoints exposed above): http://localhost:8083/swagger-ui.html

Swagger Spring Boot

Add products

curl -X POST -H "Content-Type: application/json" -d '{"name": "mobile", "id":"123e4567-e89b-12d3-a456-556642440000", "description":"iPhone", "price":"500.00"}' http://localhost:8083/api/products/add
curl -X POST -H "Content-Type: application/json" -d '{"name": "mobile", "id":"123e4567-e89b-12d3-a456-556642440001", "description":"Android", "price":"600.00"}' http://localhost:8083/api/products/add

Get products with name = mobile

curl http://localhost:8083/api/products/search/mobile

Get products with name = mobile and id = 123e4567-e89b-12d3-a456-556642440001

curl http://localhost:8083/api/products/search/mobile/123e4567-e89b-12d3-a456-556642440001

Delete product with name = mobile and id = 123e4567-e89b-12d3-a456-556642440001

curl -X DELETE http://localhost:8083/api/products/delete/mobile/123e4567-e89b-12d3-a456-556642440001

Spring Data service endpoints

Add orders

curl -H "Content-Type: application/json" -d '{"key": {"orderId":"123e4567-e89b-12d3-a456-556642440000", "productId":"123e4567-e89b-12d3-a456-556642440000"}, "productName":"iPhone", "productPrice":"500.00", "productQuantity":1, "addedToOrderTimestamp": "2020-04-12T11:21:59.001+0000"}' http://localhost:8081/api/orders/add
curl -H "Content-Type: application/json" -d '{"key": {"orderId":"123e4567-e89b-12d3-a456-556642440000", "productId":"123e4567-e89b-12d3-a456-556642440001"}, "productName":"Android", "productPrice":"600.00", "productQuantity":1, "addedToOrderTimestamp": "2020-04-12T11:22:59.001+0000"}' http://localhost:8081/api/orders/add

Get orders with order_id = 123e4567-e89b-12d3-a456-556642440000

curl http://localhost:8081/api/orders/search/order-by-id?orderId=123e4567-e89b-12d3-a456-556642440000

Get order with order_id = 123e4567-e89b-12d3-a456-556642440000 and product_id = 123e4567-e89b-12d3-a456-556642440000

curl "http://localhost:8081/api/orders/search/order-by-product-id?orderId=123e4567-e89b-12d3-a456-556642440000&productId=123e4567-e89b-12d3-a456-556642440000"

Get only the product name and price of order_id = 123e4567-e89b-12d3-a456-556642440000

curl http://localhost:8081/api/orders/search/name-and-price-only?orderId=123e4567-e89b-12d3-a456-556642440000

Shows how to use a projection with Spring Data REST

curl "http://localhost:8081/api/orders/search/name-and-price-only?orderId=123e4567-e89b-12d3-a456-556642440000&projection=product-name-and-price"

Delete order with order_id = 123e4567-e89b-12d3-a456-556642440000 and product_id = 123e4567-e89b-12d3-a456-556642440000

curl -X DELETE "http://localhost:8081/api/orders/delete/product-from-order?orderId=123e4567-e89b-12d3-a456-556642440000&productId=123e4567-e89b-12d3-a456-556642440000"

Delete order with order_id = 123e4567-e89b-12d3-a456-556642440000

curl -X DELETE "http://localhost:8081/api/orders/delete/order?orderId=123e4567-e89b-12d3-a456-556642440000"

Related Articles

cloud
kubernetes
datastax

DataStax Hyper-Converged Database: The Future of Data Infrastructure Is Here | DataStax

Patrick McFadin

7/11/2024

cassandra
kubernetes

Checkout Planet Cassandra

Claim Your Free Planet Cassandra Contributor T-shirt!

Make your contribution and score a FREE Planet Cassandra Contributor T-Shirt! 
We value our incredible Cassandra community, and we want to express our gratitude by sending an exclusive Planet Cassandra Contributor T-Shirt you can wear with pride.

Join Our Newsletter!

Sign up below to receive email updates and see what's going on with our company

Explore Related Topics

AllKafkaSparkScyllaSStableKubernetesApiGithubGraphQl

Explore Further

kubernetes