Tutorials

Spring Cloud Kubernetes

Spring Cloud Kubernetes provides implementations of Spring Cloud interfaces to allow developers to build and run Spring Cloud applications within Kubernetes environments.

We will now explain a series of steps to be able to include and use Spring Cloud Kubernetes within our projects.

Add the dependency

The first step will be to include the following dependency in our project’s «pom.xml»:

<dependency> 
       <groupId>org.springframework.cloud</groupId> 
       <artifactId>spring-cloud-starter-kubernetes-config</artifactId> 
</dependency> 

Project settings

Once the dependency has been added, we need to configure a couple of files in our project to be able to use the functionalities that Spring Cloud Kubernetes offers:

Creating the Kubernetes files

The config-map are Kubernetes files that allow us to define key-value pairs that we can then use to configure our deployments. An example of a config-map would be the following one:

apiVersion: v1 by d 
kind: ConfigMap 
metadata: 
  name: onesait-cloudkubernetes 
data: 
  application.properties: |- 
    example.message= Testing reload! Message from backend is: %s <br/> Services : %s 

Once our config-map is defined, we can import it into our deployment:

onesait-cloudkubernetes.yaml

kind: Deployment 
apiVersion: apps/v1 
metadata: 
  name: onesait-cloudkubernetes 
  labels: 
    app: onesait-cloudkubernetes 
    group: architecture 
spec: 
  replicas: 1 
  selector: 
    matchLabels: 
      app: onesait-cloudkubernetes 
  template: 
    metadata: 
      creationTimestamp: null 
      labels: 
        app: onesait-cloudkubernetes 
    spec: 
      containers: 
        - name: app-back 
          image: >- 
            solucionesregistry.azurecr.io/architecture/cloud-kubernetes-example:dev 
          ports: 
            - name: 8080tcp8080 
              containerPort: 8080 
              protocol: TCP 
          envFrom: 
            - configMapRef: 
                name: onesait-cloudkubernetes 
          resources: 
            limits: 
              cpu: 500m 
              memory: 800Mi 
            requests: 
              cpu: 250m 
              memory: 500Mi 
          terminationMessagePath: /dev/termination-log 
          terminationMessagePolicy: File 
          imagePullPolicy: Always 
      restartPolicy: Always 
      terminationGracePeriodSeconds: 30 
      dnsPolicy: ClusterFirst 
      securityContext: {} 
      imagePullSecrets: 
        - name: solutionsregistry 
      schedulerName: default-scheduler 
  strategy: 
    type: Recreate 
  revisionHistoryLimit: 10 
  progressDeadlineSeconds: 600 

Java client configuration

In our project, we have several ways to collect these environment variables that we define in the config-map. The recommended way would be to define a configuration class that collects the variables and access them through said class:

ClientConfig.java  

@Configuration 
@ConfigurationProperties(prefix = "example") 
public class ClientConfig { 
 
    private String message = "Message from backend is: %s <br/> Services : %s"; 
    // getters and setters 
} 

In this example, we are specifying that we are going to have the variable «example.message», which has a default value, but, in case a new value is specified in our configuration files, that new value would be used.

Defining this configuration class, when the project starts, it will look whether than variable exists, then it will obtain the value that has been defined for it. In this case the value is the one that has been defined in the config-map.

Definiendo esta clase de configuración, el proyecto al arrancar buscará si existe dicha variable y obtendrá el valor que se le haya definido, en este caso el valor es el que ha sido definido en el config-map

Problems with roles

There is a chance that, when starting the application, we are notified that our Service Account does not have permissions to access the config-maps or the pods. To fix this, we need to create a new role with access to the following resources:

role-example.yaml

kind: Role 
apiVersion: rbac.authorization.k8s.io/v1 
metadata: 
  name: example 
rules: 
  - verbs: 
      - get 
      - watch 
      - list 
    apiGroups: 
      - '' 
    resources: 
      - configmaps 
      - pods 

And then create a role-binding of our Service Account, with the new role that has been created:

role-binding-example.yaml

kind: RoleBinding 
apiVersion: rbac.authorization.k8s.io/v1 
metadata: 
  name: role-binding-example 
subjects: 
  - kind: ServiceAccount 
    name: default 
    namespace: onesait-caregiver 
roleRef: 
  apiGroup: rbac.authorization.k8s.io 
  kind: Role 
  name: example 

Variable reloading at runtime

Spring Cloud Kubernetes offers us the possibility that our project reloads the environment variables imported from the configmap every time they are changed, so that the execution is not interrupted and the changes are applied automatically. To do this, we need to define the following lines in our «application.yml»: 

application.yml  

spring.cloud.kubernetes.reload.enabled: true 
spring.cloud.kubernetes.reload.mode: event 
spring.cloud.kubernetes.reload.strategy: refresh 

With this setting, and collecting the variables in a configuration file like the one explained above, every time the config-map is changed, our application will reload and update the values of the program’s internal variables.

✍🏻 Author(s)

Leave a Reply