Projected Volumes

Overview

A projected volume maps several existing volume sources into the same directory.

Currently, the following types of volume sources can be projected:

All sources are required to be in the same namespace as the pod.

Projected volumes can map any combination of these volume sources into a single directory, allowing the user to:

  • automatically populate a single volume with the keys from multiple secrets, configmaps, and with downward API information, so that I can synthesize a single directory with various sources of information;

  • populate a single volume with the keys from multiple secrets, configmaps, and with downward API information, explicitly specifying paths for each item, so that I can have full control over the contents of that volume.

Example Scenarios

The following general scenarios show how you can use projected volumes.

  • ConfigMap, Secrets, Downward API. Projected volumes allow you to deploy containers with configuration data that includes passwords. An application using these resources could be deploying OpenStack on Kubernetes. The configuration data may need to be assembled differently depending on if the services are going to be used for production or for testing. If a pod is labeled with production or testing, the downward API selector metadata.labels can be used to produce the correct OpenStack configs.

  • ConfigMap + Secrets. Projected volumes allow you to deploy containers involving configuration data and passwords. For example, you might execute an Ansible playbook stored as a configmap, with some sensitive encrypted tasks that are decrypted using a vault password file.

  • ConfigMap + Downward API. Projected volumes allow you to generate a config including the pod name (available via the metadata.name selector). This application can then pass the pod name along with requests in order to easily determine the source without using IP tracking.

  • Secrets + Downward API. Projected volumes allow you to use a secret as a public key to encrypt the namespace of the pod (available via the metadata.namespace selector). This example allows the operator to use the application to deliver the namespace information securely without using an encrypted transport.

Example Pod Specifications

The following are examples of pod specifications for creating projected volumes.

Example 1. Pod with a secret, a downward API, and a configmap

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: volume-test
  5. spec:
  6. containers:
  7. - name: container-test
  8. image: busybox
  9. volumeMounts: (1)
  10. - name: all-in-one
  11. mountPath: "/projected-volume"(2)
  12. readOnly: true (3)
  13. volumes: (4)
  14. - name: all-in-one (5)
  15. projected:
  16. defaultMode: 0400 (6)
  17. sources:
  18. - secret:
  19. name: mysecret (7)
  20. items:
  21. - key: username
  22. path: my-group/my-username (8)
  23. - downwardAPI: (9)
  24. items:
  25. - path: "labels"
  26. fieldRef:
  27. fieldPath: metadata.labels
  28. - path: "cpu_limit"
  29. resourceFieldRef:
  30. containerName: container-test
  31. resource: limits.cpu
  32. - configMap: (10)
  33. name: myconfigmap
  34. items:
  35. - key: config
  36. path: my-group/my-config
  37. mode: 0777 (11)
1Add a volumeMounts section for each container that needs the secret.
2Specify a path to an unused directory where the secret will appear.
3Set readOnly to true.
4Add a volumes block to list each projected volume source.
5Specify any name for the volume.
6Set the execute permission on the files.
7Add a secret. Enter the name of the secret object. Each secret you want to use must be listed.
8Specify the path to the secrets file under the mountPath. Here, the secrets file is in /projected-volume/my-group/my-config.
9Add a Downward API source.
10Add a ConfigMap source.
11Set the mode for the specific projection

If there are multiple containers in the pod, each container needs a volumeMounts section, but only one volumes section is needed.

Example 2. Pod with multiple secrets with a non-default permission mode set

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: volume-test
  5. spec:
  6. containers:
  7. - name: container-test
  8. image: busybox
  9. volumeMounts:
  10. - name: all-in-one
  11. mountPath: "/projected-volume"
  12. readOnly: true
  13. volumes:
  14. - name: all-in-one
  15. projected:
  16. defaultMode: 0755
  17. sources:
  18. - secret:
  19. name: mysecret
  20. items:
  21. - key: username
  22. path: my-group/my-username
  23. - secret:
  24. name: mysecret2
  25. items:
  26. - key: password
  27. path: my-group/my-password
  28. mode: 511

The defaultMode can only be specified at the projected level and not for each volume source. However, as illustrated above, you can explicitly set the mode for each individual projection.

Pathing Considerations

When creating projected volumes, consider the following situations related to the volume file paths.

Collisions Between Keys when Configured Paths are Identical

