Spring Boot & Kubernetes

Spring Boot integrates with Spring Cloud Kubernetes and Spring Cloud Kubernetes Config to make instances of ConfigMaps available at runtime to be injected as any other Spring Boot configuration property.

Spring Cloud Kubernetes Config

Registering

Let’s register the Spring Cloud Kubernetes Config dependency in the build tool:

Open pom.xml file and add the following dependency:

pom.xml
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-kubernetes-fabric8-config</artifactId>
	<version>2.1.4</version>
</dependency>

Code

Then let’s create a Java class that reads Spring Boot config properties:

org.acme.hellokubernetes.HelloConfig
package org.acme.hellokubernetes;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration (1)
@ConfigurationProperties(prefix = "greeting") (2)
public class HelloConfig {

    private String message; (3)

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
1 Sets this POJO as configuration class
2 Sets the prefix of the properties (greeting)
3 Sets the property name (message)

So any property value set in the application.properties with key greeting.message will be injected in the message field.

Create a new endpoint in the HelloController class that uses this class:

org.acme.hellokubernetes.HelloController.java
@Autowired
private HelloConfig helloConfig;

@GetMapping("/greeting/{name}")
public String greeting(@PathVariable("name") String name) {
    return helloConfig.getMessage() + " " + name;
}

Configuration

But since we are in Kubernetes, instead of set this property in application.properties, let’s set it in a ConfigMap:

src/main/resources/k8s/hello-kube.cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: hello-kubernetes (1)
data: (2)
  application.properties: |-
    greeting.message: Aloha
1 ConfigMap name should be the same as spring application name by default.
2 Creates an application.properties entry in the ConfigMap to be appended in application.properties set in the project.

Then we need to configure the Spring Cloud Kubernetes Config setting the name of ConfigMaps to load during bootstrap or the namespace where the ConfigMaps are placed:

application.properties
spring.application.name=hello-kubernetes (1)

spring.cloud.kubernetes.config.name=hello-kubernetes (2)
spring.cloud.kubernetes.config.namespace=default (3)
spring.cloud.kubernetes.config.sources[0].name=hello-kubernetes (4)
spring.cloud.kubernetes.config.enabled=true
1 By default ConfigMap must be named with the name of the Spring application.
2 Sets the ConfigMap name to look up.
3 Sets the namespace where ConfigMap is deployed.
4 In the case of multiple ConfigMaps you can set them using sources array.

The last step before deploying the application is to configure a ServiceAccount with the correct Kubernetes Roles so Spring Cloud Kubernetes Config can query the ConfigMap content using the Kubernetes API. Let’s use one of the features of JKube, that let you define fragments of Kubernetes objects and Jkube will merge all of them during generation phase.

Create a new directory at src/main/jkube and place the following files:

A fragment of a Deployment file that sets the number of replicas and the service account to run the application. JKube will automatically merge these properties with the autogenerated deployment file.

src/main/jkube/deployment.yaml
spec:
  replicas: 1
  template:
    spec:
      serviceAccount: spring-boot

Then create another file that creates the service account:

src/main/jkube/sa.yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: spring-boot

The definition of the role binding:

src/main/jkube/rb.yaml
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: spring-boot-view
roleRef:
  kind: Role
  apiGroup: rbac.authorization.k8s.io
  name: spring-boot-view
subjects:
  - kind: ServiceAccount
    name: spring-boot

And finally the role:

src/main/jkube/role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: spring-boot-view
rules:
  - apiGroups: [""]
    resources: ["pods","configmaps", "services"]
    verbs: ["get", "watch", "list"]

Deploying

Before deplpoying the application, we need to crete the ConfigMap in the Kubernetes cluster:

kubectl apply -f src/main/resources/k8s/hello-kube-cm.yaml -n default
configmap/hello-kubernetes created

And we can now create the container image and deploy it to the cluster.

Create and push the container image of the application:

./mvnw clean package k8s:build k8s:push -DskipTests

Generate the resources:

./mvnw k8s:resource

And finally deploy the application:

kubectl apply -f target/classes/META-INF/jkube/kubernetes.yml -n default
kubectl get pods
NAME                    READY   STATUS    RESTARTS   AGE
hello-kubernetes-96c8574d6-cnbsp   1/1     Running   0          18s

Then port forward the service so it’s accessible from localhost.

kubectl port-forward -n default hello-kubernetes-96c8574d6-cnbsp  8080:8080
serviceaccount/spring-boot created
service/hello-kubernetes created
role.rbac.authorization.k8s.io/spring-boot-view created
rolebinding.rbac.authorization.k8s.io/spring-boot-view created
deployment.apps/hello-kubernetes created

Finally we can curl the service:

curl localhost:8080/greeting/alex
Aloha alex

Clean-Up

Before stepping to the following section, stop the kubectl port-forward process by typing Ctrl+C on the terminal.

Undeploy the service by deleteing all the resources created in the namespace:

kubectl delete all --all -n default