I have created ConfigMap from Openshift 4 Windows 10 CLI:
.\oc create configmap my-cacerts --from-file=cacerts
I can see ConfigMap with name my-cacerts and download binary file cacerts from it using web interface of Openshift 4
Now I mount it (part of my-deployment.yaml)
containers:
volumeMounts:
- name: my-cacerts-volume
mountPath: /etc/my/cacerts
volumes:
- name: my-cacerts-volume
config-map:
name: my-cacerts
Unfortunately /etc/my/cacerts is mounted as a empty folder but not as a single binary file.
How can I mount cacerts as a file and not as a directory?
Update:
If I issue
.\oc get configmap my-cacerts
There is following output:
apiVersion: v1
binaryData:
cacerts: ... big long base64...
kind: ConfigMap
metadata: ...
If I issue
.\oc describe pod my-pod
I get
Volumes:
my-cacerts-volume:
Type: EmptyDir (a temporary directory that shares a pod's lifetime)
Your volumes definition is incorrect, config-map does not exist and is invalid, but it seems the API is silently falling back to an EmptyDir here, thus leading to an empty directory.
When you create a ConfigMap using the oc command above, the result will be a ConfigMap that looks like this (note that there is one key called "cacerts"):
apiVersion: v1
kind: ConfigMap
metadata:
name: my-cacerts
data:
cacerts: |
Hello world!
In the volumes section, then use configMap: together with subPath as follows to mount a only a single key ("cacerts") from your ConfigMap:
$ oc edit deployment my-deployment
[..]
spec:
containers:
- image: registry.fedoraproject.org/fedora-minimal:33
name: fedora-minimal
volumeMounts:
- mountPath: /etc/my/cacerts
name: my-cacerts-volume
subPath: cacerts
[..]
volumes:
- configMap:
name: my-cacerts
defaultMode: 420
name: my-cacerts-volume
This then results in:
$ oc rsh ...
sh-5.0$ ls -l /etc/my/cacerts
-rw-r--r--. 1 root 1000590000 13 Dec 3 19:11 /etc/my/cacerts
sh-5.0$ cat /etc/my/cacerts
Hello world!
You can also leave subPath out and set /etc/my/ as the destination for the same result, as for each key there will be a file:
[..]
volumeMounts:
- mountPath: /etc/my/
name: my-cacerts-volume
[..]
volumes:
- configMap:
name: my-cacerts
name: my-cacerts-volume
For the right syntax, you can also check the documentation
For Openshift 4 defaultMode should be specified:
volumeMounts:
- mountPath: /etc/my
name: cacerts-ref
readOnly: true
volumes:
- name: cacerts-ref
configMap:
defaultMode: 420
name: cacerts
After that configMap contents are mapped correctly.
.\oc describe pod my-pod
Volumes:
cacerts-ref:
Type: ConfigMap (a volume populated by a ConfigMap)
Name: cacerts
Optional: false
Related
This is my pod:
spec:
containers:
volumeMounts:
- mountPath: /etc/configs/config.tmpl
name: config-main
readOnly: true
subPath: config.tmpl
volumes:
- configMap:
defaultMode: 420
name: config-main
name: config-main
This is my config map:
apiVersion: v1
kind: ConfigMap
metadata:
name: config-main
data:
config.tmpl: |
# here goes my config content
This produces the following error:
Error: failed to create containerd task: failed to create shim: OCI
runtime create failed: container_linux.go:380: starting container
process caused: process_linux.go:545: container init caused:
rootfs_linux.go:76: mounting
"/var/lib/kubelet/pods/dc66ebd1-90ef-4c25-bb69-4b3329f61a5a/volume-subpaths/config-main/podname/1"
to rootfs at "/etc/configs/config.tmpl" caused: mount through procfd:
not a directory: unknown
The container has pre-existing /etc/configs/config.tmpl file that I want to override
To mount as file instead of directory try:
spec:
containers:
volumeMounts:
- mountPath: /etc/configs/config.tmpl
name: config-main
readOnly: true
subPath: config.tmpl
volumes:
- configMap:
defaultMode: 420
items:
- key: config.tmpl
path: config.tmpl
name: config-main
name: config-main
I've managed to make it work:
mountPath must be a directory
using subPath didn't work for me, anyway official doc says "using a ConfigMap as a subPath volume mount will not receive ConfigMap updates", which isn't an option for me
so I guess you can't mount a single file, you always mount a directory but then you can optionally limit which files from the configmap's data you want there via items in a volume.
spec:
containers:
volumeMounts:
- mountPath: /etc/configs
name: config-main
readOnly: true
volumes:
- configMap:
defaultMode: 420
name: config-main
name: config-main
I have a problem mounting 2 files in a pod, one is being treated as a directory for some reason (maybe stupid one, but I looked and looked, couldn't find a solution).
in my config folder there's 2 files:
config
|- log4j.properties
|- server.main.properties
Running StatefulSet, here's the Volumes part of the manifest file:
containers:
...
volumeMounts:
- mountPath: /usr/local/confluent/config/log4j.properties
name: log4j-properties
subPath: log4j.properties
- mountPath: /usr/local/confluent/config/server.properties
name: server-properties
subPath: server.properties
restartPolicy: Always
volumes:
- name: log4j-properties
configMap:
name: log4j-properties
defaultMode: 0777
- name: server-properties
configMap:
name: server-properties
defaultMode: 0777
volumeClaimTemplates:
- metadata:
name: confluent-persistent-storage
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
Created ConfigMaps:
kubectl create configmap server-properties --from-file=config/server.main.properties
kubectl create configmap log4j-properties --from-file=config/log4j.properties
kubectl describe pod gives mounted volumes as:
Volumes:
confluent-persistent-storage:
Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
ClaimName: confluent-persistent-storage-confluent-0
ReadOnly: false
server-properties:
Type: ConfigMap (a volume populated by a ConfigMap)
Name: server-properties
Optional: false
log4j-properties:
Type: ConfigMap (a volume populated by a ConfigMap)
Name: log4j-properties
Optional: false
It's being initialized for 5-6 minutes and in logs I can see that server.properties is not a file, but a folder, and when I exec into pod, I can see that the actual folder is been created instead of file.. What am I doing wrong here?
subPath: server.properties
Wouldn't you want to use it as below?
subPath: server.main.properties
I am having a config file as a secret in kubernetes and I want to mount it into a specific location inside the container. The problem is that the volume that is created inside the container is a folder instead of a file with the content of the secrets in it. Any way to fix it?
My deployment looks like this:
kind: Deployment
apiVersion: apps/v1
metadata:
name: jetty
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: jetty
template:
metadata:
labels:
app: jetty
spec:
containers:
- name: jetty
image: quay.io/user/jetty
ports:
- containerPort: 8080
volumeMounts:
- name: config-properties
mountPath: "/opt/jetty/config.properties"
subPath: config.properties
- name: secrets-properties
mountPath: "/opt/jetty/secrets.properties"
- name: doc-path
mountPath: /mnt/storage/
resources:
limits:
cpu: '1000m'
memory: '3000Mi'
requests:
cpu: '750m'
memory: '2500Mi'
volumes:
- name: config-properties
configMap:
name: jetty-config-properties
- name: secrets-properties
secret:
secretName: jetty-secrets
- name: doc-path
persistentVolumeClaim:
claimName: jetty-docs-pvc
imagePullSecrets:
- name: rcc-quay
Secrets vs ConfigMaps
Secrets let you store and manage sensitive information (e.g. passwords, private keys) and ConfigMaps are used for non-sensitive configuration data.
As you can see in the Secrets and ConfigMaps documentation:
A Secret is an object that contains a small amount of sensitive data such as a password, a token, or a key.
A ConfigMap allows you to decouple environment-specific configuration from your container images, so that your applications are easily portable.
Mounting Secret as a file
It is possible to create Secret and pass it as a file or multiple files to Pods.
I've create simple example for you to illustrate how it works.
Below you can see sample Secret manifest file and Deployment that uses this Secret:
NOTE: I used subPath with Secrets and it works as expected.
---
apiVersion: v1
kind: Secret
metadata:
name: my-secret
data:
secret.file1: |
c2VjcmV0RmlsZTEK
secret.file2: |
c2VjcmV0RmlsZTIK
---
apiVersion: apps/v1
kind: Deployment
metadata:
...
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- name: secrets-files
mountPath: "/mnt/secret.file1" # "secret.file1" file will be created in "/mnt" directory
subPath: secret.file1
- name: secrets-files
mountPath: "/mnt/secret.file2" # "secret.file2" file will be created in "/mnt" directory
subPath: secret.file2
volumes:
- name: secrets-files
secret:
secretName: my-secret # name of the Secret
Note: Secret should be created before Deployment.
After creating Secret and Deployment, we can see how it works:
$ kubectl get secret,deploy,pod
NAME TYPE DATA AGE
secret/my-secret Opaque 2 76s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx 1/1 1 1 76s
NAME READY STATUS RESTARTS AGE
pod/nginx-7c67965687-ph7b8 1/1 Running 0 76s
$ kubectl exec nginx-7c67965687-ph7b8 -- ls /mnt
secret.file1
secret.file2
$ kubectl exec nginx-7c67965687-ph7b8 -- cat /mnt/secret.file1
secretFile1
$ kubectl exec nginx-7c67965687-ph7b8 -- cat /mnt/secret.file2
secretFile2
Projected Volume
I think a better way to achieve your goal is to use projected volume.
A projected volume maps several existing volume sources into the same directory.
In the Projected Volume documentation you can find detailed explanation but additionally I created an example that might help you understand how it works.
Using projected volume I mounted secret.file1, secret.file2 from Secret and config.file1 from ConfigMap as files into the Pod.
---
apiVersion: v1
kind: Secret
metadata:
name: my-secret
data:
secret.file1: |
c2VjcmV0RmlsZTEK
secret.file2: |
c2VjcmV0RmlsZTIK
---
apiVersion: v1
kind: ConfigMap
metadata:
name: my-config
data:
config.file1: |
configFile1
---
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: all-in-one
mountPath: "/config-volume"
readOnly: true
volumes:
- name: all-in-one
projected:
sources:
- secret:
name: my-secret
items:
- key: secret.file1
path: secret-dir1/secret.file1
- key: secret.file2
path: secret-dir2/secret.file2
- configMap:
name: my-config
items:
- key: config.file1
path: config-dir1/config.file1
We can check how it works:
$ kubectl exec nginx -- ls /config-volume
config-dir1
secret-dir1
secret-dir2
$ kubectl exec nginx -- cat /config-volume/config-dir1/config.file1
configFile1
$ kubectl exec nginx -- cat /config-volume/secret-dir1/secret.file1
secretFile1
$ kubectl exec nginx -- cat /config-volume/secret-dir2/secret.file2
secretFile2
If this response doesn't answer your question, please provide more details about your Secret and what exactly you want to achieve.
I've multiple secrets created from different files. I'd like to store all of them in common directory /var/secrets/. Unfortunately, I'm unable to do that because kubernetes throws 'Invalid value: "/var/secret": must be unique error during pod validation step. Below is an example of my pod definition.
apiVersion: v1
kind: Pod
metadata:
labels:
run: alpine-secret
name: alpine-secret
spec:
containers:
- command:
- sleep
- "3600"
image: alpine
name: alpine-secret
volumeMounts:
- name: xfile
mountPath: "/var/secrets/"
readOnly: true
- name: yfile
mountPath: "/var/secrets/"
readOnly: true
volumes:
- name: xfile
secret:
secretName: my-secret-one
- name: yfile
secret:
secretName: my-secret-two
How can I store files from multiple secrets in the same directory?
Projected Volume
You can use a projected volume to have two secrets in the same directory
Example
apiVersion: v1
kind: Pod
metadata:
labels:
run: alpine-secret
name: alpine-secret
spec:
containers:
- command:
- sleep
- "3600"
image: alpine
name: alpine-secret
volumeMounts:
- name: xyfiles
mountPath: "/var/secrets/"
readOnly: true
volumes:
- name: xyfiles
projected:
sources:
- secret:
name: my-secret-one
- secret:
name: my-secret-two
(EDIT: Never mind - I just noticed #Jonas gave the same answer earlier. +1 from me)
Starting with Kubernetes v1.11+ it is possible with projected volumes:
A projected volume maps several existing volume sources into the same
directory.
Currently, the following types of volume sources can be projected:
secret
downwardAPI
configMap
serviceAccountToken
This is an example for "... how to use a projected Volume to mount several existing volume sources into the same directory".
May be subPath (using subPath) will help.
Example:
volumeMounts:
- name: app-redis-vol
mountPath: /app/config/redis.yaml
subPath: redis.yaml
- name: app-config-vol
mountPath: /app/config/app.yaml
subPath: app.yaml
volumes:
- name: app-redis-vol
configMap:
name: config-map-redis
items:
- key: yourKey
path: redis.yaml
- name: app-config-vol
configMap:
name: config-map-app
items:
- key: yourKey
path: app.yaml
Here your configMap named config-map-redis created from file redis.yaml mounted in app/config/ as file redis.yaml.
Also configMap config-map-app mounted in app/config/ as app.yaml
There is nice article about this here: Injecting multiple Kubernetes volumes to the same directory
Edited:
#Jonas answer is correct!
However, if you use volumes as I did in the question then, short answer is you cannot do that, You have to specify mountPath to an unused directory - volumes have to be unique and cannot be mounted to common directory.
Solution:
What I did at the end was, instead keeping files in separate secrets, I created one secret with multiple files.
I'm having hard time configuring mountPath as a relative path.
Let's say I'm running the deployment from /user/app folder and I want to create secret file under /user/app/secret/secret-volume as follows:
apiVersion: v1
kind: Pod
metadata:
name: secret-test-pod
spec:
containers:
- name: test-container
image: nginx
volumeMounts:
# name must match the volume name below
- name: secret-volume
mountPath: secret/secret-volume
# The secret data is exposed to Containers in the Pod through a Volume.
volumes:
- name: secret-volume
secret:
secretName: test-secret
For some reason the file secret-volume is created in the root directory /secret/secret-volume.
It's because you have mountPath: secret/secret-volume change it to mountPath: /user/app/secret/secret-volume
Check documentation here.