Keeping the State of Apps Part 3: Introduction to ConfigMaps

In the last part of this series, we focused on Secrets which is one of the Kubernetes objects that deals with keeping your data safe and secured. In this part, we will look at ConfigMaps which is a similar Kubernetes object but differs in use case to Kubernetes Secret. After digging into theory, we will follow up with hands-on practice to test the use case and functionalities of a ConfigMap in Kubernetes.

What is a ConfigMap?

A ConfigMap is a Kubernetes object used to store non-sensitive data in contrast to a Secret which is used to store sensitive data. A ConfigMap can be injected into a container in a Pod as environment variables at runtime, as a command arg, or as a file via volumes and volumeMounts. It uses a data property in the manifest YAML file to store configuration data as key-value pairs in lieu of spec as applicable to other Kubernetes objects.

Creating a ConfigMap

You can create a ConfigMap in two ways, which can be either declarative or imperative. You can read more on the imperative and declarative ways of creating Kubernetes objects in part two of this series.

Imperative Method

Creating a ConfigMap in an imperative way is possible in three ways. You can create it from files, directories or literal values

Before we begin, it is imperative that you have basic knowledge of Pod, Deployment and volumes to follow this exercise. You also need a running Kubernetes cluster, and a kubectl command-line tool must be configured to communicate with the cluster. You can easily create a Kubernetes cluster on any environment with KubeOne. Check our Getting Started for instructions. Alternatively, you can use the Kubernetes playground for practising purposes.

Create a ConfigMap from Files

You can create a ConfigMap from single or multiple files using $ kubectl create configmap (name of the map) (file-name).

Create a ConfigMap from Single File

The below steps will guide you on how to create a ConfigMap and check the status and description.

Step 1: Create a file to store your data.

$ vim config1-example.txt

Step 2: Store the below data into your file, save, and exit the terminal. You can save any data of your choice into the file created above.

City: Hamburg
State: Hamburg
Country: Germany

Step 3: Run the command below to create the ConfigMap.

$ kubectl create configmap config-example1 --from-file=config1-example.txt

configmap/config-example1 created

Step 4: Check the status of the ConfigMap.

$ kubectl get configmap config-example1

NAME                 DATA        AGE
config-example1       1          4m8s

Step 5: Check the ConfigMap description.

$ kubectl describe configmap config-example1

Name:         config-example1
Namespace:    default
Labels:       <none>
Annotations:  <none>
Data
====
config-example.txt:
----
City: Hamburg
State: Hamburg
Country: Germany
Events:  <none>

Create a ConfigMap from Multiple Files

You can pass the --from-file flag several times to create a ConfigMap from multiple filenames or data sources. The steps below will guide you through the exercise.

Step 1: Create another file in addition to the one created above.

$ vim config2-example.txt

Step 2: Store the below data into your new file, save, and exit the terminal. You can save any data of your choice into the file.

City: Ann Arbor
State: Michigan
Country: USA

Step 3: Run the command below to create the ConfigMap from the two files that you created above.

$ kubectl create configmap config-example2 --from-file=config1-example.txt --from-file=\
config2-example.txt

configmap/config-example2 created

Step 4: Check the status of the ConfigMap.

$kubectl get configmap config-example2

NAME                 DATA    AGE
config-example2       2      10s

Name →  ## The name of the ConfigMap
Data → ## The number of data in the ConfigMap
Age → ## The ConfigMap running time

Step 5: Check the ConfigMap description.

$ kubectl describe configmap config-example2

Name:         config-example2
Namespace:    default
Labels:       <none>
Annotations:  <none>
Data
====
config1-example.txt:
----
City: Hamburg
State: Hamburg
Country: Germany

config2-example.txt:
----
City: Ann Arbor
State: Michigan
Country: USA
Events:  <none>

You can see in the description above the data stored in both files under the data field.

Create a ConfigMap from Directories

You can create a ConfigMap from a directory by storing multiple files in the directory and then using $ kubectl create to create a ConfigMap from the multiple files saved in the directory. 

In this method, kubectl identifies the files with the valid key names in the directory and packages them into the ConfigMap.

How to Create a ConfigMap from Directories

You can create a ConfigMap from a directory using $ kubectl create configmap (name of the configmap) (directory name/path). The below steps will guide you through.

Step 1: Create a directory.

$ mkdir -p configmap/directory

Step 2: Change into the directory and create a file.

$ cd configmap/directory
$ vim config-dir1

Step 3: Copy, paste, and save the data from the first exercise into your file and exit the terminal.

Step 4: Create another file.

$ vim config-dir2

Copy, paste, and save the data from the second exercise into your file and exit the terminal.

Step 5: Check the directory to be sure that the files are created using ls command.

$ ls
config-dir1  config-dir2

Change back to the home directory using cd and then enter key.

