K8s-ConfigMaps and Secrets

ConfigMaps
ConfigMaps allow us to decouple the configuration details from the container image. Using ConfigMaps, we pass configuration data as key-value pairs, which are consumed by Pods or any other system components and controllers, in the form of environment variables, sets of commands and arguments, or volumes. We can create ConfigMaps from literal values, from configuration files, from one or more files or directories.
Create a ConfigMap from Literal Values
A ConfigMap can be created with the imperative kubectl create configmap command, and we can display its details using the kubectl get or kubectl describe commands.
Create the ConfigMap:
$ kubectl create configmap my-config \
--from-literal=key1=value1 \
--from-literal=key2=value2
configmap/my-config created
Display the ConfigMap details in YAML for my-config:
$ kubectl get configmaps my-config -o yaml
apiVersion: v1
data:
key1: value1
key2: value2
kind: ConfigMap
metadata:
creationTimestamp: 2024-03-02T07:21:55Z
name: my-config
namespace: default
resourceVersion: "241345"
selfLink: /api/v1/namespaces/default/configmaps/my-config
uid: d35f0a3d-45d1-11e7-9e62-080027a46057
With the -o yaml option, we are requesting the kubectl command to produce the output in the YAML format. The object has the ConfigMap kind, and it has the key-value pairs listed under the data field. The name of ConfigMap and other details are part of the metadata field.
Create a ConfigMap from a Definition Manifest
For a declarative approach, first we need to create a definition file with the following content:
apiVersion: v1
kind: ConfigMap
metadata:
name: customer1
data:
TEXT1: Customer1_Company
TEXT2: Welcomes You
COMPANY: Customer1 Company Technology Pct. Ltd.
where we specify the kind, metadata, and data fields, targeting the v1 endpoint of the API server.
If we name the file with the definition above as customer1-configmap.yaml, we can then create the ConfigMap with the following command:
$ kubectl create -f customer1-configmap.yaml
configmap/customer1 created
Create a ConfigMap from a File
First, we need to create a file permission-reset.properties with the following configuration data stored as key-value pairs:
permission=read-only
allowed="true"
resetCount=3
We can then create the ConfigMap with the following command:
$ kubectl create configmap permission-config \
--from-file=<path/to/>permission-reset.properties
configmap/permission-config created
Use ConfigMaps Inside Pods: As Environment Variables
Inside a Container, we can retrieve the key-value data of an entire ConfigMap or the values of specific ConfigMap keys as environment variables.
In the following example all the myapp-full-container Container's environment variables receive the values of the full-config-map ConfigMap keys:
...
containers:
- name: myapp-full-container
image: myapp
envFrom:
- configMapRef:
name: full-config-map
...
In the following example the myapp-specific-container Container's environment variables receive their values from specific key-value pairs from two separate ConfigMaps, config-map-1 and config-map-2 respectively:
...
containers:
- name: myapp-specific-container
image: myapp
env:
- name: SPECIFIC_ENV_VAR1
valueFrom:
configMapKeyRef:
name: config-map-1
key: SPECIFIC_DATA
- name: SPECIFIC_ENV_VAR2
valueFrom:
configMapKeyRef:
name: config-map-2
key: SPECIFIC_INFO
...
With the configuration presented above, we will get the SPECIFIC_ENV_VAR1 environment variable set to the value of SPECIFIC_DATA key from config-map-1 ConfigMap, and SPECIFIC_ENV_VAR2 environment variable set to the value of SPECIFIC_INFO key from config-map-2 ConfigMap.
Use ConfigMaps Inside Pods: As Volumes
We can mount a vol-config-map ConfigMap as a Volume inside a Pod. The configMap Volume plugin converts the ConfigMap object into a mountable resource. For each key in the ConfigMap, a file gets created in the mount path (where the file is named with the key name) and the respective key's value becomes the content of the file:
...
containers:
- name: myapp-vol-container
image: myapp
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: vol-config-map
...
For more details, please explore the documentation on using ConfigMaps.
Using ConfigMaps as Volumes Demo Guide
This exercise guide was prepared for the video demonstration available in this chapter. It includes an index.html file and a Deployment definition manifest that can be used as templates to define other similar objects as needed. The goal of the demo is to store the custom webserver index.html file in a ConfigMap object, which is mounted by the nginx container specified by the Pod template nested in the Deployment definition manifest.
The webserver index file:
$ vim index.html
<!DOCTYPE html>
<html>
<head>
<title>Welcome to GREEN App!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
background-color: GREEN;
}
</style>
</head>
<body>
<h1 style=\"text-align: center;\">Welcome to GREEN App!</h1>
</body>
</html>
The Deployment definition manifest:
$ vim web-green-with-cm.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: green-web
name: green-web
spec:
replicas: 1
selector:
matchLabels:
app: green-web
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: green-web
spec:
volumes:
- name: web-config
configMap:
name: green-web-cm
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
volumeMounts:
- mountPath: /usr/share/nginx/html
name: web-config
status: {}
Demo: Using ConfigMaps as Volumes
We demonstrate how to use a ConfigMap to inject custom configuration data inside a container. The custom configuration data is basically an index.html file. We want to customize a web server.
vim green/index.html
$ vim index.html
<!DOCTYPE html>
<html>
<head>
<title>Welcome to GREEN App!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
background-color: GREEN;
}
</style>
</head>
<body>
<h1 style=\"text-align: center;\">Welcome to GREEN App!</h1>
</body>
</html>
It will basically display "Welcome to GREEN App!" on a green background. So I want this file to become the new index.html of our nginx web server that will run inside a cluster with the help of a pod managed by a deployment operator.
So let's begin by creating the ConfigMap resource.
kubectl create configmap green-web-cm ---from-filr=green/index.html
So the configmap resource can be created from file, and we will provide the path to the custom index.html file as an option. And the configmap will be named green-web-cm, green web ConfigMap.
So it has been created. Let's validate that the ConfigMap is there with the 'kubectl get cm'.
kubectl get cm
The abbreviated form for ConfigMaps. And then we can describe the green-web-cm, the green web ConfigMap, and it will display under its data section the content of the HTML file that we have just used as the source of the ConfigMap.
kubectl describe cm green-web-cm
And then we can describe the green-web-cm, the green web ConfigMap, and it will display under its data section the content of the HTML file that we have just used as the source of the ConfigMap.
So once this i created, let's take a quick look at the deployment definition that will use this ConfigMap.
vim web-green-with-cm.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: green-web
name: green-web
spec:
replicas: 1
selector:
matchLabels:
app: green-web
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: green-web
spec:
volumes:
- name: web-config
configMap:
name: green-web-cm
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
volumeMounts:
- mountPath: /usr/share/nginx/html
name: web-config
status: {}
So deployment is called web green.
It is a much simpler deployment than the one we have seen earlier with the volume mount and the two containers, the "debian" and the "nginx" containers.
This is green-web. So we have label and the name as green-web for the deployment. The selectors are again on green-web. And the template, the pod template only defines one simple volume, and the volume uses the configMap plugin.
So from the green-web-cm, we are actually creating a volume and then this volume will be mounted inside the single container that is specified under the pod spec.
It is an "nginx" container, exposes port 80, and its volume mount will mount the web-config that comes from the ConfigMap under the /usr/share/nginx/html, which is the home directory of nginx's index.html file.
So basically overriding the default file with the custom one injected through the ConfigMap.
So let's create this deployment.
kubectl apply -f web-green-with-cm.yaml
By now, we know that we need to expose this deployment as well.
Since the web server is already exposing port 80, all we need to do is specify the NodePort type service so we can visualize this in our browser.
kubectl expose deployment green-web --type=NodePort
Then with the help of the 'minikube service list' command, we are going to list the services of the cluster but we are mostly interested in the URL of our NodePort type service.
minikube service list
So from the command line, I could simply just run curl maybe in a silent node because we don't want too much output. Let's paste this and then maybe pipe it through head and only display maybe the first four lines of the output.
curl -s http://192.168.59.110:31844 | head -4
Okay, so every time I would curl, I would get the "Welcome to GREEN App!" displayed.
Secrets
Let's assume that we have a Wordpress blog application, in which our wordpress frontend connects to the MySQL database backend using a password. While creating the Deployment for wordpress, we can include the MySQL password in the Deployment's YAML definition manifest, but the password would not be protected. The password would be available to anyone who has access to the definition manifest.
In this scenario, the Secret object can help by allowing us to encode in base 64the sensitive information before sharing it. We can share sensitive information like passwords, tokens, or keys in the form of key-value pairs, similar to ConfigMaps; thus, we can control how the information in a Secret is used, reducing the risk for accidental exposures. In Deployments or other resources, the Secret object is referenced, without exposing its content.
It is important to keep in mind that by default, the Secret data is stored as plain text inside etcd, therefore administrators must limit access to the API server and etcd. However, Secret data can be encrypted at rest while it is stored in etcd, but this feature needs to be enabled at the API server level by the Kubernetes cluster administrator.
Create a Secret from Literal Values
To create a Secret, we can use the imperative kubectl create secret command:
$ kubectl create secret generic my-password \
--from-literal=password=mysqlpassword
The above command would create a secret called my-password, which has the value of the password key set to mysqlpassword.
After successfully creating a secret we can analyze it with the get and describe commands. They do not reveal the content of the Secret. The type is listed as Opaque.
$ kubectl get secret my-password
NAME TYPE DATA AGE
my-password Opaque 1 8m
$ kubectl describe secret my-password
Name: my-password
Namespace: default
Labels: <none>
Annotations: <none>
Type Opaque
Data
\====
password: 13 bytes
Create a Secret from a Definition Manifest
We can create a Secret manually from a YAML definition manifest. The example manifest below is named mypass.yaml. There are two types of maps for sensitive information inside a Secret: data and stringData.
With data maps, each value of a sensitive information field must be encoded using base64. If we want to have a definition manifest for our Secret, we must first create the base64 encoding of our password:
$ echo mysqlpassword | base64
bXlzcWxwYXNzd29yZAo=
and then use it in the definition manifest:
apiVersion: v1
kind: Secret
metadata:
name: my-password
type: Opaque
data:
password: bXlzcWxwYXNzd29yZAo=
Please note that base64 encoding does not mean encryption, and anyone can easily decode our encoded data:
$ echo "bXlzcWxwYXNzd29yZAo=" | base64 --decode
mysqlpassword
Therefore, make sure you do not commit a Secret's definition file in the source code.
With stringData maps, there is no need to encode the value of each sensitive information field. The value of the sensitive field will be encoded when the my-password Secret is created:
apiVersion: v1
kind: Secret
metadata:
name: my-password
type: Opaque
stringData:
password: mysqlpassword
Using the mypass.yaml definition file we can now create a secret with kubectl create command:
$ kubectl create -f mypass.yaml
secret/my-password created
Create a Secret from a File
To create a Secret from a File, we can use the kubectl create secret command.
First, we encode the sensitive data and then we write the encoded data to a text file:
$ echo mysqlpassword | base64
bXlzcWxwYXNzd29yZAo=
$ echo -n 'bXlzcWxwYXNzd29yZAo=' > password.txt
Now we can create the Secret from the password.txt file:
$ kubectl create secret generic my-file-password \ --from-file=password.txt
secret/my-file-password created
After successfully creating a secret we can analyze it with the get and describe commands. They do not reveal the content of the Secret. The type is listed as Opaque.
$ kubectl get secret my-file-password
NAME TYPE DATA AGE
my-file-password Opaque 1 8m
$ kubectl describe secret my-file-password
Name: my-file-password
Namespace: default
Labels: <none>
Annotations: <none>
Type Opaque
Data
\====
password.txt: 13 bytes
Use Secrets Inside Pods: As Environment Variables
Secrets are consumed by Containers in Pods as mounted data volumes, or as environment variables, and are referenced in their entirety (using the envFrom heading) or specific key-values (using the env heading).
Below we reference only the password key of the my-password Secret and assign its value to the WORDPRESS_DB_PASSWORD environment variable:
....
spec:
containers:
- image: wordpress:4.7.3-apache
name: wordpress
env:
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: my-password
key: password
....
Use Secrets Inside Pods: As Volumes
We can also mount a Secret as a Volume inside a Pod. The secret Volume plugin converts the Secret object into a mountable resource. The following example creates a file for each my-password Secret key (where the files are named after the names of the keys), the files containing the values of the respective Secret keys:
....
spec:
containers:
- image: wordpress:4.7.3-apache
name: wordpress
volumeMounts:
- name: secret-volume
mountPath: "/etc/secret-data"
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: my-password
........
spec:
containers:
- image: wordpress:4.7.3-apache
name: wordpress
volumeMounts:
- name: secret-volume
mountPath: "/etc/secret-data"
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: my-password
....
For more details, you can explore the documentation on managing Secrets.
Subscribe to my newsletter
Read articles from Md Nur Mohammad directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Md Nur Mohammad
Md Nur Mohammad
I am pursuing a Master's in Communication Systems and Networks at the Cologne University of Applied Sciences, Germany.