Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 85

ConfigMaps and Secrets in Kubernetes

Recommended resources

Hello! Welcome to the next lesson in our Kubernetes module.

In our previous session, we successfully exposed our Spring Boot application to both internal and external traffic using Kubernetes Service objects. While our application is now running and reachable, its configuration is still hardcoded in the Deployment manifest. This approach is inflexible and insecure, violating the "Config" principle of the Twelve-Factor App methodology, which states that configuration should be strictly separated from code.

Today, we'll fix that. This lesson is dedicated to the learning outcome: Manage application configuration in Kubernetes using ConfigMaps and Secrets. We will explore Kubernetes-native objects designed specifically for this purpose, allowing you to externalize your application's configuration and handle sensitive data securely. This is a fundamental skill for building production-ready applications on Kubernetes and a common topic in senior engineering interviews.

1. The Core Idea: Decoupling Configuration

In a typical Spring Boot application, you manage configuration in application.properties or application.yml files, often using profiles for different environments (dev, staging, prod). When moving to Kubernetes, the goal is to externalize this configuration from the container image itself. This allows the same image to be deployed across multiple environments, each with its own specific configuration.

Kubernetes provides two primary objects for this:

  • ConfigMaps: Used to store non-sensitive configuration data as key-value pairs. Think of things like logging levels, feature flags, or external service URLs.
  • Secrets: Designed specifically for sensitive data like database passwords, API keys, and TLS certificates. The data is stored Base64-encoded, and Kubernetes provides stricter access controls around Secrets.

It's crucial to understand that Base64 encoding is not encryption. It's an encoding scheme to ensure the data can be safely transmitted. The security of Secrets comes from Kubernetes' Role-Based Access Control (RBAC) and the potential for encryption-at-rest within the cluster's data store (etcd).

Kubernetes ConfigMap and Secret Manifests with Deployment Flow
This image provides a high-level overview of ConfigMap and Secret manifests and how they relate to a Pod within a Kubernetes cluster.

To understand the internal mechanics and security considerations in more detail, the following article provides an excellent deep-dive.

The Mechanics of Spring Boot Integration with Kubernetes

The article 'The Mechanics of Spring Boot Integration with Kubernetes' explains how ConfigMaps and Secrets work under the hood.

Read the sections 'How ConfigMaps Work in Kubernetes' and 'Secrets for Managing Sensitive Data'. Focus on how they are stored in etcd, the difference in data handling (plaintext vs. Base64), and the two main ways they can be injected into a Pod (as mounted volumes or environment variables).


2. The Native Way: Injecting Configuration as Environment Variables

One of the most common methods for consuming ConfigMaps and Secrets is by injecting their values as environment variables directly into your application's container. Spring Boot automatically picks up environment variables and makes them available in the Environment, overriding properties from application.properties.

This approach is straightforward and doesn't require any changes to your application code. Let's see how it works in a practical scenario involving a database connection.

Kubernetes Tutorial | ConfigMap & Secrets Implementation in Spring Boot CRUD Example | JavaTechie

The video 'ConfigMap & Secrets Implementation in Spring Boot' by Java Techie provides a clear, step-by-step demonstration of this process.

Watch the video from the beginning to 10:48. The instructor will walk you through: The 'Why': Why hardcoding configuration is bad (0:00 - 2:05). Creating a ConfigMap: For non-sensitive data like DB host and name (2:05 - 4:16). Creating a Secret: For sensitive DB username and password, including how to Base64 encode them (4:16 - 5:58). Modifying the Deployment: Updating the manifest to reference the ConfigMap and Secret using configMapKeyRef and secretKeyRef (5:58 - 10:48). Pay close attention to the YAML syntax for referencing keys from these external objects.

As demonstrated in the video, the process involves three key steps:

  1. Define a ConfigMap YAML for non-sensitive key-value pairs.
  2. Define a Secret YAML for sensitive data, ensuring values are Base64 encoded. You can do this easily with echo -n 'your-password' | base64.
  3. Update your Deployment manifest to populate environment variables from these resources.

Here's a condensed example of what the env section in your Deployment would look like:

# In your Deployment manifest's container spec
env:
  - name: SPRING_DATASOURCE_URL
    valueFrom:
      configMapKeyRef:
        name: db-config  # Name of your ConfigMap
        key: database_url # Key within the ConfigMap
  - name: SPRING_DATASOURCE_USERNAME
    valueFrom:
      secretKeyRef:
        name: db-secret   # Name of your Secret
        key: username    # Key within the Secret
  - name: SPRING_DATASOURCE_PASSWORD
    valueFrom:
      secretKeyRef:
        name: db-secret
        key: password

This method is powerful because it's language-agnostic and aligns perfectly with Kubernetes' declarative nature. However, it has one major limitation: environment variables are static. If you update the ConfigMap or Secret, the changes will not be reflected in running Pods. You would need to trigger a restart of the Pods to pick up the new values.


3. Alternative: Mounting Configuration as a Volume

Another popular method is to mount a ConfigMap as a file or directory inside the container. This is particularly useful when you want to provide a complete application.properties file to your Spring Boot application.

Kubernetes ConfigMaps and Secrets with Spring Boot
This diagram shows the two primary ways a Pod can consume configuration: as environment variables from a ConfigMap or as a mounted volume from a Secret.

The key advantage of the volume mount approach is that the mounted files are updated automatically when the ConfigMap is changed. The application still needs a way to reload the configuration, but the underlying mechanism for delivering the change is already in place.

Production Considerations for Spring on Kubernetes

