"Separation of configuration from code" is one of the tenets of the 12-factor applications. 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. So let's get started....
To configure your apps in Kubernetes, you can use:
- Good old environment variables
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
Docker for Mac etc. or a managed Kubernetes service from Azure (AKS), Google, AWS etc. To access your Kubernetes cluster, you will need
kubectl, which is pretty easy to install.
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
Let’s start off with an easy peasy example to see how to use environment variables by specifying them directly within our
Notice how we define two variables in
ENVVAR2 with values
Let’s start off by creating the
Pod using the YAML specified above.
Podis 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
spec(shorthand for specification) or
kubectl apply command to submit the
Pod information to Kubernetes.
To keep things simple, the YAML file is being referenced directly from the GitHub repo, 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
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
kubectl exec? In simple words, it allows you to execute a command in specific container within a
Pod. In this case, our
Podhas a single container, so we don't need to specify one
Ok, with that concept out of the way, we can explore
The way it works is that your configuration is defined in a
ConfigMap object which is then referenced in a
Let’s look at techniques using which you can create a
It’s possible to create a
ConfigMap along with the configuration data stored as key-value pairs in the
data section of the definition.
In the above manifest:
simpleconfigcontains two pieces of (key-value) data —
simpleconfigis referenced by a
pod2; the keys
fooare consumed as environment variables
Note that we have included the
ConfigMapdefinition in the same YAML separated by a
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
We consumed both the config data (
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
ConfigMapdata this way, the key is directly used as the environment variable name. That’s why you need to follow the naming convention i.e. Each key must consist of alphanumeric characters, ‘-’, ‘_’ or ‘.’
Just like before, we need to create the
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? :-)
Another interesting way to consume configuration data is by pointing to a
ConfigMap in the
spec.volumes section of your
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, GCP Persistent Disk etc.
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
In addition to traditional string based values, you can also include full-fledged files (JSON, text, YAML, etc.) as values in a
In the above example, we have embedded an entire JSON within the data section of our
ConfigMap. To try this out, create the
$ 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
- 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
There are multiple options:
We’re seeding the following key-value pairs into the
$ kubectl create configmap config4 --from-literal=foo_env=bar --from-literal=hello_env=world
$ kubectl create configmap config5 --from-file=/config/app-config.properties
This will create a
- a key with the same name of the file i.e.
app-config.propertiesin 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
You can seed data from multiple files (in a directory) at a time into a
$ 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
Here is a (non-exhaustive) list of things which you should bear in mind when using
- Once you define environment variables
ConfigMap, you can utilize them in the command section in
- You need to ensure that the
ConfigMapbeing referenced in a
Podis already created — otherwise, the
Podwill not start. The only way to get around this is to mark the
- Another case in which might prevent the
Podfrom starting is when you reference a key that actually does not exist in the
You can also refer the
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, simply create a free account and get going! A good starting point is to use the quickstarts, tutorials and code samples in the documentation to familiarize yourself with the service. I also highly recommend checking out the 50 days Kubernetes Learning Path. Advanced users might want to refer to Kubernetes best practices or watch some of the videos 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 or just drop a comment.