AdBlock Detected

It looks like you're using an ad-blocker!

Our team work realy hard to produce quality content on this website and we noticed you have ad-blocking enabled. Advertisements and advertising enable us to continue working and provide high-quality content.

Externalized configuration with Kubernetes

In this new entry of Refactorizando, we will see how to externalize Spring Boot config to configmap with Kubernetes. Similar to Spring Cloud, we can externalize our configuration in a Config Server with different profiles, for example, for different environments. By using Kubernetes ConfigMap, we can achieve the same result.

In this entry, we will explain step by step, using an example, how to connect our Spring Boot application with a ConfigMap in Kubernetes, allowing us to externalize our configuration and change it directly in Kubernetes. Additionally, if we add a Bus to make our configuration changes instantly effective, it would be perfect.

If you want to see the complete example, you can find it on our GitHub.

What is a Kubernetes ConfigMap?

A Kubernetes ConfigMap can be defined as a key-value dictionary used to store non-critical values in terms of security. Usually, it is used to store information related to pod configuration or values that you do not want to have in your application, so that this information is not confidential.

Configuring ConfigMap with Spring Boot

Externalize Spring Boot config to configmap with Kubernetes
Project structure for externalizing Spring Boot configuration to Kubernetes with ConfigMap

The first thing we are going to do is to add the necessary Maven dependencies:

Maven dependencies with Spring Boot and ConfigMap

The following dependency will add the necessary dependencies to connect our application.

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

Creating a ConfigMap to use it with Spring Boot

To include an application.yml in a ConfigMap, you must create the ConfigMap using the Kubernetes syntax (Kind: ConfigMap) and include the application.yml or application.properties inside the data section. The following shows how to do it:

kind: ConfigMap
apiVersion: v1
metadata:
  name: order-service
data:
  application.yml: |-
    server:
      port: 8080
    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}
    management:
      endpoint:
        health:
          enabled: true
        info:
          enabled: true
        restart:
          enabled: true

In case you want different profiles, profiles will be established in the default way of Spring in the files, for example:

kind: ConfigMap
apiVersion: v1
metadata:
  name: order-service
data:
  application.yml: |-
    greeting:
      message: Say something
    farewell:
      message: I Say Goodbye
    ---
    spring:
      profiles: development
    greeting:
      message: Say something
    farewell:
      message: I say hi

One alternative is to use different applications for each profile, such as application-dev.yml or application-pro.yml.

Configuración de los ficheros de Configuración de Spring

In order to make use of externalized configuration using a ConfigMap, we must configure it through spring.cloud.kubernetes.config.sources. This configuration should be included in our bootstrap.properties or bootstrap.yml file. In this file, we must provide the name of the ConfigMap and the namespace.

If the ConfigMap name and namespace are not provided, the application will take the default namespace in which it is deployed, and the ConfigMap will be named after the application.

Other configuration properties in our bootstrap.yml file will include:

spring.cloud.kubernetes.config.enabledBooleanActivates or deactivates the configuration with ConfigMap, by default it’s TRUE.
spring.cloud.kubernetes.config.nameStringThe name of the ConfigMap, by default, is Spring.application.name.
spring.cloud.kubernetes.config.namespaceStringSets the namespace name, by default where it is deployed.
Configmap properties

Next, we are going to write our bootstrap.yml:

spring:
  application:
    name: order-service
  cloud:
    kubernetes:
      config:
        enabled: true

In our previous file, we activated the ConfigMap configuration, but it is not necessary as it is activated by default when the Maven dependency is added. We did not specify the name of the ConfigMap or the namespace, so it will take the name of “order-service” and the namespace will be the default where it is deployed.

Using ConfigMap properties in Spring Boot

After adding the necessary configuration, the ConfigMap will work like an application.properties or application.yml in our configuration. To see this, we simply add a @Value in our controller:

    private final OrderRepository orderRepository;

    @Value("${spring.data.mongodb.username}")
    private String username;

    @Value("${spring.data.mongodb.password}")
    private String password;

    @GetMapping("/orders")
    public ResponseEntity<List<Order>> getOrders() {

        return new ResponseEntity<>(orderRepository.findAll(), HttpStatus.OK);

    }

    @PostMapping("/orders")
    public ResponseEntity<Order> saveOrder(@RequestBody Order order) {

        return new ResponseEntity<>(orderRepository.save(order), HttpStatus.CREATED);

    }

    @GetMapping("/")
    public ResponseEntity<String> info() throws UnknownHostException {

        String serviceInfo = "Host: " + InetAddress.getLocalHost().getHostName() + "<br/>"
                + "IP: " + InetAddress.getLocalHost().getHostAddress() + "<br/>"
                + "Type: " + "Order Service" + "<br/>"
                + "Connecting to mongodb with :  username:  " + username + " password: " + password;
        return new ResponseEntity<>(serviceInfo, HttpStatus.OK);
    }
}

