Using Helm and Kubernetes to merge ConfigMap and Secret in a single file

Recently, I encountered an issue with Kubernetes where I couldn’t setup a secret as an environment variable because my service only supported passing this variable as a single configuration file along with non-sensitive data. For example, it is the case for the parameters.yaml file of a Symfony project. In order to address this issue I called Helm to the rescue.

Issue: Kubernetes can't natively merge Secret and ConfigMap in a single file

Let’s say we have this standard yaml configuration file:

Applying Kubernetes concepts we should only store the database password in a Secret and the rest should be in a ConfigMap.

But we wouldn’t be able to merge those two data sources in a single file while using those ConfigMap and Secret as a volume:

Well Kubernetes doesn’t support and does not aim at supporting it: https://github.com/kubernetes/kubernetes/issues/30716

But with the help of Helm and a little bash trick you can achieve it!

Setup: Creating a Helm chart

First use Helm to create your chart:

helm create templating-inception

Let’s say your config files are at the root directory of your repository here’s the directory setup you’ll need:

 

Nothing too exciting except for this symbolic link, we’ll come back to that later but it’s a requirement if you want to keep your configuration files that way.

Helm: Templating the template

We are going to use the templating function of Helm during the templating process of our ConfigMap, here’s the configmap.yaml file you should create:

 

The instruction in this template reads all files suffixed by .tmpl and set them up as filename:filecontent in this ConfigMap.

As you can see we will have to rename our parameters.yaml file parameters.yaml.tmpl and use some Helm templating:

This step isn’t required but it helps when working with multiple environments.

 

Let’s see add our values to the values.yaml file:

 

We’re close achieve our goal but for now, we want Helm to template our parameters.yaml file as follow:

 

NB: The symlink you created before was only there because Helm uses server-side rendering on Tiller to compute the resources it has to deploy and only the chart directory is uploaded to Tiller. For now, Helm doesn’t support referencing paths higher in the hierarchy but with the 3.0.0 version currently in alpha and the disappearance of Tiller this should be achievable.

Bash: Substitutor of environments

The last part is a mix of the envsubst command and the concept of initContainer in Kubernetes.

The description of the envsubst command is as clear as its name: “Substitutes the values of environment variables.”

Let’s use it in our deployment.yaml file:

 

Here’s the shell command launched by the initContainer:

sh -c for file in $(ls -A /tmpl/ | grep tmpl); do newfile=${file%".tmpl"}; envsubst < /tmpl/$file > /app/config/$newfile; done'

 

The $DATABASE_PASSWORD environment variable will be substituted with its value in the parameters.yaml.

Using this template you’ll have to setup those values in your values.yaml file as well:

Here’s your service getting its configuration /config/paramaeters.yaml file with both values from a Secret and a ConfigMap.

This setup will work for any file added in the config directory.

 

Merging a ConfigMap and a Secret in a single file is a complex process which requires knowledge in Helm and Kubernetes if you have any questions or suggestions, don't hesitate to contact us to share your experience with us. 

Alan Longuet

Alan Longuet

Alan is a Site Reliability Engineer at Padok

What do you think? Leave your comments here !