9 min read

K8s Storage: Using ConfigMaps, Secrets and ENV variables as Configuration Data Sources.

K8s Storage: Using ConfigMaps, Secrets and ENV variables as Configuration Data Sources.

If you've been reading the previous articles about K8s Storage, then you now that Pods are ephemeral and if we want to save the data they generate, we have to create PersistentVolumeClaims, PersistentVolumes and, in the case of Dynamic Storage Provisioning, StorageClasses.

In this article, we will look at some other K8s native objects that behave much like storage and are used for the purpose of passing configuration data to Pods, also called Configuration-as-Data.

Table of Contents

  1. Why should we be concerned about Configuration-as-Data?
  2. The different ways of passing configuration data to a Pod:
    1. Demo: Environment Variables into containers
    2. Demo: Secrets Creation and Access
    3. Demo: Passing Secrets to containers as ENV
    4. Demo: Passing Secrets as a Volume
    5. Demo: Pulling a container image from a private container registry
    6. Demo: Creating and using ConfigMaps

Configuration-as-Data. Why should I care?

The first reason goes back to the basic nature of Pods which is they ARE UNRELIABLE. Pods are ephemral and they can be destroyed at will. If the data needed for configuring these Pods is kept inside them, it will be lost the minute the Pod dies. Therefore, it becomes important to save configuration related data to a K8s object (like a Secret or ConfigMap) or even a non K8s object (like environment variables), and everytime a replica Pod is spun up, we can use the information stored inside this config-data-store to ensure the right values are used.

Another important reason is SECURITY. It makes sense to use K8s objects like ConfigMaps and Secrets to store passwords and other sensitive information. In this way, the container in the Pod does not know the password but can ask for it when needed. If the Pod dies, then it dies but the Secrets and passwords are still present and ready for use for the next replica that spins up.

📢
For this article, we will work on:
- Environment Variables
- Secrets
- ConfigMaps

Demo: Environment Variables into containers

Figure 1: The contents of the manifest for this demo shows 2 environment variables DATABASE_SERVERNAME and BACKEND_SERVERNAME.

Once the Pod is up and running, these 2 variables should, in theory, be reflected in the list of environment variables inside the container.

Step 1: Execute the Deployment.

Figure 2: Apply the Deployment.

Step 2: Take a peek at the list of environment variables inside the container.

Figure 2: Look inside the Pod and list all the environment variables. In particular, look at BACKEND_SERVERNAME and DATABASE_SERVERNAME variables, that were added through the Pod Spec in the Deployment manifest.

Application developers can be sure that if they refer to these environment variables in their code, they will not face a run time error.

Demo: Secrets Creation and Access

We will create the Secret for this demo using the imperative style (i.e. typing the commands to create a Secret right into the terminal instead of in a YAML manifest).

Step 1: Create the Secret.

Figure 3: An imperative command that creates a Secret called mysecret.
  • The Secret type is generic because its being created through literal values.
📢
Secrets created from local files/directories (--from-file or --from-directory) or literal values passed through the command line are of type generic.

Step 2: Access the Secret.

Using $ kubectl get secrets mysecret, we can display the recently created Secret on the terminal.

Figure 4: mysecret has 2 pairs of key-values and is of type Opaque (which means its a user defined secret).

To check its content, use $ kubectl describe secret mysecret.

Figure 5: mysecret has 2 pairs of key-values - one called PASSWORD and the other called USERNAME.

The Secret is base64 encoded and is saved in etcd in the Control Plane, without any sort of default restrictions on who can see it.

📢
One of the big differences between Secrets and ConfigMaps is how data is stored inside etcd. Secrets store data in Base64 format; ConfigMaps store data in plain text.

Since there is no default authorization protocol for Secrets, anyone who can issue kubectl commands can easily access Secrets and decode their base64 encoding.

Step 3: Check the Secrets contents.

Checking the Secrets content is fairly easy.

Figure 6: Decoding the USERNAME value in mysecret.
Figure 7: Decoding the PASSWORD value in mysecret.

Demo: Passing Secrets to containers as ENV

Step 1: Create a Deployment using the manifest provided.

Figure 8: Secrets can be passed in as environment variables, through a Deployment or Pod manifest.

Step 2: Exec into the Pod and list all environment variables starting with 'mysecret'.

Figure 9: After EXEC'ing into the Pod and listing all environment variables starting with 'my'.

Demo: Passing Secrets as a Volume

Secrets can also be passed to Pods and containers in the form of a Volume.

Visually, the state resembles Figure 10 below:

