Technotes

Technotes for future me

Mounting a ConfigMap to an Existing Volume in Kubernetes

In this quick fix, I’ll detail how I FINALLY solved an issue that’s been plaguing me for quite some time: Mounting a single file stored in a ConfigMap into an existing directory in a Docker container on Kubernetes.

Introduction

When I build applications to deploy with Docker, I expose configuration options through environment variables. This makes updating existing deployments simple — update the environment variable of a container and you can update the configuration.

However, many applications deployed with Docker were not originally designed to run in Docker. These applications will often rely on configuration files in a config directory to store and update the configuration of an application.

In this QuickFix, we’ll look at how to use a config map as a configuration file, and how to mount it into an existing config directory without clobbering the directory on the container.

The Problem

Kubernetes has a feature that allows you to add files to a config map and mount the ConfigMap as a directory into a container. The keys in the config map become the file names, and the values in the config map become the files.

As an example, let’s look at a ConfigMap named test that contains two keys, Readme.md and test.txt. The values of those keys are the contents we want in those files.

apiVersion: v1
data:
  Readme.md: '# This is a readme, mounted from a config map'
  test.txt: This is a text file that was mounted from a config map.
kind: ConfigMap
metadata:
  name: test
  namespace: default

We can mount these config maps into a container by creating a volume mount. In the example below, we’ve mounted the config map to the /config path in the container.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test
  namespace: default
spec:
  template:
    spec:
      containers:
      - image: ubuntu:xenial
        volumeMounts:
        - mountPath: /config
          name: vol1
      volumes:
      - configMap:
          name: test
        name: vol1

This will create a new directory called /config inside which will be our Readme.md and test.txt files:

root@test:/# ls
bin  boot  config  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  varroot@test:/# cd configroot@test:/config# ls
Readme.md  test.txtroot@test:/config# cat Readme.md
# This is a readme, mounted from a config maproot@test:/config# cat test.txt
This is a text file that was mounted from a config map

This works well when the directory we want to mount into is empty or does not exist in the container. However, if the directory exists, the mount will overwrite that directory.

Many applications store their application configuration files in the same directory, and often times the developer will only want to override a small number of them.

For example, let’s say my application looks for database configuration in a file called database.yml in the /config subdirectory of my application. The working directory in the container is /usr/src/app and that I’d like to mount a file called database.yml from a ConfigMap into an existing config directory within that working directory. The config directory already contains a configuration file called default_config.yml which I don’t want to change.

Before mounting, the directory looks like this:

config
└── default_config.yml
└── application.yml

What I’d like to happen after injecting the config maps using a volume mount is this:

config
└── default_config.yml
└── application.yml

However, since the default ConfigMap volume mount overwrites the directory, I end up with the following:

config
└── database.yml

Since my application relies on default_config.yml and since the application also looks for the database.yml in the same subdirectory, I need to mount the file from the ConfigMap as a volume without overwriting the entire /config directory.

The Solution

We can use the subPath option when mounting our ConfigMap to tell Kubernetes not to overwrite the entire directory. The subPath option tells Kubernetes to mount the ConfigMap as a subpath within an existing directory, rather than as its own separate volume mount.

We’ll solve this by changing two items in our original file. First, we’ll update the mountPath to point to the specific file location that we want to place the contents of our file into. We’ll also specify that file as a subPath in our mount:

Previously:

volumeMounts:
        - mountPath: /config
          name: vol1

Now:

volumeMounts:
        - mountPath: /usr/src/app/config/database.yml
          name: vol1
          subPath: database.yml

This will ensure that the database.yml is a file and not a directory.

Next, we’ll update our ConfigMap mount to point to the specific key (with the same name as the subPath) as the file, and use that file name as the path we wish to mount to:

Previously:

volumes:
      - configMap:
          name: test
        name: vol1

Now:

volumes:
      - configMap:
          items:
          - key: database.yml
            path: database.yml
          name: test
        name: vol1

The final result looks like the following:

apiVersion: v1
data:
  database.yml: 'credentials-go-here'
kind: ConfigMap
metadata:
  name: config
  namespace: default
---------------
apiVersion: apps/v1
kind: Deployment
metadata:
  name: test
  namespace: default
  template:
    spec:
      containers:
        image: ubuntu:xenial
        name: test
        resources: {}
        volumeMounts:
        - mountPath: /usr/src/app/config/database.yml
          name: vol1
          subPath: database.yml
      volumes:
      - configMap:
          items:
          - key: database.yml
            path: database.yml
          name: config
        name: vol1

Source:
https://medium.com/swlh/quick-fix-mounting-a-configmap-to-an-existing-volume-in-kubernetes-using-rancher-d01c472a10ad

Last updated on 5 Jan 2024
Published on 16 Apr 2021
Edit on GitHub