The @Value we have indicated in the controller will work identically to when the application.yml is in the project.

Reloading properties with Spring Cloud in Kubernetes

One of the functionalities provided by Spring Cloud is the ability to change the properties in our configuration files (ConfigMap or secrets) and apply them to our application instantly. To do this, we will add the following configuration to our Bootstrap.yml:

spring:
  application:
    name: order-service
  cloud:
    kubernetes:
      config:
        enabled: true
      reload:
        enabled: true
        monitoring-config-maps: true
        strategy: refresh
        mode: event

Los campos que se van encargar de dotar a nuestra aplicación estos cambios en caliente son:

spring.cloud.kubernetes.reload.enabledtrue/falseYou can enable or disable property reload.
spring.cloud.kubernetes.reload.monitoring-config-maps true/falseit is activated if changes to the configmap are monitored
spring.cloud.kubernetes.reload.strategyrefresh/restart_contextRefresh strategy only affects the bean annotated with @RefreshScope. Restart_context reloads the application.
spring.cloud.kubernetes.reload.modeevent/pollIn Event, a modification event is generated with each change. With Poll, it checks for changes at certain intervals.
Spring Cloud Config Properties

It is important to note that if we activate the refresh strategy, we must add the @RefreshScope annotation, for example:

@Configuration(proxyBeanMethods = false)
@ConfigurationProperties(prefix = "messages")
@RefreshScope
@Getter
@Setter
public class MeesageProperties {

  private String message;


}

In the above class, it is indicated that every time the configmap has a change in “message”, that value will be automatically captured by the application without any downtime or the need for Kubernetes to perform a rollout of our service.

Example of externalizing Spring Boot configuration in Kubernetes with ConfigMap

Next, we will develop an example in which we will deploy a MongoDB database whose keys will be in a Kubernetes secret. Our Spring Boot application will connect to this MongoDB using a ConfigMap that we will deploy in our Kubernetes Cluster. You can follow the example on our GitHub.

To begin, we will deploy our database and secrets:

apiVersion: v1
kind: Service
metadata:
  labels:
    app: mongo
  name: mongodb-service
spec:
  type: NodePort
  ports:
    - name: "http"
      port: 27017
      protocol: TCP
      targetPort: 27017
  selector:
    service: mongo
---
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
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
data:
  username: dXNlcg==
  password: cDQ1NXcwcmQ=

To deploy both files in Kubernetes, we will do:

Kubectl apply -f mongo-deployment.yaml
kubectl apply -f secrets.yaml

Next, we will deploy our ConfigMap.yaml file, as we discussed and explained earlier, this file will have the configuration of our application.yml:

kind: ConfigMap
apiVersion: v1
metadata:
  name: order-service
data:
  application.yml: |-
    server:
      port: 8080
    spring:
      application:
        name: order-service
      cloud:
        kubernetes:
          reload:
            enabled: true
        secrets:
          name: db-secret
      data:
        mongodb:
          host: mongodb-service
          port: 27017
          database: admin
          username: dXNlcg==
          password: cDQ1NXcwcmQ=
    management:
      endpoint:
        health:
          enabled: true
        info:
          enabled: true
        restart:
          enabled: true


As before, we will do:

Kubectl apply -f configmap.yaml

Finally, we will configure our bootstrap.yml. Activating Kubernetes is not necessary since having the Maven dependency adds the ConfigMap configuration by default.

spring:
  application:
    name: order-service
  cloud:
    kubernetes:
      config:
        enabled: true

We cannot forget to add our service account for communication between the service and our database.

And we add our service account for communication with the database.

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: ""

The next step would only be to create the Spring Boot application normally. And deploy it on Kubernetes. To do this, let’s first create our Docker image:

Create Docker image: docker build -t order-service .

Deploy the image on Kubernetes, for which we will create a deployment and a service:

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
---
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

And in the same way as before with kubectl apply -f deployment.yml, we would have our service up and running.

Remember to execute all Kubernetes files with the command kubectl apply -f <filename>

To test it, you can run the following command:

minikube service order-service

Conclusión

Spring Cloud offers us the ability to work with externalized configuration. In this post, we have seen how to externalize Spring Boot config to configmap with Kubernetes.

If you liked it, don’t hesitate to leave a comment, and if you have any questions, feel free to ask.

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!!

Leave a Reply

Your email address will not be published. Required fields are marked *