CKA Study notes - ConfigMaps and Secrets

Overview

Continuing with my Certified Kubernetes Administrator exam preparations I'm now going to take a look at ConfigMaps and Secrets.

ConfigMaps are a way to pass configuration to a Pod, or infact other parts of the system. For instance they can be used for configuring parts of a Kubernetes cluster.

Kube-system configmaps

The above lists the kube-system configmaps in my Kubernetes cluster.

Secrets are similar to ConfigMaps, but they are designed to hold smaller objects and meant to be used for sensitive data like passwords, tokens etc.

The reason for using ConfigMaps and Secrets is to keeping Configuration data separate from the Application code

Note #1: I'm using documentation for version 1.19 in my references below as this is the version used in the current (jan 2021) CKA exam. Please check the version applicable to your usecase and/or environment

Note #2: This is a post covering my study notes preparing for the CKA exam and reflects my understanding of the topic, and what I have focused on during my preparations.

Config maps

Kubernetes Documentation reference

Where secrets are meant to store sensitive data, ConfigMaps should only hold non-sensitive data. There's no secrecy or encryption offered with ConfigMaps

ConfigMaps are not meant to hold large data objects. The maximum size of a config map is 1 MiB.

As mentioned you can use ConfigMaps for holding Kubernetes configuration, but normally we'll come across or use them to hold data meant for use by Pods. A ConfigMap can for example hold a database host variable which your application code can use to know which database server to connect to in a dev/test/prod scenario.

Data is stored in ConfigMaps as key-value pairs. You can store data as data fields or binaryData fields where the data field are designed to hold UTF-8 byte sequences while the binaryData field holds binary data.

ConfigMaps can be used by a container inside a Pod in four different ways:

  1. Command and arguments inside a container(, set at runtime)
  2. Environment variables
  3. As a volume mounted for the container to read from
  4. Through the Kubernetes API

The fourth option requires you to write the logic yourself, but you are in more control of how to respond to any changes to the ConfigMaps. This allows you also to access ConfigMaps from different namespaces, whereas the first three options requires the ConfigMap to belong to the same namespace as the pod. The first three options are pushed to the Pod by the kubelet at launch.

Create ConfigMaps

Creating a ConfigMap (configmaps can also be created imperatively with the kubectl create configmap command) through a yaml file:

 1apiVersion: v1
 2kind: ConfigMap
 3metadata:
 4  name: configmap-name
 5data:
 6  configmap-key1: "some value"
 7  some-key2: 123
 8  some.properties: |
 9    some.types: blue,green
10    some.key: 5    
New configmap created

Example of a ConfigMap accessed as environment variables in a Pod:

 1apiVersion: v1
 2kind: Pod
 3metadata:
 4  name: config-map-demo
 5spec:
 6  containers:
 7    - name: config-map-pod
 8      image: nginx
 9      env:
10        - name: MY_ENV_VARIABLE  #Used inside the container
11          valueFrom:
12            configMapKeyRef:
13              name: configmap-name #Name of an existing configmap
14              key: configmap-key1 #A key from the specified configmap that we should use the value from
Pod created with a env variable from configmap

Example of a ConfigMap accessed as a volume mount:

 1apiVersion: v1
 2kind: Pod
 3metadata:
 4  name: config-map-volume-demo
 5spec:
 6  containers:
 7    - name: config-map-volume
 8      image: nginx
 9      volumeMounts:
10        - name: my-volume
11          mountPath: "/etc/folder"
12          readOnly: true
13  volumes:
14    - name: my-volume
15      configMap:
16        name: configmap-name
Configuration of pod with configmap mounted as a volume

If you want to mount multiple ConfigMaps you must mount multiple volumes. Since volumes are mounted to the pod they can be used by multiple containers inside the pod, but they would all need the volumeMounts to be able to access it.

One important difference between volume mounted ConfigMaps and environment variable mounted ConfigMaps is that the ConfigMaps mounted through a volume will be updated if changed. This happens on periodic syncs so it's not instant, but still, there are an update mechanism there through the kubelet. Environment variables do not update and requires a Pod restart to update. (reference)

Verify ConfigMap in Pod

To verify that our configmaps are available we'll take a look at our created pods

First with the environment variable example

1kubectl exec -it <pod-name> -- sh
2
3#inside shell
4echo $<my-env-variable-name>
Verifying env variable from configmap

And then our configmap mounted as a volume

1kubectl exec -it <pod-name> -- sh
2
3#inside shell
4ls /<path>
5
6cat /<path>/<configmap-key>
Verifying configmap mounted as volume

