DEV Community

Akash for MechCloud Academy

Posted on

Part 7: Stop Hardcoding! Managing Configuration with ConfigMaps and Secrets

So far, we have a running Nginx web server exposed to the world via a Service. Our application's state is defined declaratively in YAML files. This is a huge step forward.

But our application is still naive. In the real world, applications need configuration: database connection strings, API keys, feature flags, tuning parameters. Where does this information go?

A common anti-pattern is to hardcode these values directly into the container image. This is a terrible practice for several reasons:

  • Inflexible: A change in the database password requires rebuilding and redeploying the entire container image.
  • Insecure: It bundles sensitive information like API keys with your application code, which might be stored in a less-secure registry.
  • Not Portable: The image is tied to a specific environment (development, staging, production) and its configuration.

Kubernetes provides a robust solution for this: a way to completely decouple configuration from your application code. This is done using two specific objects: ConfigMaps and Secrets.

ConfigMaps: For Your Plain-Text Data

A ConfigMap is a Kubernetes object used to store non-confidential configuration data as key-value pairs. Think of it as a public bulletin board for your application.

  • Use Cases: Application mode (development or production), theme names, public API endpoints, feature flags.
  • Analogy: A ConfigMap is a public notice pinned to the wall inside your Pod's "house." It's for information that anyone is allowed to see.

Let's create a ConfigMap for our application. Create a new file named configmap.yaml:

# configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_COLOR: "blue"
  APP_MODE: "staging"
Enter fullscreen mode Exit fullscreen mode

The data section holds our key-value pairs. It's simple, plain text.

Secrets: For Your Sensitive Data

A Secret is very similar to a ConfigMap but is intended exclusively for sensitive information.

  • Use Cases: Database passwords, API tokens, TLS certificates.
  • Analogy: A Secret is a locked safe inside your Pod's "house." Access is restricted.

Kubernetes treats Secrets differently from ConfigMaps. The data in a Secret is stored in the cluster using base64 encoding.

Important: Base64 is encoding, not encryption. It prevents someone from casually reading the secret if they dump the raw YAML, but it's easily decoded. The real security of Secrets comes from Kubernetes' Role-Based Access Control (RBAC), which controls who (or what) is allowed to read them.

Let's create a Secret. The data values must be base64-encoded. You can generate one easily from your terminal:

# This command will output 'c3VwZXJzZWNyZXRQYXNzd29yZA=='
echo -n 'supersecretPassword' | base64
Enter fullscreen mode Exit fullscreen mode

Now, create a file named secret.yaml with the encoded value:

# secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: app-secret
type: Opaque # Default type for key-value secrets
data:
  DB_PASSWORD: "c3VwZXJzZWNyZXRQYXNzd29yZA=="
Enter fullscreen mode Exit fullscreen mode

Using ConfigMaps and Secrets in a Pod

Now that we have defined our configuration objects, how do we get them into our application?

The most common way is to inject them as environment variables. Most applications are built to read configuration from environment variables, making this a natural fit.

We need to update our deployment.yaml to tell the Pod's container where to find its configuration. Let's modify the spec.template.spec.containers section of our deployment.yaml from Part 5.

Here is the complete, updated deployment.yaml:

# deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx-web-server
        image: nginx
        # This is the new section we are adding
        envFrom:
        - configMapRef:
            name: app-config
        - secretRef:
            name: app-secret
Enter fullscreen mode Exit fullscreen mode

The new envFrom section is an array. It tells the container to:

  1. Go to the ConfigMap named app-config and expose all its key-value pairs as environment variables.
  2. Go to the Secret named app-secret and expose all its key-value pairs as environment variables.

Applying and Verifying Everything

Let's put this into action. Apply all three files. Since we already have the deployment, apply will update it.

# First, create the configuration objects
kubectl apply -f configmap.yaml
kubectl apply -f secret.yaml

# Now, update the deployment to use them
kubectl apply -f deployment.yaml
Enter fullscreen mode Exit fullscreen mode

The deployment will notice the change in its template and automatically create a new Pod with the new configuration.

How can we verify that our environment variables are inside the running container? We can use the kubectl exec command to run a command inside our Pod.

  1. Get your Pod's name:

    kubectl get pods
    # Find the name of your running 'hello-nginx' pod, e.g., hello-nginx-7c789f54b6-abcde
    
  2. Execute the printenv command inside the Pod:
    (Replace <your-pod-name> with the name you just found)

    kubectl exec <your-pod-name> -- printenv
    

You will see a long list of environment variables. If you scroll through them, you will find our new variables!

...
APP_COLOR=blue
APP_MODE=staging
DB_PASSWORD=supersecretPassword
...
Enter fullscreen mode Exit fullscreen mode

Notice that kubectl exec automatically decodes the secret for us when we access it from within the container's environment. It worked! Our application now has access to its configuration without it being hardcoded.

What's Next

We've successfully decoupled our application's configuration from its code, making it more flexible, portable, and secure.

However, our application is still stateless. The Nginx web server doesn't need to save any data. But what if we wanted to deploy a database? If that Pod crashes and Kubernetes recreates it, the new Pod starts with a fresh, empty filesystem. All the data from the old one would be lost forever.

In the next part, we will solve this critical problem by diving into Kubernetes storage. We will learn how to provide persistent data to our applications using PersistentVolumes and PersistentVolumeClaims.

Top comments (0)