As a comprehensive container orchestration platform, Kubernetes offers a fully-featured platform to support all your application needs from container lifecycle management to networking services, etc. Managing application configurations and sensitive data such as passwords is a core consideration in almost any software development. Kubernetes uses ConfigMaps and Secrets to facilitate this need in containerized application development.
Let’s assume there is an application container that requires database access. In that case, hardcoding a single database configuration within that container will not be a good solution as it will create a doubt on which database to specify. We cannot use the production database in the staging environment or staging databases in production with different credentials, which can cause security implications. In this instance, K8s ConfigMaps provides the ideal way to pass configuration data, while K8s Secrets provides a way to securely pass the database credentials within these two environments.
The main purpose of using a ConfigMap is to decouple the application configuration data from the application itself. This separation improves the reusability of the container as a decoupled architecture enables developers to use and test containerized applications under different configurations.
However, ConfigMaps are not designed to handle large amounts of data and are capped at 1MB. Developers need to look for other solutions such as volumes or external data sources if configuration data exceeds that limit. Thus, ConfigMaps are ideal for implementing non-sensitive configuration data less than 1MB in total size.
On the other hand, secrets provide the solution to decouple sensitive data from containers or pod configurations. This disconnect essentially prevents the need to include confidential data within the application code.
As an independent entity, secrets also ensure that this confidential data will not be exposed during the Pod creation or modification process. Kubernetes will also treat secrets differently and will abstain from writing them to nonvolatile storage. Kubernetes offers different kinds of secrets for specific use cases.
The primary difference between these two is that while ConfigMaps are designed to store any type of non-sensitive application data, Secrets are designed to store sensitive application data such as passwords, tokens, etc. Moreover, Secrets must adhere to the specific secret types that determine which kind of data can be handled by the secret.
Both these K8s objects can be created either via kubectl or a YAML file independently of any other process or object. Now, let’s look at creating YAML-based configuration files to host our database connectivity data and authentication information.
These ConfigMaps can also be created using different data sources such as files, Envfiles, etc. In this example, we will create a simple YAML that contains the data.
apiVersion: v1 kind: ConfigMap # Metadata of the ConfigMap metadata: name: app-database-config namespace: default # The Configuration Data data: server.host: "10.10.10.245" server.port: "3660" db.name: web-application
As secrets are defined by type, let’s create a basic-auth type Secret that contains the database username and password.
apiVersion: v1 kind: Secret # Metadata of the Secret metadata: name: app-database-auth namespace: default # Secret Type type: kubernetes.io/basic-auth # Secret Data (This changes depending on the type) stringData: username: admin password: admin
Then these YAML and Secret can be created using the kubectl apply command as shown below.
kubectl apply -f .\\db-configmap.yaml kubectl apply -f .\\db-secret.yaml
Now you know how to create ConfigMaps and Secrets. Yet, how can you read these objects? We can simply use the kubectl describe command to view our objects.
Reading a ConfigMap
Use the following command with the appropriate ConfigMap name defined in the metadata section
kubectl describe configmap app-database-config
As secrets contain confidential data, the describe command does not expose this secret data even when the user query through the kubectl using the specific secret name.
kubectl describe secret app-database-auth
In this section, we will see how to use these K8s objects in our containerized applications.
ConfigMaps can be mounted as data volumes or configured as environmental variables using the configMapKeyRef flag. We can use both these approaches in our use case for database connectivity information. However, we will go ahead with mounting ConfigMaps as data volumes approach as those volumes are automatically updated.
apiVersion: v1 kind: Pod metadata: name: test-web-app labels: app: test-web-app spec: # Specify the Container Details containers: - name: ubuntu-app image: ubuntu:latest command: ["/bin/sleep", "3650d"] # Setup the volumeMounts volumeMounts: - name: database-config mountPath: /etc/config resources: requests: memory: "250Mi" cpu: "0.5" limits: memory: "1024Mi" cpu: "1" # Create a volume pointing to the ConfigMap volumes: - name: database-config configMap: name: app-database-config restartPolicy: Always
When mounting a ConfigMap as a volume, users need to specify the volume with the “configMap.name” field referencing the created ConfigMap and use the volume name to reference it within the container specifications.
Secrets can also be mounted as volumes that update automatically when the underlying Secret is updated
apiVersion: v1 kind: Pod metadata: name: test-web-app labels: app: test-web-app spec: # Specify the Container Details containers: - name: ubuntu-app image: ubuntu:latest command: ["/bin/sleep", "3650d"] # Setup the volumeMounts volumeMounts: - name: database-authentication mountPath: /etc/authentication resources: requests: memory: "250Mi" cpu: "0.5" limits: memory: "1024Mi" cpu: "1" # Create a volume pointing to the Secret volumes: - name: database-authentication secret: secretName: app-database-auth restartPolicy: Always
Simply mount these as two distinct volumes as shown below to include both the ConfigMap and Secret so that all the details will be available for the application.
apiVersion: v1 kind: Pod metadata: name: test-web-app labels: app: test-web-app spec: containers: - name: ubuntu-app image: ubuntu:latest command: ["/bin/sleep", "3650d"] # Setup the volumeMounts volumeMounts: # ConfigMap Mount - name: database-config mountPath: /etc/config # Secret Mount - name: database-authentication mountPath: /etc/authentication resources: requests: memory: "250Mi" cpu: "0.5" limits: memory: "1024Mi" cpu: "1" volumes: # ConfigMap Volume - name: database-config configMap: name: app-database-config # Secret Volume - name: database-authentication secret: secretName: app-database-auth restartPolicy: Always
The main issue with the built-in secret mechanism is the default unencrypted nature of etcd where data is stored. Usually, any administrative user will have direct access to the secret data. To mitigate this issue, users need to manually enable encryption at rest and configure role-based access control to limit access to etcd. Additionally, we can see the following common problems.
ConfigMaps and Secrets are core K8s objects that enable users to decouple non-sensitive and sensitive data respectively from other objects, creating a more reusable and secure architecture for your application.