IBM Cloud Docs
非 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 チャートを使用してイメージをデプロイする場合は、 init コンテナーを使用するように Helm デプロイメントを編集します。

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

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

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