Kubernetes ConfigMap on Dynamic Applications
Kubernetes has a feature called ConfigMap where it can be used as a configuration file or environment variables. We will talk about what is ConfigMap and what are the possibilities using ConfigMap?
What is ConfigMap
ConfigMap allows you to decouple configuration artifacts from image content to keep containerized applications portable.
As per Kubernetes documentation, ConfigMap is a mechanism to separate configuration from a containerized application (since we’re talking Kubernetes, all application inside Kubernetes is containerized). This will lead the applications to be more portable and more configurable. ConfigMap usage is more suitable for a configuration that doesn’t contain sensitive information since it doesn’t have any encryption.
How ConfigMap works?
There are 3 ways to create ConfigMap:
--from-file
flag. Example:kubectl create configmap myConfigMap --from-file /my/path/to/directory/or/file
--from-literal
flag. Example:kubectl create configmap myConfigMap --from-literal KEY1=VALUE1 KEY2=VALUE2
- With Kubernetes manifest with kind
ConfigMap
. We can create a file with.yaml
or.json
, and the we can apply it usingkubectl apply -f myConfigMap.yaml
kubectl create configmap
or kubectl apply -f <file>
command will upload the ConfigMap definition to the Kubernetes Apiserver that lives inside Kubernetes cluster.
The Apiserver will distribute the ConfigMap definition to all Pods inside the cluster.
How to use ConfigMap?
There are 2 ways to use ConfigMap
1. As environment variable
As an example, we will create a ConfigMap like this
Descriptions:
1. ConfigMap name
2. ConfigMap data (can be in KEY-VALUE format or filename format)
We will attach the ConfigMap values to a Pod and use it as environment variables
Descriptions:
1. Environment variable’s name that will be injected into Pod
2. Reference to a particular ConfigMap name. This can be found from metada.name on the ConfigMap manifest
3. ConfigMap key name that we will use
4. Load all key and values from another ConfigMap \
2. As a volume that attached to a Pod
ConfigMap also supports the Volume plugin. For instances, we will use the same ConfigMap manifest and we will create a Pod manifest like this
Descriptions:
1. Volume name that will be used. In this example, it will use the volume from number 3 which is “config-volume” volume
2. Volume mount path
3. Volume name
4. ConfigMap name that will be used by the “config-volume” volume \
Using the ConfigMap as environment variables using volume will lead us to a possibility where we can update Pod’s environment variables without the need to restart. This method usually called as “live-update” or “hot-config”.
Why do we need “live-update”? In the world of the container, the environment variables will be set in stone if the application (or container) is already running. This means we can’t change the environment variables on the fly if we are injecting environment variables using docker run -e <KEY>=<VALUE>
(for Docker) or using env
(for Kubernetes Pod). Instead, we can use a file to store the environment variables. And in this example, we will use ConfigMap that will be attached to a Pod volume.
Let’s examine the ConfigMap behavior if it got an update. Let’s create a simple ConfigMap manifest like this
After applied it using kubectl apply -f
, let’s take a look inside the Pod’s container how ConfigMap works as environment variables. We will go to the volume mount path directory based on the Pod configuration (in this case, it mounts to /etc/config
)
If we look at the image above, every key that we define on ConfigMap will be mounted as files by Kubernetes. These files have symlinks to ...data
folder and inside that folder have the same names. For example Misal ENV_IS_MAINTENANCE
symlinked to ..data/ENV_IS_MAINTENACE
, etc. Also ..data
folder symlinked to another folder called ..2019_02_22_04_00_47.246998214
.
Now for proof of concept, let’s create a simple application using NodeJS and ExpressJS and ship it as a container. This application main purpose is to read the environment variables from ConfigMap and listen to that folder if there is an update
- js
1 |
|
Those code above will listen to a “rename” event inside /etc/config
folder. Let’s containerized it and deploy it to Kubernetes. As usual, we will run kubectl apply -f
to create the Kubernetes Service, Deployment, and Pod at the same time.
- yaml
1 |
|
Next, let’s update the ConfigMap value to
From the image above, the symlinks between environment variables files didn’t change at all. The only change is the symlink for ...data
changed to a new folder called ..2019_02_23_19_01_09.591362024
. For more clarity, take a look at the short gif below
As we can see, updating environment variables using this method is quite easy since we don’t need to redeploy the Pod at all!
But there are some things worth to note:
- ConfigMap only “live” inside a namespace. Which mean it can’t be shared to another namespace
- ConfigMap update is eventually consistent which means it doesn’t update immediately after we update it. This is the default behavior from the Kubernetes (kubelet to be specific) where the sync frequency every 60 seconds. If we want to make it faster, we can change the
--sync-frequency
on the kubelet. Please look at the official documentation for more reference
Extra!
The implementation of the “live-update” above can only work on NodeJS applications since it has fs
standard library which can listen to the filesystem events (fs.watch
). If we want to implement “live-update” on other than NodeJS, so we need to adjust our code and our Pod’s deployment. For simplicity, we still using NodeJS but not using fs.watch
.
- yaml
1 |
|
If we look closely at the container’s configuration above, we add a new container from jimmidyson/configmap-reload with its configurations to read the ConfigMap. This method usually called “sidecar container”.
- js
1 |
|
On the NodeJS application, we need to add a new endpoint to receive the notification from the sidecar container (“configmap-reload” container).
With this method, the application flow will be like this
- Sidecar container “configmap-reload” will watch if there is any update on the environment variables files inside the volume
- If those files got updated, sidecar container “configmap-reload” will send a notification (HTTP call) to our main container’s endpoint
/-/reload
(same with the--webhook-url
parameter on the sidecar container) - The main application will re-read all environment variables files and then store it on the global object
process.env
Using this method, we can implement “live-update” to every programming language that we like. Happy coding!
References
- https://unofficial-kubernetes.readthedocs.io/en/latest/tasks/configure-pod-container/configmap/
- https://cloud.google.com/kubernetes-engine/docs/concepts/configmap
- https://www.slideshare.net/kubecon/kubecon-eu-2016-keynote-kubernetes-state-of-the-union
- https://medium.com/@xcoulon/kubernetes-configmap-hot-reload-in-action-with-viper-d413128a1c9a
- https://github.com/kubernetes/kubernetes/issues/30189
- https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap
- https://github.com/jimmidyson/configmap-reload