When we decided to build a microservices architecture, we chose to use Spring Cloud and Kubernetes as the platform to deploy them. These are good solutions. In this article, “Microservices with Spring Cloud Kubernetes“, we will explore the fairly recent project by Pivotal, Spring Cloud Kubernetes. If you need an introduction to Kubernetes, you might find these articles helpful. Although we will be using other components such as MongoDB, circuit breaker, and communication via WebClient, this article will focus on deployment and configuration in Kubernetes, with mention of the other created functionalities.
What do we need to get started?
- A Kubernetes installation. In our case, we are using Minikube, configured and running.
- Two Spring Boot applications that can communicate via REST.
However, you can find everything you need on our GitHub.
What are we going to build and use?
The example we have created involves the communication between two microservices: a shop that places orders. It’s a very simple example, but it covers the necessary concepts to take advantage of Kubernetes. We will use a Service Discovery and Load Balancer provided by Kubernetes, and we will also use Hystrix as a circuit breaker with fallback capability in case there is an error when making a call to another microservice using WebClient.
In our example, we will use the following:
- Secrets: Kubernetes secrets allow us to store sensitive information such as passwords, OAuth tokens, SSH keys, etc. In our case, we will store the password and username for the MongoDB database.
- ConfigMaps: This object is used to store non-sensitive data in a key-value format. In our case, it will store a text to display the services.
- Load Balancer: In this case, we will use Ribbon to balance the load between two instances.
- Service Discovery: The Kubernetes dependency of Spring Cloud helps us discover deployed instances or services.
Kubernetes and Spring Cloud Example
Maven Dependencies
Here are the necessary dependencies for Kubernetes in the applications:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-kubernetes</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-kubernetes- ribbon</artifactId> </dependency>
In addition, some more dependencies from the Netflix stack, as well as the MongoDB and Spring Cloud versions (Hoxton.SR8), have been added.
Service Discovery and Load Balancer Configuration
To make use of service discovery and the load balancer with Ribbon, we need to enable them in the main class of the application:
@RibbonClient(name = "order-service", configuration = RibbonConfiguration.class) @EnableDiscoveryClient
With the above annotations in the Spring project’s main class, we have enabled the load balancer with Ribbon to balance the requests between instances.
ConfigMap and Secrets Configuration
In Spring Cloud, we can use the Config Server to store non-sensitive information and Vault to store secrets or sensitive information like passwords. However, with Spring Cloud Kubernetes, we can utilize Kubernetes’ Secrets and ConfigMaps objects for this type of information.
ConfigMap
To make use of Kubernetes’ ConfigMap, we first need to deploy a YAML file like this:
apiVersion: v1 kind: ConfigMap metadata: name: shop-service data: application.properties: |- configmap.message=Services : %s
Then, to read the information from this file (from the data
section), we need to map it to the application:
@Configuration @ConfigurationProperties(prefix = "configmap") public class ShopConfig { private String message = "Services : %s";
In this way, we will be able to display the text from the configmap in the message field. It is important to note that the name of the configmap must match the name of the service for it to be accessible.
kubectl apply -f configmap.yaml
Secrets
As we have mentioned, secrets in Kubernetes are a type of object where we store sensitive information, similar to how we would use Vault with Spring.
To create objects for sensitive information such as the username and password for MongoDB, we can create a Secrets object as follows:
apiVersion: v1 kind: Secret metadata: name: db-secret data: username: dXNlcg== password: cDQ1NXcwcmQ=
Once we have deployed this object, we can use this information wherever it is needed.
kubectl apply -f secret.yaml
Using Kubernetes Secrets
Once we have deployed the secrets, let’s see how to use them with a MongoDB image in Kubernetes:
apiVersion: apps/v1 kind: Deployment metadata: name: mongo spec: replicas: 1 selector: matchLabels: service: mongo template: metadata: labels: service: mongo name: mongodb-service spec: containers: - args: - mongod image: mongo:latest name: mongo env: - name: MONGO_INITDB_ROOT_USERNAME valueFrom: secretKeyRef: name: db-secret key: username - name: MONGO_INITDB_ROOT_PASSWORD valueFrom: secretKeyRef: name: db-secret key: password
kubectl create -f mongo-deployment.yaml
In the above object, we are using the username and password created in the secret. It is important to note that the secret’s name should match the name of the deployed secret, which in our case is “db-secret.”
Configuring the Service to Use Secrets
Once we have deployed both the secrets object and the deployment that uses the secrets (in our case, MongoDB), we need to configure the application to apply the secrets:
spring: application: name: order-service cloud: kubernetes: reload: enabled: true secrets: name: db-secret data: mongodb: host: mongodb-service port: 27017 database: admin username: ${MONGO_USERNAME} password: ${MONGO_PASSWORD}
In this configuration, we specify where the secrets are located, and we do not write the actual username and password.
Configuring Role-Based Access
As a final configuration step, without which our applications would not function properly, we need to set up role-based access within the cluster. This ensures that a Pod running with spring-cloud-kubernetes has access to the Kubernetes API and can, for example, retrieve all services.
kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: namespace: default name: namespace-reader rules: - apiGroups: ["", "extensions", "apps"] resources: ["configmaps", "pods", "services", "endpoints", "secrets"] verbs: ["get", "list", "watch"] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: namespace-reader-binding namespace: default subjects: - kind: ServiceAccount name: default apiGroup: "" roleRef: kind: Role name: namespace-reader apiGroup: ""
Deploying Spring Cloud Application on Kubernetes
Once we have created all our Kubernetes objects such as services, secrets, configmaps, and deployments, it’s time to create the deployments for the applications we have developed in Spring.
First, we’ll have the service to expose an application running in a set of Pods.
apiVersion: v1 kind: Service metadata: name: order-service spec: selector: app: order-service ports: - protocol: TCP port: 8080 nodePort: 30081 type: NodePort externalIPs: - 1.2.4.120
Next, we’ll have the deployment for the application itself. However, before that, we need to create the Docker image for the application. This deployment will reference the service, and the following deployment definition will include the MongoDB username and password as variables, which we have specified in the application.yaml file of the application:
apiVersion: apps/v1 kind: Deployment metadata: name: order-service spec: selector: matchLabels: app: order-service replicas: 2 template: metadata: labels: app: order-service spec: containers: - name: order-service image: order-service:latest imagePullPolicy: Never ports: - containerPort: 8080 env: - name: MONGO_USERNAME valueFrom: secretKeyRef: name: db-secret key: username - name: MONGO_PASSWORD valueFrom: secretKeyRef: name: db-secret key: password
This deployment references the “order-service” image, which should have been created beforehand using a Dockerfile within the application.
kubectl create -f deployment.yaml
Verifying the Proper Functioning of All Pods
We can verify that our pods are running by executing the following command:
kubectl get pods
or alternatively,
kubectl get po
If the status is not “Running,” we can execute
kubectl logs -f <pod-name>
to view the application logs.
If everything is correct, we can open a terminal and run
minikube service shop-service
to launch the browser and see what it displays.
Conclusion
In this Microservices with Spring Cloud Kubernetes tutorial, we have seen how to quickly and effectively set up an architecture using the key features provided by Kubernetes. You can find the entire project on our GitHub.
If you need more information, you can leave us a comment or send an email to refactorizando.web@gmail.com You can also contact us through our social media channels on Facebook or twitter and we will be happy to assist you!!