Figure 10: mysecret will be mounted as a volume called volSecret, allowing /etc/appconfig to get mysecret USERNAME and mysecret PASSWORD values.

The manifest for the Pod shown in Figure 11 is below:

Figure 11: mysecret will be mounted as a volume called volSecret, allowing /etc/appconfig to get mysecret USERNAME and mysecret PASSWORD values.

Step 1: Deploy the Pods using the manifest for this demo.

Step 2: EXEC into the Pod and look for the /etc/appconfig directory in the container.

Figure 12: Navigating all the way to the end of /etc/appconfig shows us that mysecret.USERNAME and mysecret.PASSWORD have both been rendered as READ-ABLE files.

Demo: Pulling a container image from a private container registry

So far, we've seen the different ways Secrets can be used as a source of data (through environment variables and Volume mounts). Now its time to see another more everyday application for them.

What is a Pod if not an executor of containers? In most demos, the source for containers is the public facing docker hub or other registries that allow unauthenticated access but in a security obsessed world, some may decide to save their containers in private registries. Secrets can be used as the 'authentication' mechanism when containers from such registries are needed.

📢
Don't have a private container registry? No matter. Follow instructions at this link to set up on now.

Step 1: Create a Secret that will hold your registry login (docker hub for this demo).

Figure 13: A Secret of type docker-registry called private-reg-cred is created.

Step 2: Peek inside private-reg-cred using kubectl describe.

Figure 14: The only data point inside this Secret is a configuration file containing docker auth information.

Step 3: Create a Deployment referencing the Secret created in Step 2.

Figure 15 shows the content of the Deployment manifest for this part of the demo:

Figure 15: The majority of the syntax is familiar to us EXCEPT the imagePullSecrets attribute. This is a special keyword in K8s and is used for downloading container images from private repos.

Step 4: Using kubectl describe, look at the resulting Pods metadata to confirm the image is indeed the one from the private registry.

Once the Deployment manifest has been implemented, we can take a look at the resulting Pods metadata to confirm the image from the private repo was downloaded as expected.

Figure 16: Notice the image was successfully pulled in under 3 seconds, confirming our docker-registry Secret worked as expected.

Demo: Creating and using ConfigMaps

ConfigMaps are like Secrets, the only difference being they are not base64 encoded. It is a collection of key-value pairs that can contain application or environment specific settings. Using ConfigMaps (and Secrets) allows us to decouple configuration and Pods, increasing the portability of our application infrastructure.

Using ConfigMaps made using literals for Pod configurations

Step 1: Create a ConfigMap using literal values passed through command line.

Figure 17: Imperatively created ConfigMap using literals.

Step 2: Confirm the values stored in the ConfigMap are the ones passed through literals.

Figure 18: The ConfigMap contains both the keys and their values, as were passed through the command line.

Step 3: Deploy Pod using this ConfigMap.

The manifest for a Deployment using cm-from-literals as a source of configuration data is shown below in Figure 19:

Figure 19: Values stored in cm-from-literals will be 'converted' into environment variables and become accessible to the Pod.

Step 4: EXEC into the generated Pod and confirm the contents of cm-from-literals are environment variables.

Figure 20: Both BACKEND and DATABASE have been absorbed by the Pod as environment variables.

Using ConfigMaps made using files for Pod configurations

Step 1: Create a ConfigMap using a file.

Figure 21: The --from-file switch tells Kube-API server where to look for the actual files with configuration values. In our case, the file is saved in the same directory from where we are trying to make the configmap.

Step 2: Confirm the values stored in the ConfigMap are the same as those in the file.

Figure 22: The values for both keys are the same as in the file.
📢
Beware. The display for the ConfigMap made from literals and the one made from a file are slightly different. The one made from the file includes the name of the file as a key, followed by the child key-value pair.

Step 3: Deploy Pod using this ConfigMap.

Figure 23: Deployment manifest shows Pod load cm-from-file like a volume, which the container will map its /etc/appconfig folder to.

Step 4: EXEC into the Pod to check the contents of container's folder at /etc/appconfig.

Figure 24: The container was able to pick up the contents of cm-from-file inside the /etc/appconfig directory.
📢
Though only different styles for loading ConfigMaps are shown here, they are not the only ones. For example, cm-from-literals can be mounted as a Volume and cm-from-file contents can become environment variables. It is ideal to experiment with different combinations and work with the one that is a best fit for your circumstances.

I write to remember and if in the process, I can help someone learn about Containers, Orchestration (Docker Compose, Kubernetes), GitOps, DevSecOps, VR/AR, Architecture, and Data Management, that is just icing on the cake.