IBM Cloud Docs
當非 root 使用者擁有 NFS 檔案儲存空間裝載路徑時,為何我的應用程式失敗?

當非 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,以新增 NFS 檔案儲存空間的非 root 使用者存取權。

開始之前: 登入您的帳戶。 適用的話,請將適當的資源群組設為目標。 設定叢集的環境定義。

  1. 開啟應用程式的 Dockerfile,然後取得您要授與磁區裝載路徑寫入權的使用者的使用者 ID (UID) 及群組 ID (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}
    ...
    
  2. 藉由建立持續性磁區要求 (PVC),將持續性儲存空間新增至應用程式。 這個範例使用 ibmc-file-bronze 儲存空間類別。 若要檢閱可用的儲存類別,請執行 kubectl 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
    
  3. 建立 PVC。

    kubectl apply -f mypvc.yaml
    
  4. 在部署 .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
    
  5. 建立 Pod,並將 PVC 裝載至 Pod。

    kubectl apply -f my-pod.yaml
    
  6. 驗證磁區已順利裝載至 Pod。 請記下 Pod 名稱及 Containers/Mounts 路徑。

    kubectl 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
    
    
  7. 使用您先前記下的 Pod 名稱來登入 Pod。

    kubectl exec -it <my-pod-123456789> /bin/bash
    
  8. 驗證容器裝載路徑的許可權。 在此範例中,裝載路徑為 /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(在此範例中是 10001000)擁有容器內的裝載路徑。