Learn how to configure Kubernetes apps using ConfigMap
Tags: kubernetes, tutorial, showdev, beginners
Published: 2019-09-18 13:35:37

"Separation of configuration from code" is one of the tenets of the [12-factor applications](https://12factor.net/config). We externalize things which can change and this in turn helps keep our applications portable. This is critical in the Kubernetes world where our applications are packaged as Docker images. A Kubernetes `ConfigMap` allows us to abstract configuration from code and ultimately the Docker image. 

This blog post will provide a hands-on guide to app configuration related options available in Kubernetes.

As always, the code is [available on GitHub](https://github.com/abhirockzz/kubernetes-in-a-nutshell). So let's get started....


![](https://media.giphy.com/media/l2SpYDOZmp3H2cAAo/giphy.gif)

To configure your apps in Kubernetes, you can use:

- Good old environment variables
- `ConfigMap`
- `Secret` — this will be covered in a subsequent blog post

You will need a Kubernetes cluster to begin with. This could be a simple, single-node local cluster using [`minikube`](https://kubernetes.io/docs/setup/learning-environment/minikube/), [`Docker for Mac`](https://blog.docker.com/2018/01/docker-mac-kubernetes/) etc. or a managed Kubernetes service from [Azure (AKS)](https://docs.microsoft.com/azure/aks/?WT.mc_id=devto-blog-abhishgu), [Google](https://cloud.google.com/kubernetes-engine/), [AWS](https://aws.amazon.com/eks/) etc. To access your Kubernetes cluster, you will need [`kubectl`](https://kubernetes.io/docs/reference/kubectl/overview/), which is pretty [easy to install](https://kubernetes.io/docs/tasks/tools/install-kubectl/).

e.g. to install `kubectl` for Mac, all you need is

    curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/darwin/amd64/kubectl && \
    chmod +x ./kubectl && \
    sudo mv ./kubectl /usr/local/bin/kubectl


### Using Environment Variables for configuration

Let’s start off with an easy peasy example to see how to use environment variables by specifying them directly within our `Pod` specification.

{% gist https://gist.github.com/abhirockzz/b88a7079c819184c60eae6947cf2d6f7 %}

Notice how we define two variables in `spec.containers.env` — `ENVVAR1` and `ENVVAR2` with values `value1` and `value2` respectively.

Let’s start off by creating the `Pod` using the YAML specified above. 

> `Pod` is just a Kubernetes resource or object. The YAML file is something that describes its desired state along with some basic information - it is also referred to as a `manifest`, `spec` (shorthand for specification) or `definition`.

Use the [`kubectl apply`](https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#apply) command to submit the `Pod` information to Kubernetes.

> To keep things simple, the YAML file is being referenced directly from the [GitHub repo](https://github.com/abhirockzz/kubernetes-in-a-nutshell), but you can also download the file to your local machine and use it in the same way.

    $ kubectl apply -f   https://raw.githubusercontent.com/abhirockzz/kubernetes-in-a-nutshell/master/configuration/kin-config-envvar-in-pod.yaml
    
    pod/pod1 created

To check the environment variables, we will need to execute a command "inside" of the Pod using `kubectl exec` — you should see the ones which were seeded in the `Pod` definition.

Here, we have used `grep` to filter for the variable(s) we’re interested in

    $ kubectl exec pod1 -it -- env | grep ENVVAR
    
    ENVVAR1=value1
    ENVVAR2=value2


> **What's `kubectl exec`?** In simple words, it allows you to execute a command in specific container within a `Pod`. In this case, our `Pod` has a single container, so we don't need to specify one


Ok, with that concept out of the way, we can explore `ConfigMap`s.

### Using a `ConfigMap`

The way it works is that your configuration is defined in a `ConfigMap` object which is then referenced in a `Pod` (or `Deployment`).

Let’s look at techniques using which you can create a `ConfigMap`

#### Using a manifest file

It’s possible to create a `ConfigMap` along with the configuration data stored as key-value pairs in the `data` section of the definition.

{% gist https://gist.github.com/abhirockzz/9e6a65545e033b64b2b2367bea6bed6f %}


In the above manifest:

- the `ConfigMap` named `simpleconfig` contains two pieces of (key-value) data — `hello=world` and `foo=bar`
- `simpleconfig` is referenced by a `Pod` (`pod2`; the keys `hello` and `foo` are consumed as environment variables `HELLO_ENV_VAR` and `FOO_ENV_VAR` respectively.

> Note that we have included the `Pod` and `ConfigMap` definition in the same YAML separated by a `---`

Create the `ConfigMap` and confirm that the environment variables have been seeded

    $ kubectl apply -f   https://raw.githubusercontent.com/abhirockzz/kubernetes-in-a-nutshell/master/configuration/kin-config-envvar-configmap.yaml
    
    configmap/config1 created
    pod/pod2 created
    
    $ kubectl get configmap/config1
    
    NAME      DATA   AGE
    config1   2      18s
    
    $ kubectl exec pod2 -it -- env | grep _ENV_
    
    FOO_ENV_VAR=bar
    HELLO_ENV_VAR=world


#### Shortcut using `envVar`

We consumed both the config data (`foo` and `hello`) by referencing them separately, but there is an easier way! We can use `envFrom` in our manifest to directly refer to all key-value data in a `ConfigMap`.

> When using `ConfigMap` data this way, the key is directly used as the environment variable name. That’s why you need to follow the [naming convention](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.15/#configmap-v1-core) i.e. Each key must consist of alphanumeric characters, ‘-’, ‘_’ or ‘.’

{% gist https://gist.github.com/abhirockzz/6e64f71ac818b57d201f0a296424c5c3 %}

Just like before, we need to create the `Pod` and `ConfigMap` and confirm the existence of environment variables

    $ kubectl apply -f   https://raw.githubusercontent.com/abhirockzz/kubernetes-in-a-nutshell/master/configuration/kin-config-envvar-with-envFrom.yaml
    
    configmap/config2 created
    pod/pod3 created
    
    $ kubectl get configmap/config2
    
    NAME      DATA   AGE
    config2   2      25s
    
    $ kubectl exec pod3 -it -- env | grep _ENV
    
    HELLO_ENV=world
    FOO_ENV=bar

Nice little trick ha? :-)

![](https://media.giphy.com/media/11ISwbgCxEzMyY/giphy.gif)


#### Configuration data as files

Another interesting way to consume configuration data is by pointing to a `ConfigMap` in the `spec.volumes` section of your `Deployment` or `Pod` spec. 

> If you have no clue what `Volumes` (in Kubernetes) are, don’t worry. They will be covered in upcoming blogs. For now, just understand that volumes are a way of abstracting your container from the underlying storage system e.g. it could be a local disk or in the cloud such as [Azure Disk](https://docs.microsoft.com/azure/virtual-machines/windows/managed-disks-overview?WT.mc_id=devto-blog-abhishgu), [GCP Persistent Disk](https://cloud.google.com/persistent-disk/) etc.

{% gist https://gist.github.com/abhirockzz/bcd2f866cef291e1d1470310372a3c46 %}

In the above spec, pay attention to the `spec.volumes` section — notice that it refers to an existing `ConfigMap`. Each key in the `ConfigMap` is added as a file to the directory specified in the spec i.e. `spec.containers.volumeMount.mountPath` and the value is nothing but the contents of the file. 

> Note that the files in volumes are automatically updated if the `ConfigMap` changes. 

In addition to traditional string based values, you can also include full-fledged files (JSON, text, YAML, etc.) as values in a `ConfigMap` spec.

{% gist https://gist.github.com/abhirockzz/fe77367d657f728d86d609c8aa42b31c %}

In the above example, we have embedded an entire JSON within the data section of our `ConfigMap`. To try this out, create the `Pod` and `ConfigMap`

    $ kubectl apply -f   https://raw.githubusercontent.com/abhirockzz/kubernetes-in-a-nutshell/master/configuration/kin-config-envvar-json.yaml
    
    configmap/config3 created
    pod/pod4 created
    
    $ kubectl get configmap/config3
    
    NAME      DATA   AGE
    config3   1     11s

As an exercise, confirm that the environment variable was seeded into the `Pod`. Few pointers:

- the name of the `Pod` is `pod4` 
- double-check the name of the environment variable you should be looking for

You can also use `kubectl` CLI to create a `ConfigMap`. It might not be suitable for all use cases but it certainly makes things a lot easier

### Using `kubectl`

There are multiple options:

#### Using `--from-literal` to seed config data

We’re seeding the following key-value pairs into the `ConfigMap` — `foo_env=bar` and `hello_env=world`

    $ kubectl create configmap config4 --from-literal=foo_env=bar --from-literal=hello_env=world

#### Using `--from-file`

    $ kubectl create configmap config5 --from-file=/config/app-config.properties

This will create a `ConfigMap` (`config5`) with

- a key with the same name of the file i.e. `app-config.properties` in this case
- and, value as the contents of the file

You can choose to use a different key (other than the file name) to override the default behavior

    $ kubectl create configmap config6 --from-file=CONFIG_DATA=/config/app-config.properties

In this case, `CONFIG_DATA` will be the key

#### From files in a directory

You can seed data from multiple files (in a directory) at a time into a `ConfigMap`

    $ kubectl create configmap config7 --from-file=/home/foo/config/

You will end up with

- multiple keys which will the same as the individual file name
- the value will be the contents of the respective file

### Good to know

Here is a (non-exhaustive) list of things which you should bear in mind when using `ConfigMap`s:

- Once you define environment variables `ConfigMap`, you can utilize them in the command section in `Pod` spec i.e. `spec.containers.command` using the `$(VARIABLE_NAME)` format
- You need to ensure that the `ConfigMap` being referenced in a `Pod` is already created — otherwise, the `Pod` will *not start*. The only way to get around this is to mark the `ConfigMap` as `optional`.
- Another case in which might prevent the `Pod` from starting is when you reference a key that actually does not exist in the `ConfigMap`.

> You can also refer the [`ConfigMap API`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.15/#configmap-v1-core)

That's it for this edition of the "Kubernetes in a Nutshell" series. Stay tuned for more!


If you are interested in learning Kubernetes and Containers using [Azure](https://azure.microsoft.com/services/kubernetes-service/?WT.mc_id=devto-blog-abhishgu), simply [create a **free** account](https://azure.microsoft.com/en-us/free/?WT.mc_id=devto-blog-abhishgu) and get going! A good starting point is to use the [quickstarts, tutorials and code samples](https://docs.microsoft.com/azure/aks/?WT.mc_id=devto-blog-abhishgu) in the documentation to familiarize yourself with the service. I also highly recommend checking out the [50 days Kubernetes Learning Path](https://azure.microsoft.com/resources/kubernetes-learning-path/?WT.mc_id=devto-blog-abhishgu). Advanced users might want to refer to [Kubernetes best practices](https://docs.microsoft.com/azure/aks/best-practices?WT.mc_id=devto-blog-abhishgu) or watch some of the [videos](https://azure.microsoft.com/resources/videos/index/?services=kubernetes-service&WT.mc_id=devto-blog-abhishgu) for demos, top features and technical sessions.


I really hope you enjoyed and learned something from this article! Please like and follow if you did. Happy to get feedback via [@abhi_tweeter](https://twitter.com/abhi_tweeter) or just drop a comment.