FNSP Web Page

Volumes no Docker: a pasta que aparece a pertencer a ninguém

O sintoma aparece sempre da mesma maneira. O contentor escreve numa pasta montada, vai-se ver ao anfitrião, e os ficheiros pertencem a um número em vez de a uma pessoa:

ls -l dados/
# -rw-r--r-- 1 999 999 8192 Sep 13 10:14 base.db

Ou então o contrário: a pasta é sua, tem as permissões certas, e o contentor insiste em não conseguir escrever lá.

1. A causa é uma só

Já escrevi isto a propósito das permissões no Linux e repete-se aqui porque é a chave de tudo: o sistema de ficheiros não guarda nomes, guarda números. O nome que o ls mostra é uma tradução feita na hora, a partir do /etc/passwd.

Um contentor tem o seu próprio /etc/passwd. O núcleo é o mesmo, o número é o mesmo — só a tabela de tradução é outra.

id -u
# 1000

docker run --rm alpine id -u
# 0, e ali chama-se root

Não há camada nenhuma a converter. Se o processo lá dentro corre como 999, os ficheiros que ele criar pertencem ao 999 cá fora — quer isso corresponda a alguém, quer não.

2. Três formas de montar, e só uma dá problemas

# Bind mount: a pasta pertence ao anfitrião, e o dono vem de lá
docker run -v "$PWD/dados:/dados" a-minha-imagem

# Volume nomeado: o Docker cria-o, e se estiver vazio copia
# dono e permissões da pasta que existe dentro da imagem
docker run -v dados:/dados a-minha-imagem

É esta diferença que explica a confusão mais comum — «com volume nomeado funciona, com bind mount não». O volume nomeado, quando nasce vazio, herda o dono de dentro da imagem. O bind mount não herda nada: a pasta já existia e já tinha dono.

3. As saídas, pela ordem que eu tentaria

Não escrever em disco. É a melhor, e é a que menos se considera. Este site corre em contentor sem volume nenhum: as imagens, o conteúdo e a sessão vivem na base de dados ou na imagem, e o servidor não grava ficheiros. O Dockerfile acaba com uma linha só sobre o assunto:

# Não há razão para correr como root: o servidor não escreve em disco.
USER node

Quem não precisa de escrever não tem este problema, e ganha ainda a parte de segurança: um contentor sem permissão de escrita é muito menos interessante para quem entrar nele.

Volume nomeado em vez de bind mount, quando os dados só interessam ao contentor — a pasta de dados de um Postgres é o exemplo perfeito. Ninguém precisa de os abrir no anfitrião, e o Docker trata das permissões.

Acertar o UID na invocação, quando o bind mount é mesmo preciso:

docker run --user "$(id -u):$(id -g)" -v "$PWD:/app" -w /app node:22 npm run build

Funciona, mas convém saber o que custa: o processo passa a correr com um número que não existe no /etc/passwd do contentor. Programas que perguntem quem são — ou que queiram uma pasta pessoal — passam a falhar de formas pouco óbvias. O npm é um dos que se queixa.

Ou fazer como as imagens oficiais: arrancar como root, corrigir o dono da pasta montada, e só depois baixar de privilégios antes de entregar o processo à aplicação. É o que o entrypoint do Postgres faz, e é a razão de aquela imagem funcionar sem ninguém pensar no assunto.

4. O caso do node_modules

O clássico do desenvolvimento. Monta-se o projeto inteiro para ter recarregamento automático, e o node_modules do anfitrião — compilado para outro sistema — tapa o que a imagem tinha instalado:

docker run -v "$PWD:/app" -v /app/node_modules a-minha-imagem

Aquele segundo -v sem origem cria um volume anónimo por cima da pasta, e é o que impede o de fora de a tapar. Parece um truque, e é — mas é o truque certo.

5. O aviso que interessa mesmo

No Docker Desktop, em Windows ou em macOS, os ficheiros atravessam uma máquina virtual e uma camada de tradução que faz a propriedade parecer certa. Nada disto dói.

Em Linux não há camada nenhuma, e o UID é o UID. Ou seja: o problema não aparece na sua máquina, aparece no servidor. Se o contentor vai correr em Linux, é em Linux que isto tem de ser experimentado — e mais vale descobri-lo antes do primeiro deploy do que a meio dele.

O resumo

Antes de montar uma pasta, uma pergunta: alguém, fora do contentor, precisa mesmo de abrir estes ficheiros? Se a resposta for não, um volume nomeado resolve e o assunto acaba ali. Se for sim, então é preciso decidir de propósito com que número o processo corre — porque o que o contentor não faz é adivinhá-lo por si.

Comentários

Ainda ninguém comentou este artigo.

Voltar ao blog

Gostávamos de saber quantas pessoas visitam o site, com o Google Analytics. Sem a sua autorização não corre nada. Política de Privacidade.