当非 root 用户拥有NFS文件存储挂载路径时,为什么我的应用程序会失败?
经典基础结构
向部署添加 NFS 存储器后,容器的部署失败。 检索容器的日志时,您可能会看到如下所示的错误。 pod 发生故障,并且卡在重新装入循环中。
write-permission
don't have required permission
can't create directory '/bitnami/mariadb/data': Permission denied
默认情况下,非 root 用户在NFS存储的卷挂载路径上没有写权限。 一些公共应用程序映像(例如,Jenkins 和 Nexus3)会在 Dockerfile 中指定拥有安装路径的非 root 用户。
通过此 Dockerfile 创建容器时,由于安装路径上非 root 用户的许可权不足,创建容器会失败。 要授予写入权限,可以修改 Dockerfile,在更改挂载路径权限之前将非 root 用户临时添加到 root 用户组,或者使用 "init
容器。
如果使用Helm图表部署映像,请编辑Helm部署以使用 "init
容器。
在部署中包含 "init
容器时,可以赋予在 Dockerfile 中指定的非 root 用户写入容器内卷挂载路径的权限。
init
容器会在应用程序容器启动前启动。 init
容器在容器内创建卷挂载路径,将挂载路径更改为由正确(非 root)用户拥有,然后关闭。 随后,应用程序容器将以必须写入安装路径的非 root 用户身份启动。 由于该路径已由非 root 用户拥有,因此写入安装路径成功。 如果不想使用 "init
容器,可以修改 Dockerfile,添加非 root 用户访问NFS文件存储的权限。
开始之前:
- 访问 Red Hat OpenShift 集群。
- 选取允许部署执行
chown
操作的 安全上下文约束(SCC)。
要使用 init
容器来授予非 root 用户对卷安装路径的写许可权:
-
打开应用程序的 Dockerfile,并从要向其授予卷安装路径的写入者许可权的用户获取用户标识 (UID) 和组标识 (GID)。 在来自 Jenkins Dockerfile 的示例中,信息为:
- UID:
1000
- GID:
1000
示例 Dockerfile
FROM openjdk:8-jdk RUN apt-get update && apt-get install -y git curl && rm -rf /var/lib/apt/lists/* ARG user=jenkins ARG group=jenkins ARG uid=1000 ARG gid=1000 ARG http_port=8080 ARG agent_port=50000 ENV JENKINS_HOME /var/jenkins_home ENV JENKINS_SLAVE_AGENT_PORT ${agent_port} ...
- UID:
-
通过创建持久卷声明 (PVC) 将持久性存储器添加到应用程序。 此示例使用
ibmc-file-bronze
存储类。 要查看可用的存储类,请运行 "oc get sc
。apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mypvc annotations: volume.beta.kubernetes.io/storage-class: "ibmc-file-bronze" spec: accessModes: - ReadWriteMany resources: requests: storage: 20Gi
-
创建 PVC。
oc apply -f mypvc.yaml
-
在部署文件“
.yaml
中,添加”init
容器。 包含您先前检索到的 UID 和 GID。initContainers: - name: initcontainer # Or replace the name image: alpine:latest command: ["/bin/sh", "-c"] args: - chown <UID>:<GID> /mount; # Replace UID and GID with values from the Dockerfile volumeMounts: - name: volume # Or you can replace with any name mountPath: /mount # Must match the mount path in the args line
查看以下示例 Jenkins 部署。
apiVersion: apps/v1 kind: Deployment metadata: name: my-pod spec: replicas: 1 selector: matchLabels: app: jenkins template: metadata: labels: app: jenkins spec: containers: - name: jenkins image: jenkins volumeMounts: - mountPath: /var/jenkins_home name: volume volumes: - name: volume persistentVolumeClaim: claimName: mypvc initContainers: - name: permissionsfix image: alpine:latest command: ["/bin/sh", "-c"] args: - chown 1000:1000 /mount; volumeMounts: - name: volume mountPath: /mount
-
创建 pod 并将 PVC 安装到该 pod。
oc apply -f my-pod.yaml
init
容器是否发生故障? 由于 Red Hat OpenShift 设置了受限安全上下文约束,因此您可能会看到错误,例如chown: /opt/ibm-ucd/server/ext_lib: Operation not permitted
。 对于部署,使用 SCC 允许chown
操作并重试。 -
验证卷是否已成功安装到 pod。 记下 pod 名称和 Containers/Mounts 路径。
oc describe pod <my-pod>
示例输出
Name: mypod-123456789 Namespace: default ... Init Containers: ... Mounts: /mount from volume (rw) /var/run/secrets/kubernetes.io/serviceaccount from default-token-cp9f0 (ro) ... Containers: jenkins: Container ID: Image: jenkins Image ID: Port: <none> State: Waiting Reason: PodInitializing Ready: False Restart Count: 0 Environment: <none> Mounts: /var/jenkins_home from volume (rw) /var/run/secrets/kubernetes.io/serviceaccount from default-token-cp9f0 (ro) ... Volumes: myvol: Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace) ClaimName: mypvc ReadOnly: false
-
使用先前记下的 pod 名称登录到 pod。
oc exec -it <my-pod-123456789> /bin/bash
-
验证容器安装路径的许可权。 在示例中,安装路径为
/var/jenkins_home
。ls -ln /var/jenkins_home
示例输出
jenkins@mypod-123456789:/$ ls -ln /var/jenkins_home total 12 -rw-r--r-- 1 1000 1000 102 Mar 9 19:58 copy_reference_file.log drwxr-xr-x 2 1000 1000 4096 Mar 9 19:58 init.groovy.d drwxr-xr-x 9 1000 1000 4096 Mar 9 20:16 war
此输出显示 Dockerfile 中的 GID 和 UID(在此示例中为
1000
和1000
)拥有容器内的安装路径。