Step 6: Run the command below to create a ConfigMap from the directory.

$ kubectl create configmap config-example --from-file=configmap/directory

configmap/config-example created

Check the status of the ConfigMap:

$ kubectl get configmap config-example

NAME               DATA    AGE
config-example      2      31s

Check the description:

$ kubectl describe configmap config-example

Name:         config-example
Namespace:    default
Labels:       <none>
Annotations:  <none>
Data
====
config-dir1:
----
City: Hamburg
State: Hamburg
Country: Germany

config-dir2:
----
City: Ann Arbor
State: Michigan
Country: USA
Events:  <none>

The description and the status above produced the same output as that of creating a ConfigMap with multiple files.

Creating a ConfigMap from Literal Values

You can create a ConfigMap from literal values by using --from-literal tag on the command line. It uses a key-value pair or multiple key-value pairs to create the ConfigMap.

How to Create a ConfigMap from Literal Values

Run the command below to create the ConfigMap:

$ kubectl create configmap literal-example --from-literal=City=Hamburg --from-literal=Country=Germany --from-literal=username=Ann-Arbor --fromliteral=Position=Director 
--from-literal=Department=Admin

configmap/literal-example created

Check the ConfigMap status:

$ kubectl get configmap literal-example

NAME                DATA     AGE
literal-example      6       26s

Check the detailed status in a YAML format:

$ kubectl get configmap -o yaml

apiVersion: v1
items:
  apiVersion: v1
  data:
    City: Hamburg
    Country: Germany
    Department: Admin
    Position: Director
    State: Hamburg
    username: Ann-Arbor
  kind: ConfigMap
  metadata:
    creationTimestamp: "2020-08-13T15:46:42Z"
    managedFields:
      apiVersion: v1
      fieldsType: FieldsV1

Check the description:

$ kubectl describe configmap literal-example

Name:         literal-example
Namespace:    default
Labels:       <none>
Annotations:  <none>
Data
====
username:
----
Ann-Arbor
City:
----
Hamburg
Country:
----
Germany
Department:
----
Admin
Position:
----
Director
State:
----
Hamburg
Events:  <none>

Note: In literal values, you can not use a key for two different data, unlike the other methods we used earlier.

Declarative Method

You can create a ConfigMap in a declarative way by first declaring the configuration data in a manifest YAML file and then use kubectl create command to create the ConfigMap. 

Follow the below steps to create a ConfigMap using the declarative method.

Step 1: Create a file with YAML syntax extension.

$ vim config-declarative.yaml

Step 2: Copy and paste the below configuration into the file, save and exit the terminal.

apiVersion: v1
kind: ConfigMap
metadata:
  name: manifest-example
data:
  city: Ann Arbor
  state: Michigan
  
apiVersion → ## This field represents the version of the ConfigMap API. 
kind → ## The object kind which must be the name of the object
metadata.name → ## The name of the application configuration which can be any value
Data → ## This field holds the data of the ConfigMap, which consists of the keys and the values. 

Step 3: Create the ConfigMap using the below command.

$kubectl create -f config-declarative.yaml

configmap/manifest-example created

Check the status and description using the commands used in the previous exercises.

Application Configuration using ConfigMap

You can use ConfigMap to configure your application by using files in read-only volume or as Environment variables. We will walk you through the use of both methods and perform some exercises.

How to Configure an Application using Files

The following steps will guide you on how to configure your app using files. You need to first create a ConfigMap before creating the Pod and then reference the ConfigMap in the Pod by adding a volume. Then, mount the volume with volumeMounts property in the Pod manifest file. 

A running Kubernetes cluster and a kubectl command-line are essential for this exercise, just like the previous exercises. The kubectl command-line tool must be configured to talk to the cluster. Details on how you can get a cluster running using KubeOne can be found here.

Follow the steps below to configure an application using files:

Step 1: Create a ConfigMap.

$ vim config-file.yaml

Step 2: Copy, paste and save the below manifest in your YAML file and exit the terminal.

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: default
  name: my-configmap
data:
  app-config: 
    City = Hamburg
    State = Hamburg
    Country = Germany

Step 3: Create the ConfigMap using kubectl create.

$ kubectl create -f config-file.yaml

configmap/my-configmap created

Step 4: Check the ConfigMap status.

$kubectl get configmap config-example1

NAME              	DATA       AGE
config-example1   	 1         4m8s

Step 5: Create the Pod that will consume the ConfigMap.

$ vim pod-file.yaml

Copy, paste, and save the below manifest into your file and exit the terminal.

apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: myapp
  labels:
    app: my-app
spec:
  containers:
      name: my-app
      image: nginx
      ports:
          containerPort: 8080
      imagePullPolicy: Always
      volumeMounts:
          name: my-volume
          mountPath: /app/config
  volumes:
      name: my-volume
      configMap:
        name: my-configmap

