Perché la mia app non funziona quando il percorso di montaggio dello spazio di archiviazione file NFS appartiene a un utente non root?

Risoluzione dei problemi relativi al montaggio di NFS senza privilegi di root per l'archiviazione dei file.

Infrastrutture classiche

Dopo aver aggiunto l'archiviazione NFS alla tua distribuzione, la distribuzione del tuo contenitore ha esito negativo. Quando richiami i log per il tuo contenitore, potresti vedere degli errori simili ai seguenti. Il pod genera un errore e si blocca in un ciclo di ricaricamento.

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

Per impostazione predefinita, gli utenti non root non dispongono dei permessi di scrittura sul percorso di montaggio del volume per lo spazio di archiviazione basato su NFS. Alcune immagini comuni dell'applicazione, come Jenkins e Nexus3, specificano un utente non root che possiede il percorso di montaggio nel Dockerfile.

Quando crei un contenitore da questo Dockerfile, la creazione del contenitore non riesce a causa di autorizzazioni insufficienti dell'utente non root sul percorso di montaggio. Per concedere i permessi di scrittura, è possibile modificare il Dockerfile in modo da aggiungere temporaneamente l'utente non root al gruppo root prima che modifichi i permessi del percorso di mount, oppure utilizzare un container init.

Se utilizzi un grafico " Helm " per distribuire l'immagine, modifica la distribuzione " Helm " in modo da utilizzare un container " init ".

Quando si include un container init nella propria distribuzione, è possibile assegnare a un utente non root specificato nel Dockerfile i permessi di scrittura per il percorso di montaggio del volume all'interno del container.

Il container " init " viene avviato prima dell'avvio del container dell'applicazione. Il container " init " crea il percorso di montaggio del volume all'interno del container, modifica il percorso di montaggio in modo che sia di proprietà dell'utente corretto (non root) e si chiude. Quindi, viene avviato il contenitore dell'applicazione con l'utente non root che deve scrivere sul percorso di montaggio. Poiché il percorso è già di proprietà dell'utente non root, la scrittura sul percorso di montaggio ha esito positivo. Se non si desidera utilizzare un container init, è possibile modificare il Dockerfile per consentire l'accesso da parte di un utente non root all'archivio file NFS.

Prima di iniziare:

Per utilizzare un container init e assegnare a un utente non root i permessi di scrittura sul percorso di montaggio del volume:

  1. Apri il Dockerfile per la tua applicazione e ottieni l'ID utente (UID) e l'ID di gruppo (GID) dell'utente a cui desideri concedere l'autorizzazione di scrittore sul percorso di montaggio del volume. Nell'esempio di un Dockerfile Jenkins, le informazioni sono:

    • UID: 1000
    • GID: 1000

    Esempio di 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. Aggiungi l'archiviazione persistente alla tua applicazione creando un'attestazione del volume persistente (PVC, persistent volume claim). Questo esempio utilizza la classe di archiviazione ibmc-file-bronze. Per visualizzare le classi di archiviazione disponibili, eseguire il comando 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. Crea la PVC.

    oc apply -f mypvc.yaml
    
  4. Nel file .yaml della tua distribuzione, aggiungi il contenitore init . Includi l'UID e il GID che hai richiamato in precedenza.

    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
    

    Esamina il seguente esempio di distribuzione 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. Crea il pod e monta la PVC nel tuo pod.

    oc apply -f my-pod.yaml
    

    Il contenitore " init " presenta un errore? Poiché Red Hat OpenShift imposta dei vincoli relativi al contesto di sicurezza limitato, è possibile che venga visualizzato un errore del tipo " chown: /opt/ibm-ucd/server/ext_lib: Operation not permitted". Per la tua distribuzione, utilizza un SCC che consente le operazioni chown e riprova.

  6. Verifica che il volume sia stato correttamente montato nel tuo pod. Prendi nota del nome del pod e del percorso Containers/Mounts.

    oc describe pod <my-pod>
    

    Output di esempio

    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. Accedi al pod utilizzando il nome pod di cui hai preso nota precedentemente.

    oc exec -it <my-pod-123456789> /bin/bash
    
  8. Verifica le autorizzazioni del percorso di montaggio del tuo contenitore. Nell'esempio, il percorso di montaggio è /var/jenkins_home.

    ls -ln /var/jenkins_home
    

    Output di esempio

    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
    

    Questo output mostra che il GID e l'UID dal tuo Dockerfile (in questo esempio, 1000 e 1000) possiedono il percorso di montaggio all'interno del contenitore.