Kubernetes:如何设置 VolumeMount 用户组和文件权限
Kubernetes: how to set VolumeMount user group and file permissions
我正在 运行使用 kops 在 AWS 上建立一个 Kubernetes 集群。我已将一个 EBS 卷安装到容器上,它在我的应用程序中是可见的,但它是只读的,因为我的应用程序没有 运行 作为 root。如何以 root 以外的用户身份挂载 PersistentVolumeClaim
? VolumeMount
似乎没有任何选项来控制挂载路径的用户、组或文件权限。
这是我的部署 yaml 文件:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: notebook-1
spec:
replicas: 1
template:
metadata:
labels:
app: notebook-1
spec:
volumes:
- name: notebook-1
persistentVolumeClaim:
claimName: notebook-1
containers:
- name: notebook-1
image: jupyter/base-notebook
ports:
- containerPort: 8888
volumeMounts:
- mountPath: "/home/jovyan/work"
name: notebook-1
Pod 安全上下文支持设置 fsGroup
,它允许您设置拥有该卷的组 ID,以及谁可以写入它。文档中的示例:
apiVersion: v1
kind: Pod
metadata:
name: hello-world
spec:
containers:
# specification of the pod's containers
# ...
securityContext:
fsGroup: 1234
有关此的更多信息here
对于k8s 1.10+版本,添加了runAsGroup
,它与fsGroup
类似,但工作方式不同。
可以在此处跟踪实施情况:https://github.com/kubernetes/features/issues/213
我最终得到了一个 initContainer
与主容器相同的 volumeMount
来设置适当的权限,在我的例子中,是自定义 Grafana 图像。
当 pod 中的容器 运行 宁作为 root
以外的用户并且需要对已安装卷的写入权限时,这是必需的。
initContainers:
- name: take-data-dir-ownership
image: alpine:3
# Give `grafana` user (id 472) permissions a mounted volume
# https://github.com/grafana/grafana-docker/blob/master/Dockerfile
command:
- chown
- -R
- 472:472
- /var/lib/grafana
volumeMounts:
- name: data
mountPath: /var/lib/grafana
更新: 请注意,在没有 -R
(递归)标志的情况下 运行 chown
可能就足够了,因为权限通常会在 pod 重新启动时保持不变。如果卷中有大量文件,这将是可取的,因为处理所有文件需要时间(取决于为 initContainer
设置的 resources
限制)。
这是 Kubernetes Deployments/StatefulSets 面临的挑战之一,当您必须 运行 作为非根用户在容器内进行处理时。但是,当您将卷安装到 pod 时,它总是在 root:root
的许可下安装。
因此,非root用户必须有权访问要读写数据的文件夹。
请按照以下步骤操作。
- 创建用户组并在 Dockerfile 中分配组 ID。
- 使用用户 ID 创建用户并添加到 Dockerfile 中的组。
- 递归更改用户进程想要的文件夹的所有权read/write。
在 pod spec
上下文的 Deployment/StatefulSet 中添加以下行。
spec:
securityContext:
runAsUser: 1099
runAsGroup: 1099
fsGroup: 1099
运行AsUser
指定对于 Pod 中的任何容器,所有进程 运行 用户 ID 1099。
运行同组
为 Pod 的任何容器内的所有进程指定主组 ID 1099。
如果省略此字段,容器的主要组 ID 将为 root(0)
。
当指定 runAsGroup
时,创建的任何文件也将归用户 1099 和组 1099 所有。
fsGroup
指定附加的任何卷的所有者将成为组 ID 1099 的所有者。
在其下创建的任何文件都将具有 nonrootgroup:nonrootgroup
.
的权限
在实际容器启动前initcontainer
更改文件系统权限运行
这里是 elastic search pod 的例子
initContainers:
- command:
- sh
- -c
- chown -R 1000:1000 /usr/share/elasticsearch/data
- sysctl -w vm.max_map_count=262144
- chgrp 1000 /usr/share/elasticsearch/data
image: busybox:1.29.2
imagePullPolicy: IfNotPresent
name: set-dir-owner
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts: #Volume mount path
- mountPath: /usr/share/elasticsearch/data
name: elasticsearch-data
更改容器中的用户组
spec:
containers:
securityContext:
privileged: true
runAsUser: 1000
请参考本期:https://github.com/kubernetes/kubernetes/issues/2630
如果是emptydir
,可以使用spec
中的securityContext
:
spec:
securityContext:
runAsUser: 1000
fsGroup: 1000
containers: ...
如果卷是 hostpath
,则 initContainer
可用于卷中的 chown
路径:
initContainers:
- name: example-c
image: busybox:latest
command: ["sh","-c","mkdir -p /vol-path && chown -R 1000:1000 /vol-path"]
resources:
limits:
cpu: "1"
memory: 1Gi
volumeMounts:
- name: vol-example
mountPath: /vol-path
经过几次迭代后,我最终使用了
{{- $root := . }}
...
initContainers:
- name: volume-mount-hack
image: busybox
command: ["sh", "-c", "find /data -user root -exec chown 33:33 {} \;"]
volumeMounts:
{{- range $key,$val := .Values.persistence.mounts }}
- name: data
mountPath: /data/{{ $key }}
subPath: {{ $root.Values.projectKey }}/{{ $key }}
{{- end }}
与其他解决方案相比,它更加简洁且可配置。此外,它 更快 - find 命令仅更改实际上属于 root 用户的 files/directories 的权限。
当您装载包含大量文件的卷时,这会对您的容器产生重大影响 boot/load 次(几秒甚至几分钟!)。
尝试比较
的执行时间
chown www-data:www-data ./ -R
和
find /data -user root -exec chown 33:33 {} \;
你可能会感到惊讶!
- 在 minikube 中,它在 运行
initContainers
之后作为 root 用户工作,通过设置 runAsUser: 0
initContainers:
- name: change-ownership-container
image: busybox
command: ["/bin/chown","-R","1000:1000", "/home/jovyan/work"]
securityContext:
runAsUser: 0
privileged: true
volumeMounts:
- name: notebook-data
mountPath: /home/jovyan/work
所以整个 Yaml 文件看起来像这样
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: jupyter
labels:
release: jupyter
spec:
replicas:
updateStrategy:
type: RollingUpdate
serviceName: jupyter-headless
podManagementPolicy: Parallel
selector:
matchLabels:
release: jupyter
template:
metadata:
labels:
release: jupyter
annotations:
spec:
restartPolicy: Always
terminationGracePeriodSeconds: 30
securityContext:
runAsUser: 1000
fsGroup: 1000
containers:
- name: jupyter
image: "jupyter/base-notebook:ubuntu-20.04"
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8888
protocol: TCP
- name: blockmanager
containerPort: 7777
protocol: TCP
- name: driver
containerPort: 2222
protocol: TCP
volumeMounts:
- name: notebook-data
mountPath: /home/jovyan/work
resources:
limits:
cpu: 200m
memory: 300Mi
requests:
cpu: 100m
memory: 200Mi
initContainers:
- name: change-ownership-container
image: busybox
command: ["/bin/chown","-R","1000:1000", "/home/jovyan/work"]
securityContext:
runAsUser: 0
privileged: true
volumeMounts:
- name: notebook-data
mountPath: /home/jovyan/work
volumes:
- name: notebook-data
persistentVolumeClaim:
claimName: jupyter-pvc
在我的例子中,我使用 scratch
作为基本图像并将用户设置为 65543。我需要对目录的写入权限。我是通过使用 emptyDir
volume,
spec:
containers:
...
volumeMounts:
- mountPath: /tmp
name: tmp
# readOnly: true
volumes:
- name: tmp
emptyDir: {}
For people using configmap as file inside pod
我正在从 configmap 加载数据作为 pod 容器内的文件,这是我的清单:
#./script-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: script-cm
labels:
app: script
data:
data-script: |
#!/bin/bash
set -e
echo "some script commands"
#./deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: script
namespace: default
labels:
app: script
spec:
selector:
matchLabels:
app: script
replicas: 1
template:
metadata:
labels:
app: script
spec:
restartPolicy: Always
containers:
- name: script-container
image: ubuntu:20.04
resources: {}
volumeMounts:
- name: influxdb-provisioning
mountPath: /docker-entrypoint-initdb.d/data.sh
subPath: data.sh
volumes:
- name: script-bind
configMap:
name: script-cm
items:
- key: data-script
path: data.sh
mode: 0777
如您所见,我正在按照 k8s docs 将配置映射绑定到 pod,mode: 0777
允许我在该特定文件上提供 execution permissions
,您也可以 运行 以下命令使用 kubectl explain
获得更好的想法:
kubectl explain deployment.spec.template.spec.volumes.configMap.items.mode
Make sure to put the right permissions instead of 0777 since it's not recommended especially on sensitive data!
我正在 运行使用 kops 在 AWS 上建立一个 Kubernetes 集群。我已将一个 EBS 卷安装到容器上,它在我的应用程序中是可见的,但它是只读的,因为我的应用程序没有 运行 作为 root。如何以 root 以外的用户身份挂载 PersistentVolumeClaim
? VolumeMount
似乎没有任何选项来控制挂载路径的用户、组或文件权限。
这是我的部署 yaml 文件:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: notebook-1
spec:
replicas: 1
template:
metadata:
labels:
app: notebook-1
spec:
volumes:
- name: notebook-1
persistentVolumeClaim:
claimName: notebook-1
containers:
- name: notebook-1
image: jupyter/base-notebook
ports:
- containerPort: 8888
volumeMounts:
- mountPath: "/home/jovyan/work"
name: notebook-1
Pod 安全上下文支持设置 fsGroup
,它允许您设置拥有该卷的组 ID,以及谁可以写入它。文档中的示例:
apiVersion: v1
kind: Pod
metadata:
name: hello-world
spec:
containers:
# specification of the pod's containers
# ...
securityContext:
fsGroup: 1234
有关此的更多信息here
对于k8s 1.10+版本,添加了runAsGroup
,它与fsGroup
类似,但工作方式不同。
可以在此处跟踪实施情况:https://github.com/kubernetes/features/issues/213
我最终得到了一个 initContainer
与主容器相同的 volumeMount
来设置适当的权限,在我的例子中,是自定义 Grafana 图像。
当 pod 中的容器 运行 宁作为 root
以外的用户并且需要对已安装卷的写入权限时,这是必需的。
initContainers:
- name: take-data-dir-ownership
image: alpine:3
# Give `grafana` user (id 472) permissions a mounted volume
# https://github.com/grafana/grafana-docker/blob/master/Dockerfile
command:
- chown
- -R
- 472:472
- /var/lib/grafana
volumeMounts:
- name: data
mountPath: /var/lib/grafana
更新: 请注意,在没有 -R
(递归)标志的情况下 运行 chown
可能就足够了,因为权限通常会在 pod 重新启动时保持不变。如果卷中有大量文件,这将是可取的,因为处理所有文件需要时间(取决于为 initContainer
设置的 resources
限制)。
这是 Kubernetes Deployments/StatefulSets 面临的挑战之一,当您必须 运行 作为非根用户在容器内进行处理时。但是,当您将卷安装到 pod 时,它总是在 root:root
的许可下安装。
因此,非root用户必须有权访问要读写数据的文件夹。
请按照以下步骤操作。
- 创建用户组并在 Dockerfile 中分配组 ID。
- 使用用户 ID 创建用户并添加到 Dockerfile 中的组。
- 递归更改用户进程想要的文件夹的所有权read/write。
在 pod
spec
上下文的 Deployment/StatefulSet 中添加以下行。spec: securityContext: runAsUser: 1099 runAsGroup: 1099 fsGroup: 1099
运行AsUser
指定对于 Pod 中的任何容器,所有进程 运行 用户 ID 1099。
运行同组
为 Pod 的任何容器内的所有进程指定主组 ID 1099。
如果省略此字段,容器的主要组 ID 将为 root(0)
。
当指定 runAsGroup
时,创建的任何文件也将归用户 1099 和组 1099 所有。
fsGroup
指定附加的任何卷的所有者将成为组 ID 1099 的所有者。
在其下创建的任何文件都将具有 nonrootgroup:nonrootgroup
.
在实际容器启动前initcontainer
更改文件系统权限运行
这里是 elastic search pod 的例子
initContainers:
- command:
- sh
- -c
- chown -R 1000:1000 /usr/share/elasticsearch/data
- sysctl -w vm.max_map_count=262144
- chgrp 1000 /usr/share/elasticsearch/data
image: busybox:1.29.2
imagePullPolicy: IfNotPresent
name: set-dir-owner
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts: #Volume mount path
- mountPath: /usr/share/elasticsearch/data
name: elasticsearch-data
更改容器中的用户组
spec:
containers:
securityContext:
privileged: true
runAsUser: 1000
请参考本期:https://github.com/kubernetes/kubernetes/issues/2630
如果是emptydir
,可以使用spec
中的securityContext
:
spec:
securityContext:
runAsUser: 1000
fsGroup: 1000
containers: ...
如果卷是 hostpath
,则 initContainer
可用于卷中的 chown
路径:
initContainers:
- name: example-c
image: busybox:latest
command: ["sh","-c","mkdir -p /vol-path && chown -R 1000:1000 /vol-path"]
resources:
limits:
cpu: "1"
memory: 1Gi
volumeMounts:
- name: vol-example
mountPath: /vol-path
经过几次迭代后,我最终使用了
{{- $root := . }}
...
initContainers:
- name: volume-mount-hack
image: busybox
command: ["sh", "-c", "find /data -user root -exec chown 33:33 {} \;"]
volumeMounts:
{{- range $key,$val := .Values.persistence.mounts }}
- name: data
mountPath: /data/{{ $key }}
subPath: {{ $root.Values.projectKey }}/{{ $key }}
{{- end }}
与其他解决方案相比,它更加简洁且可配置。此外,它 更快 - find 命令仅更改实际上属于 root 用户的 files/directories 的权限。
当您装载包含大量文件的卷时,这会对您的容器产生重大影响 boot/load 次(几秒甚至几分钟!)。
尝试比较
的执行时间chown www-data:www-data ./ -R
和
find /data -user root -exec chown 33:33 {} \;
你可能会感到惊讶!
- 在 minikube 中,它在 运行
initContainers
之后作为 root 用户工作,通过设置runAsUser: 0
initContainers:
- name: change-ownership-container
image: busybox
command: ["/bin/chown","-R","1000:1000", "/home/jovyan/work"]
securityContext:
runAsUser: 0
privileged: true
volumeMounts:
- name: notebook-data
mountPath: /home/jovyan/work
所以整个 Yaml 文件看起来像这样
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: jupyter
labels:
release: jupyter
spec:
replicas:
updateStrategy:
type: RollingUpdate
serviceName: jupyter-headless
podManagementPolicy: Parallel
selector:
matchLabels:
release: jupyter
template:
metadata:
labels:
release: jupyter
annotations:
spec:
restartPolicy: Always
terminationGracePeriodSeconds: 30
securityContext:
runAsUser: 1000
fsGroup: 1000
containers:
- name: jupyter
image: "jupyter/base-notebook:ubuntu-20.04"
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8888
protocol: TCP
- name: blockmanager
containerPort: 7777
protocol: TCP
- name: driver
containerPort: 2222
protocol: TCP
volumeMounts:
- name: notebook-data
mountPath: /home/jovyan/work
resources:
limits:
cpu: 200m
memory: 300Mi
requests:
cpu: 100m
memory: 200Mi
initContainers:
- name: change-ownership-container
image: busybox
command: ["/bin/chown","-R","1000:1000", "/home/jovyan/work"]
securityContext:
runAsUser: 0
privileged: true
volumeMounts:
- name: notebook-data
mountPath: /home/jovyan/work
volumes:
- name: notebook-data
persistentVolumeClaim:
claimName: jupyter-pvc
在我的例子中,我使用 scratch
作为基本图像并将用户设置为 65543。我需要对目录的写入权限。我是通过使用 emptyDir
volume,
spec:
containers:
...
volumeMounts:
- mountPath: /tmp
name: tmp
# readOnly: true
volumes:
- name: tmp
emptyDir: {}
For people using configmap as file inside pod
我正在从 configmap 加载数据作为 pod 容器内的文件,这是我的清单:
#./script-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: script-cm
labels:
app: script
data:
data-script: |
#!/bin/bash
set -e
echo "some script commands"
#./deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: script
namespace: default
labels:
app: script
spec:
selector:
matchLabels:
app: script
replicas: 1
template:
metadata:
labels:
app: script
spec:
restartPolicy: Always
containers:
- name: script-container
image: ubuntu:20.04
resources: {}
volumeMounts:
- name: influxdb-provisioning
mountPath: /docker-entrypoint-initdb.d/data.sh
subPath: data.sh
volumes:
- name: script-bind
configMap:
name: script-cm
items:
- key: data-script
path: data.sh
mode: 0777
如您所见,我正在按照 k8s docs 将配置映射绑定到 pod,mode: 0777
允许我在该特定文件上提供 execution permissions
,您也可以 运行 以下命令使用 kubectl explain
获得更好的想法:
kubectl explain deployment.spec.template.spec.volumes.configMap.items.mode
Make sure to put the right permissions instead of 0777 since it's not recommended especially on sensitive data!