volumes.name → ## The name of the volume is declared in this field
volumes.configMap → ## The ConfigMap is declared in this field 
volumes.configMap.name → ## The name of the ConfigMap which must be the same with the name of ConfigMAp created earlier
volumeMounts.name → ## The declared volume is injected into the Pod in this field which is why the declaration is under the container specification; the name must be the same as the name of the volumes
volumeMounts.mountPath → ## The unused directory where you will like the ConfigMap to appear

Step 6: Create the Pod using kubectl create.

$ kubectl create -f pod-file.yaml

pod/myapp created

Step 7: Check the Pod status.

$ kubectl get pods myapp

NAME    READY   STATUS    RESTARTS   AGE
myapp    1/1    Running      0       17s

Now that the Pod is running, you need to exec into the Pod to confirm if the ConfigMap has been injected. You can read more on exec-ing into a Pod in part two of this series.

Step 8: Exec into the Pod.

$ kubectl exec -it myapp -- /bin/bash ## where myapp is the name of the Pod
root@myapp:/#

Step 9: Change into the mountPath directory(/app/config) declared in the Pod manifest and check the file in the folder.

$ root@myapp:/# cd /app/config
$ root@myapp:/app/config# ls
app-config

Step 10: Open the file using cat.

root@myapp:/app/config# cat app-config
City = Hamburg
State = Hamburg
Country = Germany

The final output shows that the ConfigMap has been injected into the Pod. This is why you can view the data stored inside the ConfigMap in the container in a Pod.

How to Configure Your Application Using Environment Variables

The following steps will guide you on how to configure your app using environment variables. You need to first create a ConfigMap before creating the Pod/Deployment. Then, reference the ConfigMap in the Pod/Deployment by adding the environment variable block which contains properties such as name, valueFrom, and others.

Step 1: Create a ConfigMap and check the status.

$ vim config-file.yaml

Step 2: Copy, paste and save the below manifest in your YAML file and exit the terminal.

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: default
  name: my-configmap
data:
   City:  Hamburg
   State: Hamburg
   Country: Germany

Step 3: Create the ConfigMap using kubectl create.

$ kubectl create -f config-file.yaml

configmap/my-configmap created

Step 4: Check the ConfigMap status.

$ kubectl get configmap my-configmap

NAME          	   DATA      AGE
my-configmap        3        14m

Step 5: Create the Pod that will consume the ConfigMap.

$ vim pod-file.yaml

Copy, paste and save the below manifest in your YAML file and exit the terminal.

apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: myapp
  labels:
    app: my-app
spec:
  containers:
      name: my-app
      image: nginx
      ports:
          containerPort: 8080
      imagePullPolicy: Always
      env:
          name: CONFIG-ENV-CITY
          valueFrom:
            configMapKeyRef:
              name: my-configmap
              key: City
          name: CONFIG-ENV-STATE
          valueFrom:
            configMapKeyRef:
              name: my-configmap
              key: State
          name: CONFIG-ENV-COUNTRY
          valueFrom:
            configMapKeyRef:
              name: my-configmap
              key: Country
      restartPolicy: Never

If you look at the above manifest, you will see that the env property is introduced in lieu of volumes and volumeMounts, as well as the properties described below.

env.name → ## The name of the environment variable
env.valueFrom → ## This field declares where the values are located which is in the ConfigMap
env.valueFrom.configMapKeyRef → ## The ConfigMap is declared here 
env.valueFrom.configMapKeyRef.name → ## The ConfigMap name  where the environment variables will be pulled from; the name must be the same as the name of the ConfigMap.
env.valueFrom.configMapKeyRef.key → ## The environment variables to pull from the ConfigMap; the key must be the same with the key stored in the ConfigMap configuration.

Step 6: Create the Pod using kubectl create.

$ kubectl create -f pod-file.yaml

pod/myapp created

Step 7: Check the Pod status.

$ kubectl get pods myapp

NAME    READY    STATUS      RESTARTS   AGE
myapp    1/1     Running        0       50s

Step 8: Exec into the Pod and check if the ConfigMap data has been injected into the Pod using kubectl exec.

$ kubectl exec -it myapp -- /bin/bash
root@myapp:/# 

Now that you are in the container, you can populate the data using the env command

root@myapp:/# env
CONFIG-ENV-STATE=Hamburg
CONFIG-ENV-CITY=Hamburg
CONFIG-ENV-COUNTRY=Germany

The above data showed that the data is stored in the ConfigMap, which has been injected into the Pod.

Next in our series, we will continue with the data persistence by introducing you to more Kubernetes objects used for data preservation. You will learn more about PersistentVolumes (PVs) and PersistentVolumeClaims (PVCs) as well as how to make its process more dynamic using StorageClass. We encourage you to contact us with any questions you have!

Learn More: