Providing config information to containers in Kubernetes

Secrets and configmaps allow passing information to containers, whether it is regular configuration or sensitive information. They can define individual pieces of information like a password or username, or be used to store more extensive information such as entire configuration files. There are multiple ways to set them up and provide them to the container, so I embraced using them because of the flexibility. Using them also made it easier for me to provide configuration to containers that I would have otherwise had to build a custom image for. With configmaps/secrets I could set up key config files or ENV variables in my deployments instead of baking those into an image.

Setting up a configmap with the help of ansible

I do most of my cluster configuration with ansible, so I will use an ansible configmap setup example, and then demonstrate different variations. The two plays are used to check for a configmap and then set it up if it does not exist.

  - name: check if fileBeat configmap has already been created
    k8s_facts:
      api_version: v1
      kind: ConfigMap
      namespace: example
      name: example-config
    register: example_configmap

  - name: Set up the configMap to hold my filebeat config
    shell: "kubectl create configmap -n example example-config --from-file=/path/example_k8s.yml"
    when: example_configmap.resources is not defined

The command for creating the configmap

In this case I am creating the configmap from a file. It is also possible to create them from directories (each file gets mapped to a key) or from multiple files by repeating the —from-file key. There is also a literal value option to do it all on the cli.
Files fed to the configmap are saved as data associated with keys Configmaps can store multiple pieces of data or files. Each is saved/accessed as a key. In this example configmap, the key to the file would be the same as the name of the file, filebeat_k8s.yml, since no key was specified.

kubectl create configmap -n example example-config --from-file=/path/example_k8s.yml

Specify a key name

This command could also have been written like this to specify a key:

kubectl create configmap configname --from-file=keyname=/path/to/file.name  

Creating a configmap with Multiple files constained

Reuse the —from-file argument, specify a different file and key.

kubectl create configmap configname --from-file=keyname=/path/to/file.name  --from-file=keyname2=/path/to/otherfile 
Using A directory with files in it as the source

The same —from-file argument it used, but it is going to look for all files in the directory, and add them all to the configmap with their names as the key for each.

kubectl create configmap configname --from-file=keyname=/path/to/dir/  

You can also use literal values
Configmaps can be created from literal values provided on the cli.

kubectl create configmap configname --from-literal=key.path=value

Attaching the configmap to a deployment uses a volume

You can also use environment variables, but my experience is with using files so I will demonstrate that. You have to be aware of what key you gave to the item you want to attach.
Example of setting up configmap keys using a volume
name: - the name of the configmap you created.
key: - the key to the data you want to access from that configmap.
path: - the path to place the file at, which will be relative to the
path for the entire volume.

// Set up the configmap volume.  
  volumes:
  - name: config
    configMap:
      defaultMode: 0600
      name: example-config
      items:
        - key: example_k8s.yml
          path: example.yml
        - key: another_file.txt
          path: another.txt
// Attach the configmap volume to a container in a deploy/daemonset/statefulset
  volumeMounts:
  - name: config
    readOnly: true
    mountPath: /etc/config

Given the above config, the file contained in the configmap would be mounted at /etc/config/filebeat.yml. I also added another key/path pair just to demonstrate that more than one key can be added. It would be found at /etc/config/another.txt in this example.

Setting up secrets for containers

Base64 format is preferred to avoid any character encoding problems. If you want to pass secrets directly, this will be the method to use.

echo -n 'username' | base64
echo -n 'randompassword' | base64

Creating a secret from a config file

This example uses the values from above base64 encoded as an example.
secret.yml:

apiVersion: v1  
kind: Secret  
metadata:  
  name: test-secret  
  namespace: default  
data:  
  username: dXNlcm5hbWU
  password: cmFuZG9tcGFzc3dvcmQ

Then use kubectl apply on this config file
kubectl apply -f /path/to/secret.yaml

Data in secrets is mapped to keys just like in configmaps

So there would be a username and password file in this case, each holding the respective pieces of data.

Secrets can also be created directly on the command line -

kubectl create secret generic test-secret --from-literal=username='nameforuser'   
\--from-literal=password='cmFuZG9tcGFzc3dvcmQ'  

Copy a secret from one namespace to another
They are namespace specific, so if you should happen to need to transfer them, this is how to go about doing that.

kubectl get secret -n default secretname --export -o yaml | \  
kubectl apply -n othernamespace -f 

Using secrets within a container

My preferred method is once again a volume and volumemount, but you could also set them up as environment variables. In this example I mount two separate secrets, on which has redis password, and the other which has a database username and password.

# The paths for each secrete are determined based on the volumeMounts.  
# I used these paths for docker-compose kubernetes compatibility.  
    volumeMounts:
    - name: db-cred
      mountPath: /run/secrets/psdb
    - name: redis-cred
      mountPath: /run/secrets/redis

# If you don't specify key/path, all the keys in the secret will be mounted  
# with filenames matching the keys. In this case I am specifying which  
# keys to use and the names for the files that will hold them.  
    volumes:
    - name: db-cred
      secret:
        secretName: db-cred
        items:
        - key: username
          path: username.txt
        - key: password
          path: password.txt
    - name: redis-cred
      secret:
        secretName: redis-cred
        items:
        - key: password
          path: password.txt

This is the same as how configmaps are mounted

You might have noticed that this process is the same as for configmaps. These two concepts are very similar. You could potentially use configmaps for secrets and vice versa, but the separate commands are at least a good way to separate out data that is sensitive.