Notice since we specified the ConfigMap in the volume created in the pod it brings in all the key-value pairs in the ConfigMap.

Let's also test updating the ConfigMap and see the results inside the pod (again note that the configmaps are updated on a periodic sync and might take some time to reflect inside the pod)

1kubectl edit configmaps <config-map-name> #Ideally you'd edit the yaml file and re-apply that
2
3kubectl describe configmap <config-map-name>
4
5kubectl exec -it <pod-name> -- sh
6
7#inside pod
8cat /<path>/<config-map-key>
Configmap updated and verified in pod

Secrets

Kubernetes Documentation reference

Now let's switch over to Secrets which is a way to store sensitive configuration data.

By default you'll probably have a few secrets already configured in the cluster. Like the following example in the default namespace that refers to a service-account which is used to communicate with the kube-apiserver

1kubectl get secrets
2
3kubectl describe secret <secret-name>
Service account secret

Like a ConfigMap a Secret is used in a Pod by mounting it through a volume or specifying it as an environmental variable. A secret is also used by the kubelet when pulling images for a Pod

The secrets are stored as key-value pairs, as they are in the case of ConfigMaps. We can specify the value as a data and/or a stringData field (technically all stringData fields are converted to a data field internally). Values in a data field has to be a base64 encoded string, whereas a stringData field can hold arbitrary strings.

There are a few built-in secret types available, where the Opaque type is the default and what is used for arbitrary user-data. We can also specify custom types if needed. There's also types for service-accounts, certificates and more.

Create secret

Let's create a simple secret as an example

1kubectl create secret <secret-type> <secret-name>
Create a generic empty secret

We haven't specified any data in the secret so for now it's empty.

Let's recreate the secret with values from the commandline. For that we use the --from-literal parameter

1kubectl create secret <secret-type> <secret-name> --from-literal=<key>=<value> --from-literal=<key2>=<value>
Create secret with values

As we can see the actual values have been base64 encoded. Notice also that the secret-type is set to Opaque which is the default/generic.

Let's try to decode the password value. First we'll examine the yaml output of the secret, and then extract the .data.password value and pipe it through a base64 decoder

1kubectl get secret my-secret -o yaml
2
3kubectl get secret my-secret -o jsonpath='{.data}'
4
5echo $(kubectl get secret my-secret -o jsonpath='{.data.password}' | base64 --decode )
Decoding a secret

As this shows, be careful with who can access the secrets in the cluster as it's fairly easy to retrieve the values.

Data can be added from a text file as well. We use the --from-file parameter instead of the --from-literal as we saw in the previous example.

1kubectl create secret <secret-type> <secret-name> --from-file=<key>=<value>
Create secret from file, and decode to verify

Note that in this example, we're only creating the password, but we could create the username as well and add it to the same secret

Secrets can of course also be created from a yaml (or json) file. Note that you have to add the data fields with base64 encoded values

Base64 encode password
1apiVersion: v1
2kind: Secret
3metadata:
4  name: <my-secret-name>
5type: Opaque
6data:
7  password: <base64-encoded-string>
Create secret from yaml and verify

Use secret in a Pod

Let's see how we can use a secret inside a Pod.

 1apiVersion: v1
 2kind: Pod
 3metadata:
 4  name: <my-name>
 5spec:
 6  containers:
 7  - name: <my-name>
 8    image: nginx
 9    env:
10      name: <my-env-name>
11      valueFrom:
12        secretKeyRef:
13          name: <my-secret-name>
14          key: <key>
Create Pod with secret mapped to env variable

Now let's check if we have access to the secret inside our pod

1kubectl exec -it <pod-name> -- sh
Verify secret inside Pod

We'll only look at an Environment variable example, but we could mount secrets as a volume as we did with our ConfigMaps earlier

ImagePullSecrets

Kubernetes Documentation reference

One very common use case for Secrets is storing the credentials needed to pull container images from registries like DockerHub etc. These can then be referenced in the imagePullSecrets section in a Pod definition. (reference)

An image pull secret can also be assigned to a service account.(reference)

Summary

ConfigMaps and Secrets is a very important concept in Kubernetes as it allows us to decouple configuration and application code. This allows our applications to be used in many environments and clusters without having to change anything but the references to the configuration objects.

There's more to both ConfigMaps and Secrets than shown in this post. As mentioned in the start of this post I'm using this post as a way to reinforce studying for the CKA exam. Be sure to check out the documentation for more info

This page was modified on January 14, 2021: Fixed note reg. doc