The article 'Production Considerations for Spring on Kubernetes' provides a great example of mounting an application.properties file from a ConfigMap.

Read the section starting with 'For external configuration, you can fairly easily create a ConfigMap...'. It shows the kubectl command to create a ConfigMap from a file and the volumeMounts YAML needed in your Deployment.

As the article shows, you would:

  1. Create a ConfigMap from your application.properties file:
    kubectl create configmap my-spring-config --from-file=application.properties
  2. Mount it into your Pod using volumes and volumeMounts in the Deployment manifest.

While the file updates automatically, a standard Spring Boot application won't reload its configuration just because a file changed. To apply the changes, you would typically trigger a zero-downtime rolling restart:
kubectl rollout restart deployment/your-deployment-name

This brings us to a more advanced, Spring-aware solution.


4. Dynamic Reloading with Spring Cloud Kubernetes

What if you could update a configuration property and have your running Spring Boot application reload it without restarting the Pod? This is possible with Spring Cloud Kubernetes.

This library integrates your Spring Boot application more deeply with the Kubernetes API, enabling features like dynamic configuration reloading.

Spring Cloud Kubernetes | ConfigMaps | Config Server in Kubernetes | Tech Primers

The video 'Spring Cloud Kubernetes | ConfigMaps' from Tech Primers demonstrates how to enable dynamic configuration reloading.

This video shows the end-to-end process. Focus on these key parts: Dependencies: Adding spring-cloud-starter-kubernetes-config (2:32 - 4:10). Bootstrap Config: Using bootstrap.yml to tell Spring which ConfigMap to use on startup (13:06 - 15:05). The Refresh: Seeing how to update the ConfigMap and trigger a refresh using the /actuator/refresh endpoint without a Pod restart (17:40 - 19:23). Security Note: The brief but important mention of RBAC requirements at the end (19:23 - 20:13).

The key takeaways from this advanced approach are:

  • You add a specific Spring Cloud Kubernetes dependency to your project.
  • You configure a bootstrap.yml file to enable the config integration and point to your ConfigMap.
  • You enable the /actuator/refresh endpoint.
  • When the ConfigMap is updated, you can send a POST request to /actuator/refresh, and beans annotated with @ConfigurationProperties or @RefreshScope will be re-initialized with the new values.

This gives you fine-grained control to update configuration on-the-fly, which is extremely powerful for managing applications in production.

Test your understanding! (Interview Question)

You are tasked with changing the logging level of a critical, high-traffic Spring Boot microservice running in Kubernetes from INFO to DEBUG to troubleshoot a production issue. You must do this without causing any service downtime. Describe two distinct methods to achieve this, and discuss the pros and cons of each.

Show answer

This is an excellent question that tests practical knowledge of Kubernetes operations and Spring Boot integration.

Method 1: Native Kubernetes with Rolling Restart

  1. Action: The logging level is stored in a ConfigMap (e.g., in a key logging.level.org.springframework=INFO). You would update the ConfigMap to set the value to DEBUG (kubectl apply -f updated-configmap.yaml). Then, you trigger a rolling restart of the deployment: kubectl rollout restart deployment/my-service.
  2. Pros:
    • Simple and reliable. It's a standard, well-understood Kubernetes workflow.
    • Requires no special dependencies in the application, making it language-agnostic.
  3. Cons:
    • It's a "heavy" operation. Even though a rolling update provides zero downtime by replacing Pods one by one, it still involves terminating old Pods and starting new ones. This consumes resources and can be slow for complex applications.

Method 2: Spring Cloud Kubernetes with Dynamic Refresh

  1. Action: Assuming the service includes the spring-cloud-kubernetes-config starter and has the /actuator/refresh endpoint enabled. You would first update the ConfigMap as before. Then, instead of restarting the Pods, you would send an empty POST request to the /actuator/refresh endpoint of each service instance.
  2. Pros:
    • Extremely fast and lightweight. The change is applied within the running JVM without restarting the Pod or container.
    • Less disruptive to the service and the cluster.
  3. Cons:
    • Requires a specific dependency (spring-cloud-kubernetes-config) and initial setup in the application (bootstrap.yml).
    • The application's service account needs RBAC permissions to "watch" ConfigMaps from the Kubernetes API server, which adds a layer of security and infrastructure complexity.

A strong candidate would be able to articulate both methods, demonstrating a grasp of both fundamental Kubernetes principles and advanced, framework-specific integrations.


Conclusion

You have now mastered a critical aspect of running applications on Kubernetes: externalized and secure configuration management. This practice is essential for building scalable, maintainable, and secure microservices.

Key Takeaways:

  • Decouple Configuration: Always separate configuration from your application image using ConfigMaps and Secrets.
  • Use the Right Tool: Use ConfigMaps for general configuration and Secrets for sensitive data like passwords and tokens.
  • Know Your Injection Methods: You can inject configuration as environment variables (simple, static) or as mounted volumes (enables dynamic updates).
  • Embrace Dynamic Reloading: For production-grade applications, leverage libraries like Spring Cloud Kubernetes to enable hot-reloading of configuration without restarting Pods, minimizing disruption.

Preview of the Next Lesson:
Our application is now deployed, exposed, and properly configured. But how does Kubernetes know if our application is actually healthy and ready to receive traffic? In the next lesson, "Implement liveness and readiness probes for a Spring Boot application to improve service reliability," we will explore how to configure health checks that allow Kubernetes to automatically manage our application's lifecycle, restarting failed instances and routing traffic away from unready ones.