1. Overview
Secrets and ConfigMaps are Kubernetes resources designed to separate the configuration and sensitive information from the application code in a centralized manner. However, each of them has specific use cases and exhibits distinct behavior.
In this tutorial, we’ll explore some prominent differences between ConfigMaps and Secrets in Kubernetes.
2. Creation and Data Encoding
In this section, we will explore some important distinctions between ConfigMaps and Secrets when it comes to their creation.
First, let’s take a look at the my-config.yaml manifest file that shows us different types of fields that we can hold within a ConfigMap:
$ cat my-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: my-configmap
data:
string-value: "Hello, world!"
number-value: "42"
boolean-value: "true"
multiline-value: |
This is a multiline value
that spans multiple lines
list-value: |
- item1
- item2
- item3
object-value: |
key1: value1
key2: value2
json-value: |-
{
"key": "value",
"array": [1, 2, 3],
"nested": {
"innerKey": "innerValue"
}
}
yaml-value: |-
key: value
array:
- 1
- 2
- 3
nested:
innerKey: innerValue
We must note that ConfigMap supports key-value pairs in plaintext format. Further, we need to specify all these values under the data field.
Now, let’s apply the configuration to create the my-config ConfigMap:
$ kubectl apply -f my-config.yaml
configmap/my-config created
Further, let’s see how we can access one of the values from the newly created my-configmap:
$ kubectl get configmap my-configmap -o jsonpath='{.data.string-value}'
Hello, world!
Next, let’s look into the my-secret.yaml manifest file for creating the my-secret Secret:
$ cat my-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
data:
string-value: SGVsbG8sIHdvcmxkIQ==
number-value: NDI=
boolean-value: dHJ1ZQ==
multiline-value: |
VGhpcyBpcyBhIG1pbmxpbmUgdmFsdWUgZXZlcnkgdGhhdCBzcGFuIG11bHRpcGxlIGxpbmVzCg==
json-value: eyAiY29tcGxleCI6ICIxMjM0NSIsICJzdHJpbmciOiAiSGVsbG8sIHdvcmxkISIsICJuZXN0ZWQiOiB7ICJpbnN0YW5jZSI6IDEsICJzdHJpbmciOiAxLCAibmVzdGVkIjogeyAiaW5uZXJLZXkiOiAiSW5uZXJWYWx1ZSJ9fX0=
binary-value: YmFzZTY0IHN0cmluZwo=
stringData:
list-value: |
item1
item2
item3
object-value: |
key1: value1
key2: value2
key3: value3
yaml-value: |
key: value
string: Hello, world!
nested:
innerKey: innerValue
It’s important to note that some values are in plaintext format, while some are in Base64-encoded format. Additionally, we specify the Base64-encoded values under the data field, whereas the plaintext-formatted values are available under the stringData field.
Moving on, let’s go ahead and create the my-secret Secret:
$ kubectl apply -f my-secret.yaml
secret/my-secret created
Lastly, we must understand that Kubernetes encodes the values under the stringData field before storing them under the data field. So, we need to access all the secret values using a path relative to the data field only:
$ kubectl get secret my-secret -o jsonpath='{.data.string-value}' | base64 -d
Hello, world!
$ kubectl get secret my-secret -o jsonpath='{.data.yaml-value}' | base64 -d
key: value
string: Hello, world!
nested:
innerKey: innerValue
It’s interesting to note that, unlike ConfigMap values, we must explicitly decode the Secret values to see them in the plaintext format.
3. Environment Variables
Now that we’ve learned to create ConfigMap and Secret, we can inject them as environment variables into a Kubernetes pod. So, let’s go ahead and look into the pod.yaml manifest file that uses an ubuntu image to run inside the container:
$ cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: ubuntu-container
image: ubuntu
command: ["sleep", "infinity"]
env:
- name: SECRET_VALUE
valueFrom:
secretKeyRef:
name: my-secret
key: string-value
- name: CONFIG_VALUE
valueFrom:
configMapKeyRef:
name: my-configmap
key: number-value
We must note that we used the secretKeyRef field to refer to the my-secret Secret, and configMapKeyRef to refer to the my-configmap ConfigMap. Further, we named the environment variables as SECRET_VALUE and CONFIG_VALUE.
Next, let’s apply the configuration to create the my-pod pod in the Kubernetes cluster:
$ kubectl apply -f pod.yaml
pod/my-pod created
Finally, let’s use the kubectl exec command to check the environment variables inside the my-pod:
$ kubectl exec my-pod -- env | grep -i VALUE
CONFIG_VALUE=42
SECRET_VALUE=Hello, world!
Great! The environment variables are defined as expected. However, it’s worth noting that *although the *string-value**Secret was defined in Base64 encoding, it’s decoded into plaintext format while populating the value for the SECRET_VALUE environment variable**.
4. Built-in Types and Usage
The Kubernetes Secret resource supports several in-built types, such as Opaque, service-account-token, tls, dockerconfigjson, and so on. On the other hand, ConfigMaps don’t have any specific types. Let’s go through some of these types and their usage in this section.
4.1. Opaque Secrets
We can use the Opaque Secrets for storing arbitrary sensitive information in a Kubernetes cluster. To understand the usage, let’s start by defining the mysql-secret to contain the credentials for a MySQL database:
$ cat mysql-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
type: Opaque
data:
username: bXktcHJvdG9jb2xvcmVk
password: bXktcGFzc3dvcmQ=
root-password: bXktcm9vdC1wYXNzd29yZA==
Now, let’s define the mysql-pod.yaml manifest to hold the configuration for the mysql-pod:
$ cat mysql-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: mysql-pod
spec:
containers:
- name: mysql-container
image: mysql
env:
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: mysql-secret
key: username
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: root-password
ports:
- containerPort: 3306
restartPolicy: Never
Next, let’s create the Secret and Pod resources using the mysql-secret.yaml and mysql-pod.yaml manifest files, respectively:
$ kubectl apply -f mysql-secret.yaml -f mysql-pod.yaml
secret/mysql-secret created
pod/mysql-pod created
Lastly, let’s validate that the MySQL server is up and running inside the mysql-pod pod:
$ kubectl exec -it mysql-pod -- mysql -uroot -pmy-root-password -e "SELECT VERSION();"
mysql: [Warning] Using a password on the command line interface can be insecure.
+-----------+
| VERSION() |
+-----------+
| 8.0.33 |
+-----------+
Great! We’ve got this one right.
4.2. tls Secrets
Now, let’s explore how to create tls secrets and use them inside the pods.
First, let’s generate the TLS private key in the tls.key file and certificate in the tls.crt file for demonstration purposes:
$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt
Now, let’s see how we can create the tls secret using the custom options –cert and –key:
$ kubectl create secret tls my-tls-secret --cert=tls.crt --key=tls.key
secret/my-tls-secret created
So, we must note that the kubectl create* command supports a few custom options for Secrets, which is not applicable for *ConfigMaps.
Next, let’s look at the manifest file for the tls-pod that’s going to mount the tls secret at the /etc/tls path:
$ cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: tls-pod
spec:
containers:
- name: ubuntu
image: ubuntu
command: ["sleep", "infinity"]
volumeMounts:
- name: tls-volume
mountPath: /etc/tls
readOnly: true
volumes:
- name: tls-volume
secret:
secretName: my-tls-secret
Moving on, let’s create the tls-pod using the pod.yaml manifest file:
$ kubectl apply -f pod.yaml
pod/tls-pod created
Lastly, let’s verify that the secrets are available inside the container:
$ kubectl exec tls-pod -- ls /etc/tls/
tls.crt
tls.key
Perfect! We’ve got the relevant files inside the container for further use.
Based on our exploration, we can safely infer that compared to ConfigMaps, Secrets have a much broader scope, support, and usage within Kubernetes.
5. AutoMounting
Although we can explicitly mount both ConfigMaps and Secrets as volumes inside containers, only secrets of specific types, such as service-account-token, support the automounting feature. For this purpose, we need to specify the service account in the pod’s specification and set the automountServiceAccountToken field to true.
Let’s go ahead and look at the sa-ubuntu.yaml file that contains the configuration for the my-service-account service account and the ubuntu-pod pod:
$ cat sa-ubuntu.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-service-account
---
apiVersion: v1
kind: Pod
metadata:
name: ubuntu-pod
spec:
serviceAccountName: my-service-account
containers:
- name: ubuntu-container
image: ubuntu
command: ["sleep", "infinity"]
automountServiceAccountToken: true
Next, let’s use this manifest to create the my-service-account and ubuntu-pod pod:
$ kubectl apply -f sa-ubuntu.yaml
serviceaccount/my-service-account created
pod/ubuntu-pod created
Lastly, we can verify that a token is automatically mounted at the /var/run/secrets/kubernetes.io/serviceaccount/token path:
$ kubectl exec ubuntu-pod -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
# token is hidden
Fantastic! We’ve discovered one more feature of Kubernetes Secrets that is absent from ConfigMaps.
6. Feature Comparision
Now, we’ve developed a good understanding of Kubernetes ConfigMap and Secret. So, let’s use our understanding to draw a feature comparison summary between the two:
Comparison Criteria
ConfigMap
Secret
Data Classification
Stores non-sensitive information
Stores sensitive information
Data Encoding
Stores data in original format
Stores data in base64 format
Types and Usage
No specific type, and used for storing configuration data
Several in-built types, such as Opaque, tls, dockerconfigjson, each for a distinct usage
API Support
kubectl API provides generic support to interact with ConfigMap
kubectl API provides broader support for different types of Secret
Mounting as volumes
Only support explicit mounting
Supports automounting for service-account-token
7. Conclusion
In this article, we compared the features of Secrets and ConfigMaps in Kubernetes.
Furthermore, we explored how to create them, use them as environment variables, explicitly mount them, and enable automount for the service account tokens.