非 root ユーザーが NFS ファイル・ストレージのマウント・パスを所有しているとアプリが失敗するのはなぜですか?

ファイルストレージにおける、root権限を持たない NFS のマウントに関する問題のトラブルシューティング。

クラシック・インフラストラクチャー

デプロイメントに NFS ストレージを追加した後に、コンテナーのデプロイメントが失敗します。 コンテナーのログを取得すると、以下のようなエラーが表示されることがあります。 ポッドが失敗し、再ロードが繰り返されます。

write-permission
don't have required permission
can't create directory '/bitnami/mariadb/data': Permission denied

デフォルトでは、非 root ユーザーには、NFS 補助ストレージのボリューム・マウント・パスに対する書き込み権限がありません。 一部の一般的なアプリのイメージ (Jenkins や Nexus3 など) は、マウント・パスを所有する非 root ユーザーを Dockerfile に指定しています。

この Dockerfile からコンテナーを作成すると、マウント・パスに対する非 root ユーザーの権限が不十分なために、コンテナーの作成は失敗します。 書き込み権限を付与するには、Dockerfile を修正して、マウントパスの権限を変更する前に非rootユーザーを一時的にrootユーザーグループに追加するか、 init コンテナを使用することができます。

Helm チャートを使用してイメージをデプロイする場合は、 Helm のデプロイ設定を編集し、 init コンテナを使用するようにしてください。

デプロイメントに init コンテナを含める場合、Dockerfileで指定した非rootユーザーに対して、コンテナ内のボリュームマウントパスへの書き込み権限を付与することができます。

init コンテナは、アプリコンテナが起動する前に起動します。 init コンテナは、コンテナ内にボリュームのマウントパスを作成し、そのマウントパスの所有者を適切な(root以外の)ユーザーに変更してから、終了します。 その後、マウント・パスに書き込む必要がある非 root ユーザーでアプリ・コンテナーが開始されます。 パスは既に非 root ユーザーによって所有されているため、マウント・パスへの書き込みは成功します。 init コンテナを使用したくない場合は、Dockerfileを修正して、 NFS のファイルストレージへの非rootユーザーによるアクセス権を追加することができます。

始める前に:

init コンテナを使用して、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 ストレージ・クラスを使用します。 利用可能なストレージクラスを確認するには、 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
    
  3. PVC を作成します。

    oc 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. ポッドを作成して、PVC をポッドにマウントします。

    oc apply -f my-pod.yaml
    

    init コンテナに障害が発生していますか? Red Hat OpenShift では、制限されたセキュリティー・コンテキスト制約が設定されるため、chown: /opt/ibm-ucd/server/ext_lib: Operation not permitted などのエラーが発生する場合があります。 デプロイメントで、 操作を許可する SCC を使用chownしてから、もう一度試してください。

  6. ボリュームがポッドに正常にマウントされたことを確認します。 ポッド名と 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
    
  7. 前にメモしたポッド名を使用して、ポッドにログインします。

    oc 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) がコンテナー内部のマウント・パスを所有していることを示しています。