Por que meu aplicativo falha quando um usuário não raiz é proprietário do caminho de montagem do armazenamento de arquivo NFS?

Resolva problemas de montagem de partições “ NFS ” não-root para armazenamento de arquivos.

Infraestrutura clássica

Depois de incluir armazenamento NFS em sua implementação, a implementação de seu contêiner falha. Ao recuperar os logs para seu contêiner, é possível ver erros como os seguintes. O pod falha e fica preso em um ciclo de recarregamento.

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

Por padrão, os usuários não raiz não têm permissão de gravação no caminho de montagem do volume para armazenamento suportado por NFS. Algumas imagens comuns do app, como Jenkins e Nexus3, especificam um usuário não raiz que possui o caminho de montagem no Dockerfile.

Quando você cria um contêiner por meio desse Dockerfile, a criação do contêiner falha devido a permissões insuficientes do usuário não raiz no caminho de montagem. Para conceder permissão de gravação, você pode modificar o Dockerfile para adicionar temporariamente o usuário não root ao grupo do usuário root antes que ele altere as permissões do caminho de montagem, ou usar um contêiner com o comando init .

Se você usar um gráfico do Helm para implantar a imagem, edite a implantação do Helm para usar um contêiner do init.

Ao incluir um contêiner init em sua implantação, você pode conceder a um usuário que não seja o root — especificado no seu Dockerfile — permissões de gravação para o caminho de montagem do volume dentro do contêiner.

O contêiner do init é iniciado antes do contêiner do seu aplicativo. O contêiner init cria o caminho de montagem do volume dentro do contêiner, altera a propriedade do caminho de montagem para o usuário correto (que não seja o root) e é encerrado. Em seguida, seu contêiner de app é iniciado com o usuário não raiz que deve gravar no caminho de montagem. Como o caminho já pertence ao usuário não raiz, a gravação no caminho de montagem é bem-sucedida. Se você não quiser usar um contêiner do tipo “ init ”, pode modificar o Dockerfile para adicionar acesso de um usuário que não seja o root ao armazenamento de arquivos d NFS.

Antes de iniciar:

Para usar um contêiner do init para conceder permissões de gravação a um usuário não root no caminho de montagem do volume:

  1. Abra o Dockerfile para seu app e obtenha o ID do usuário (UID) e ID do grupo (GID) do usuário que você deseja fornecer permissão de gravação no caminho de montagem do volume. No exemplo de um Dockerfile Jenkins, as informações são:

    • UID:1000
    • GID: 1000

    Dockerfile de exemplo

    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. Inclua armazenamento persistente para seu app criando um persistent volume claim (PVC). Este exemplo usa a classe de armazenamento ibmc-file-bronze. Para consultar as classes de armazenamento disponíveis, execute o 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. Crie o PVC.

    oc apply -f mypvc.yaml
    
  4. No arquivo de implantação .yaml , adicione o contêiner init . Inclua o UID e o GID que você recuperou anteriormente.

    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
    

    Revise o exemplo de implementação do Jenkins a seguir.

    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. Crie o pod e monte o PVC em seu pod.

    oc apply -f my-pod.yaml
    

    O contêiner “ init ” está apresentando falha? Como o Red Hat OpenShift configura restrições de contexto de segurança restritas, é possível ver um erro como chown: /opt/ibm-ucd/server/ext_lib: Operation not permitted. Para sua implementação, use um SCC que permita operações chown e tente novamente.

  6. Verifique se o volume foi montado com êxito no pod. Anote o nome do pod e o caminho Contêineres/Montagens.

    oc describe pod <my-pod>
    

    Saída de exemplo

    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. Efetue login no pod usando o nome do pod que você anotou anteriormente.

    oc exec -it <my-pod-123456789> /bin/bash
    
  8. Verifique as permissões do caminho de montagem do seu contêiner. No exemplo, o caminho de montagem é /var/jenkins_home.

    ls -ln /var/jenkins_home
    

    Saída de exemplo

    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
    

    Essa saída mostra que o GID e o UID de seu Dockerfile (neste exemplo, 1000 e 1000) possuem o caminho de montagem dentro do contêiner.