• Discuss configuration management for applications in Kubernetes using ConfigMaps.
  • Share sensitive data (such as passwords) using Secrets.

ConfigMaps

ConfigMaps allow us to decouple the configuration details from the container image. In the form of environment variables, sets of commands and arguments, or volumes, we can pass configuration data, which then are consumed by Pods. We can create ConfigMaps from literal values, from configuration files, from one or more files or directories.

Create from Literal Values

As with other objects we can create and display ConfigMaps by using the kubectl CLI.
We are going to create the ConfigMap and display the Details

$ kubectl create configmap my-config --from-literal=key1=value1 --from-literal=key2=value2
configmap/my-config created

$ kubectl get configmaps my-config -o yaml

We can see in the output, in yaml format, our passed values in the data field. The name and other details are part of the metadata field.

Create from Configuration File

We specify the kind, metadata and data fields, targeting the v1 endpoint.

apiVersion: v1
kind: COnfigMap
metadata:
  name: customer1
data:
  TEXT1: Customer1_Company
  TEXT2: Welcomes You
  COMPANY: Customer1 Company Technology Ltd.
$ kubectl create -f customer1-configmap.yaml
configmap/customer1 created

Create a ConfigMap from a File

We are going to create a file called permission-reset.properties

permission=read-only
allowed="true"
resetCount=3

And create the ConfigMap with kubectl.

$ kubectl create configmap permission-config --from-file=./permission-reset.properties
configmap/permission-config created

Use ConfigMaps inside Pods

Inside the container, we retrieve key-value data or values as environment variables.

...
  containers:
  - name: myapp-full-container
    image: myapp
    envFrom:
    - configMapRef:
      name: full-config-map
...

This configuration will give the myapp-full-container the values of full-config-map as environment variables.
It is also possible to specify two seperate ConfigMaps from which the container should receive its environment variables.

...
  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
...

We will get the SPECIFIC_ENV_VAR1 variable set to SPECIFIC_DATA key of config-map-1, and SPECIFIC_ENV_VAR2 set to SPECIFIC_INFO key from config-map-2.

A Volume can be configured inside a Pod. For each key in the ConfigMap, a file gets created inthe mount path and the content of that file becomes the respective key's value:

...
  containers:
  - name: myapp-vol-container
    image: myapp
    volumeMounts:
    - name: config-volume
      mountPath: /etc/config
  volumes:
  - name: config-volume
    configMap:
      name: vol-config-map
...

How to configure pod ConfigMaps can be found here. Also some Hands on examples are given.

Secrets

Secrets are similar to ConfigMaps, but we are able to control how the information in a Secre is used, reducing the risk for accidental exposures. In Deployments or other resources, the Secret object is referenced, wihtout exposing its content.

Caution:
Kubernetes Secrets are, by default, stored as unencrypted base64-encoded strings. By default they can be retrieved - as plain text - by anyone with API access, or anyone with access to Kubernetes' underlying data store, etcd. In order to safely use Secrets, we recommend you (at a minimum):
Enable Encryption at Rest for Secrets.
Enable RBAC rules that restrict reading and writing the Secret. Be aware that secrets can be obtained implicitly by anyone with the permission to create a Pod.

Create a Secret from literal

We can create secrets by using the kubectl CLI and view the details, although they will not reveal the content of the Secret.

$ kubectl create secret generic my-password --from-literal=password=mysqlpassword
secret/my-password created

$ kubectl get secret my-password
NAME          TYPE     DATA   AGE
my-password   Opaque   1      27s

$ kubectl describe secret my-password
Name:         my-password
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
password:  13 bytes

Create a Secret Manually

First we encode the password using base64 and then use it in the configuration file.

echo mysqlpassword |base64
bXlzcWxwYXNzd29yZAo=
---
apiVersion: v1
kind: Secret
metadata:
  name: my-password
type: Opaque
data:
  password: bXlzcWxwYXNzd29yZAo=

Note that any one can decode our encoded string, base64 doesn't mean encryption.
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 password Secret is created. Using mypass.yaml we can use the kubectl create command.

apiVersion: v1
kind: Secret
metadata:
  name: my-password
type: Opaque
stringData:
  password: mysqlpassword
$ kubectl create -f mypass.yaml
secret/my-password created

Create a Secret from File and display its details

We are going to encode our sensitive data and then write encoded data to a Text file.

$ echo mysqlpassword | base64

bXlzcWxwYXNzd29yZAo=

$ echo -n 'bXlzcWxwYXNzd29yZAo=' > password.txt

Create the secret from the password.txt file, get and describe it.

$ kubectl create secret generic my-file-password --from-file=password.txt
secret/my-file-password created

$ kubectl get secret my-file-password 
NAME               TYPE     DATA   AGE
my-file-password   Opaque   1      77s

$ kubectl describe secret my-file-password
Name:         my-file-password
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
password.txt:  20 bytes

Use Secrets inside pods

Just like ConfigMaps, Secrets are consumed by containers in pods as mounted data volumes or as environment variables.
Using secrets as Environment Variables, we'll assing the password key of the my-password Secret.

...
spec:
  containers:
  - image: wordpress:4.7.3-apache
    name: wordpress
    env:
    - name: WORDPRESS_DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: my-password
          key: password
...

Using secrets as Files from a Pod, where we mount a Secret as a Volume inside a Pod.

....
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 and some examples you can visit Managing Secrets.

Hands On

We are going to alter the index.html file inside an nginx container by providing a ConfigMap, lets assume following web-config.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: web-config
  namespace: default
data:
  STRING: Welcome to MY-NGINX!
  PATH: /usr/share/nginx/html/index.html

We'll create the object and display the web-config with two data fields.

$ kubectl create -f web-config.yaml
configmap/web-config created

$ kubectl describe cm
Name:         web-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
PATH:
----
/usr/share/nginx/html/index.html
STRING:
----
Welcome to MY-NGINX!
Events:  <none>

Let's assume a app-config.yaml where we create a Pod with a nginx container, which replaces the content of the given DATA_STRING on the given DATA_PATH.
Both these variables will be passed in as environment Variables via the ConfigMap we just created.

apiVersion: v1
kind: Pod
metadata:
  name: app-config
spec:
  containers:
  - name: nginx
    image: nginx
    command: ["/bin/sh", "-c", "echo $(DATA_STRING) > $(DATA_PATH); sleep 3600" ]
    env:
      - name: DATA_STRING
        valueFrom:
          configMapKeyRef:
            name: web-config
            key: STRING
            optional: true
      - name: DATA_PATH
        valueFrom:
          configMapKeyRef:
            name: web-config
            key: PATH
            optional: true
  restartPolicy: Never
$ kubectl create -f app-config.yaml
pod/app-config created

This time we will observe the value inside the container by using the kubectl exec command. And by cat index.html we will see the content of the file.

$ kubectl exec app-config -- /bin/sh -c 'cat /usr/share/nginx/html/index.html'
Welcome to MY-NGINX!

As we expected the content of the file will be the value we have passed in by the ConfigMap.

The Secrets part of this Hands On would be almost the same. This is how we consume ConfigMaps in our Pods containers. A good way to provide different values for the same containers, without creating several similar containers.

That's it for today folks! There is much more to read and to configure out here. We only went through a simple example, we also know how to consume environmental values in our containers. Be it php, nodejs, .net and what not. This way we will give simple values as well as sensitive information in our containers.