If you configure any keys with the same path, the pod spec will not be accepted as valid. In the following example, the specified path for mysecret and myconfigmap are the same:

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: volume-test
  5. spec:
  6. containers:
  7. - name: container-test
  8. image: busybox
  9. volumeMounts:
  10. - name: all-in-one
  11. mountPath: "/projected-volume"
  12. readOnly: true
  13. volumes:
  14. - name: all-in-one
  15. projected:
  16. sources:
  17. - secret:
  18. name: mysecret
  19. items:
  20. - key: username
  21. path: my-group/data
  22. - configMap:
  23. name: myconfigmap
  24. items:
  25. - key: config
  26. path: my-group/data

Collisions Between Keys without Configured Paths

The only run-time validation that can occur is when all the paths are known at pod creation, similar to the above scenario. Otherwise, when a conflict occurs the most recent specified resource will overwrite anything preceding it (this is true for resources that are updated after pod creation as well).

Collisions when One Path is Explicit and the Other is Automatically Projected

In the event that there is a collision due to a user specified path matching data that is automatically projected, the latter resource will overwrite anything preceding it as before

Configuring a Projected Volume for a Pod

The following example shows how to use a projected volume to mount an existing Secret volume source.

The steps can be used to create a user name and password Secrets from local files. You then create a pod that runs one container, using a projected volume to mount the Secrets into the same shared directory.

  1. Create files containing the secrets:

    For example:

    1. $ nano secret.yaml

    Enter the following, replacing the password and user information as appropriate:

    1. apiVersion: v1
    2. kind: Secret
    3. metadata:
    4. name: mysecret
    5. type: Opaque
    6. data:
    7. pass: MWYyZDFlMmU2N2Rm
    8. user: YWRtaW4=

    The user and pass values can be any valid string that is base64 encoded. The examples used here are base64 encoded values user: admin, pass:1f2d1e2e67df.

    1. $ echo -n "admin" | base64
    2. YWRtaW4=
    3. $ echo -n "1f2d1e2e67df" | base64
    4. MWYyZDFlMmU2N2Rm
  2. Use the following command to create the secrets:

    1. $ oc create -f <secrets-filename>

    For example:

    1. $ oc create -f secret.yaml
    2. secret "mysecret" created
  3. You can check that the secret was created using the following commands:

    1. $ oc get secret <secret-name>
    2. $ oc get secret <secret-name> -o yaml

    For example:

    1. $ oc get secret mysecret
    2. NAME TYPE DATA AGE
    3. mysecret Opaque 2 17h
    1. oc get secret mysecret -o yaml
    2. apiVersion: v1
    3. data:
    4. pass: MWYyZDFlMmU2N2Rm
    5. user: YWRtaW4=
    6. kind: Secret
    7. metadata:
    8. creationTimestamp: 2017-05-30T20:21:38Z
    9. name: mysecret
    10. namespace: default
    11. resourceVersion: "2107"
    12. selfLink: /api/v1/namespaces/default/secrets/mysecret
    13. uid: 959e0424-4575-11e7-9f97-fa163e4bd54c
    14. type: Opaque
  4. Create a pod configuration file similar to the following that includes a volumes section:

    1. apiVersion: v1
    2. kind: Pod
    3. metadata:
    4. name: test-projected-volume
    5. spec:
    6. containers:
    7. - name: test-projected-volume
    8. image: busybox
    9. args:
    10. - sleep
    11. - "86400"
    12. volumeMounts:
    13. - name: all-in-one
    14. mountPath: "/projected-volume"
    15. readOnly: true
    16. volumes:
    17. - name: all-in-one
    18. projected:
    19. sources:
    20. - secret:
    21. name: user
    22. - secret:
    23. name: pass
  5. Create the pod from the configuration file:

    1. $ oc create -f <your_yaml_file>.yaml

    For example:

    1. $ oc create -f secret-pod.yaml
    2. pod "test-projected-volume" created
  6. Verify that the pod container is running, and then watch for changes to the Pod:

    1. $ oc get pod <name>

    The output should appear similar to the following:

    1. $ oc get pod test-projected-volume
    2. NAME READY STATUS RESTARTS AGE
    3. test-projected-volume 1/1 Running 0 14s
  7. In another terminal, use the oc exec command to open a shell to the running container:

    1. $ oc exec -it <pod> <command>

    For example:

    1. $ oc exec -it test-projected-volume -- /bin/sh
  8. In your shell, verify that the projected-volumes directory contains your projected sources:

    1. / # ls
    2. bin home root tmp
    3. dev proc run usr
    4. etc projected-volume sys var