Venho brincando com o Docker há um tempo e continuo encontrando o mesmo problema ao lidar com dados persistentes.
Crio Dockerfile
e exponho um volume ou uso --volumes-from
para montar uma pasta host dentro do meu contêiner .
Quais permissões devo aplicar ao volume compartilhado no host?
Eu posso pensar em duas opções:
Até agora, eu dei a todos acesso de leitura / gravação, para que eu possa gravar na pasta do contêiner do Docker.
Mapeie os usuários do host para o contêiner, para que eu possa atribuir permissões mais granulares. Não tenho certeza se isso é possível e não encontrei muito sobre isso. Até agora, tudo o que posso fazer é executar o contêiner como um usuário:,
docker run -i -t -user="myuser" postgres
mas esse usuário tem um UID diferente do meu hostmyuser
, portanto, as permissões não funcionam. Além disso, não tenho certeza se o mapeamento dos usuários representará alguns riscos à segurança.
Existem outras alternativas?
Como vocês estão lidando com esse problema?
Respostas:
ATUALIZAÇÃO 02-03-2016 : A partir do Docker 1.9.0, o Docker nomeou volumes que substituem os contêineres somente de dados . A resposta abaixo, assim como minha postagem no blog vinculada, ainda tem valor no sentido de pensar sobre os dados dentro da janela de encaixe, mas considere usar volumes nomeados para implementar o padrão descrito abaixo, em vez de contêineres.
Acredito que a maneira canônica de resolver isso é usando contêineres somente de dados . Com essa abordagem, todo o acesso aos dados do volume é feito por contêineres que usam
-volumes-from
o contêiner de dados, portanto, o host uid / gid não importa.Por exemplo, um caso de uso fornecido na documentação está fazendo backup de um volume de dados. Para fazer isso, outro contêiner é usado para fazer o backup via
tar
e também usa-volumes-from
para montar o volume. Então, acho que o ponto principal do grok é: em vez de pensar em como obter acesso aos dados no host com as permissões adequadas, pense em como fazer o que for necessário - backups, navegação etc. - através de outro contêiner . Os próprios contêineres precisam usar uid / gids consistentes, mas não precisam mapear para nada no host, permanecendo portáteis.Isso também é relativamente novo para mim, mas se você tiver um caso de uso específico, sinta-se à vontade para comentar e tentarei expandir a resposta.
ATUALIZAÇÃO : Para o caso de uso especificado nos comentários, você pode ter uma imagem
some/graphite
para executar grafite e uma imagemsome/graphitedata
como o contêiner de dados. Portanto, ignorando portas e outras coisas, aDockerfile
imagemsome/graphitedata
é algo como:Crie e crie o contêiner de dados:
O
some/graphite
Dockerfile também deve receber o mesmo uid / gids, portanto, pode ser algo como isto:E seria executado da seguinte maneira:
Ok, agora isso nos fornece nosso contêiner de grafite e um contêiner somente de dados associado ao usuário / grupo correto (observe que você pode reutilizar o
some/graphite
contêiner para o contêiner de dados, substituindo o entrypoing / cmd ao executá-lo, mas tendo-o como imagens separadas O IMO é mais claro).Agora, digamos que você queira editar algo na pasta de dados. Portanto, em vez de vincular a montagem do volume ao host e editá-lo lá, crie um novo contêiner para fazer esse trabalho. Vamos chamá-lo
some/graphitetools
. Permite também criar o usuário / grupo apropriado, assim como asome/graphite
imagem.Você pode tornar esse DRY herdando
some/graphite
ousome/graphitedata
na Dockerfile, ou em vez de criar uma nova imagem apenas um dos existentes re-uso (substituindo entrypoint / cmd, se necessário).Agora, você simplesmente executa:
e depois
vi /data/graphite/whatever.txt
. Isso funciona perfeitamente porque todos os contêineres têm o mesmo usuário de grafite com uid / gid correspondente.Como você nunca monta a
/data/graphite
partir do host, não se importa como o uid / gid do host é mapeado para o uid / gid definido dentro dographite
egraphitetools
contêineres . Esses contêineres agora podem ser implantados em qualquer host e continuarão funcionando perfeitamente.O mais interessante disso é que
graphitetools
poderia ter todos os tipos de utilitários e scripts úteis, que agora você também pode implantar de maneira portátil.ATUALIZAÇÃO 2 : Depois de escrever esta resposta, decidi escrever uma postagem de blog mais completa sobre essa abordagem. Espero que ajude.
ATUALIZAÇÃO 3 : Corrigi esta resposta e adicionei mais detalhes. Antes, ele continha algumas suposições incorretas sobre propriedade e permissões - a propriedade geralmente é atribuída no momento da criação do volume, ou seja, no contêiner de dados, porque é quando o volume é criado. Veja este blog . Porém, isso não é um requisito - você pode simplesmente usar o contêiner de dados como uma "referência / manipulador" e definir a propriedade / permissões em outro contêiner via chown em um ponto de entrada, que termina com gosu para executar o comando como o usuário correto. Se alguém estiver interessado nessa abordagem, comente e eu posso fornecer links para uma amostra usando essa abordagem.
fonte
/data/newcontainer
? Suponho que você executedocker
como root (é possível não fazer isso?) Além disso, há alguma diferença nessas permissões se os dados forem montados diretamente no contêiner principal ou por meio de um contêiner somente de dados ?/var/lib/docker
algum lugar, mas ainda uma enorme dorUma solução muito elegante pode ser vista na imagem oficial do redis e em geral em todas as imagens oficiais.
Descrito no processo passo a passo:
Como visto nos comentários do Dockerfile:
O gosu é uma alternativa de
su
/sudo
para facilitar o abandono do usuário root. (Redis é sempre executado com oredis
usuário)/data
volume e defina-o como workdirAo configurar o volume / data com o
VOLUME /data
comando, agora temos um volume separado que pode ser volume do docker ou montado em ligação a um diretório de host.A configuração como workdir (
WORKDIR /data
) torna o diretório padrão de onde os comandos são executados.Isso significa que todas as execuções de contêiner serão executadas pelo script docker-entrypoint e, por padrão, o comando a ser executado é redis-server.
docker-entrypoint
é um script que executa uma função simples: Altere a propriedade do diretório atual (/ data) e desça deroot
para oredis
usuário para executarredis-server
. (Se o comando executado não for redis-server, ele executará o comando diretamente.)Isso tem o seguinte efeito
Se o diretório / data for montado em ligação ao host, o docker-entrypoint preparará as permissões do usuário antes de executar o redis-server no
redis
usuário.Isso lhe dá a tranqüilidade de que não há configuração zero para executar o contêiner em qualquer configuração de volume.
Obviamente, se você precisar compartilhar o volume entre imagens diferentes, precisará garantir que eles usem o mesmo ID do usuário / ID do grupo, caso contrário, o contêiner mais recente seqüestrará as permissões de usuário do anterior.
fonte
chown
dentro doENTRYPOINT
script?Este não é o melhor caminho para a maioria das circunstâncias, mas ainda não foi mencionado, portanto talvez ajude alguém.
Volume do host de montagem de ligação
Host folder FOOBAR is mounted in container /volume/FOOBAR
Modifique o script de inicialização do contêiner para encontrar o GID do volume em que você está interessado
$ TARGET_GID=$(stat -c "%g" /volume/FOOBAR)
Verifique se o usuário pertence a um grupo com esse GID (talvez seja necessário criar um novo grupo). Neste exemplo, fingirei que meu software é executado como
nobody
usuário quando está dentro do contêiner, portanto, quero garantir quenobody
pertença a um grupo com um ID de grupo igual aTARGET_GID
Gosto disso porque posso modificar facilmente as permissões de grupo nos volumes do meu host e sei que essas permissões atualizadas se aplicam dentro do contêiner do Docker. Isso acontece sem nenhuma permissão ou modificação de propriedade nas pastas / arquivos do host, o que me deixa feliz.
Eu não gosto disso porque supõe que não há perigo em se adicionar a grupos arbitrários dentro do contêiner que estejam usando o GID desejado. Ele não pode ser usado com uma
USER
cláusula em um Dockerfile (a menos que esse usuário tenha privilégios de root, suponho). Além disso, ele grita hack job ;-)Se você quer ser hardcore, obviamente pode estender isso de várias maneiras - por exemplo, pesquise todos os grupos em qualquer subarquivo, vários volumes, etc.
fonte
$TARGET_GID
seria usargrep ':$TARGET_GID:'
, caso contrário, se o contêiner tiver, por exemplo, gid 10001 e seu host for 1000, essa verificação será aprovada, mas não deve.Ok, isso está sendo rastreado na edição docker # 7198
Por enquanto, estou lidando com isso usando sua segunda opção:
Dockerfile
CLI
ATUALIZAÇÃO Atualmente, estou mais inclinado a responder ao Hamy
fonte
id -u <username>
,id -g <username>
,id -G <username>
para obter o ID de usuário e ID de grupo de um usuário específico em vezTente adicionar um comando ao Dockerfile
os créditos vão para https://github.com/denderello/symfony-docker-example/issues/2#issuecomment-94387272
fonte
Da mesma forma que você, eu estava procurando uma maneira de mapear usuários / grupos de contêineres de host para docker e esta é a maneira mais curta que encontrei até agora:
Este é um extrato do meu docker-compose.yml.
A idéia é montar (no modo somente leitura) listas de usuários / grupos do host para o contêiner, assim, após a inicialização do contêiner, ele terá o mesmo uid-> nome de usuário (assim como para grupos) correspondências com o host. Agora você pode definir as configurações de usuário / grupo para o seu serviço dentro do contêiner como se estivesse funcionando no sistema host.
Quando você decide mover seu contêiner para outro host, basta alterar o nome de usuário no arquivo de configuração de serviço para o que você possui nesse host.
fonte
-u $( id -u $USER ):$( id -g $USER )
e não precisa mais se preocupar com o nome de usuário. Isso funciona bem para ambientes de desenvolvimento locais onde você deseja gerar arquivos (binários por exemplo) aos quais você tem acesso de leitura / gravação por padrão.Aqui está uma abordagem que ainda usa um contêiner somente de dados, mas não exige que seja sincronizado com o contêiner do aplicativo (em termos de ter o mesmo uid / gid).
Presumivelmente, você deseja executar algum aplicativo no contêiner como $ USER não raiz sem um shell de logon.
No Dockerfile:
Então, em entrypoint.sh:
fonte
Para proteger e alterar a raiz do contêiner de docker, um host de docker tenta usar
--uidmap
e--private-uids
opçõeshttps://github.com/docker/docker/pull/4572#issuecomment-38400893
Você também pode remover vários recursos (
--cap-drop
) no contêiner do docker para segurançahttp://opensource.com/business/14/9/security-for-docker
O suporte para UPDATE deve vir
docker > 1.7.0
UPDATE Version
1.10.0
(04-02-2016) adicionar--userns-remap
sinalizador https://github.com/docker/docker/blob/master/CHANGELOG.md#security-2fonte
--uidmap
nem as--private-uids
opções. Parece que o PR não conseguiu e não foi mesclado."We apparently do have so some of conflicting designs between libnetwork and user namespaces ... and something we'd like to get in for 1.8.0. So don't think we're dropping this, we're definitely going to take a break after all these, and see how we need to reconsider the current design and integration of libnetwork to make this possible. Thanks!"
github.com/docker/docker/pull/12648 Então, acho que devemos esperar a próxima versão estável.Minha abordagem é detectar o UID / GID atual, criar esse usuário / grupo dentro do contêiner e executar o script sob ele. Como resultado, todos os arquivos que ele criará corresponderão ao usuário no host (que é o script):
fonte
Imagem Base
Use esta imagem: https://hub.docker.com/r/reduardo7/docker-host-user
ou
Importante: isso destrói a portabilidade do contêiner entre hosts .
1)
init.sh
2)
Dockerfile
3) run.sh
4) Construa com
docker
4) Corra!
fonte
No meu caso específico, eu estava tentando criar meu pacote de nós com a imagem da janela de encaixe para que não precisasse instalar o npm no servidor de implementação. Funcionou bem até que, fora do contêiner e na máquina host, tentei mover um arquivo para o diretório node_modules que a imagem da janela de encaixe do nó havia criado, para a qual me foi negada a permissão porque pertencia à raiz. Percebi que poderia solucionar isso copiando o diretório do contêiner para a máquina host. Através dos documentos do docker ...
Este é o código bash que eu usei para alterar a propriedade do diretório criado por e dentro do contêiner do docker.
Se necessário, você pode remover o diretório com um segundo contêiner de janela de encaixe.
fonte
Para compartilhar uma pasta entre o host do docker e o contêiner, tente o comando abaixo
O sinalizador -v monta o diretório de trabalho atual no contêiner. Quando o diretório do host de um volume montado por ligação não existir, o Docker criará automaticamente esse diretório no host para você,
No entanto, existem 2 problemas que temos aqui:
Solução:
Contêiner: crie um usuário, diga 'testuser', por padrão, o ID do usuário começará em 1000,
Anfitrião: crie um grupo, digamos 'testgroup', com o ID do grupo 1000 e mostre o diretório para o novo grupo (testgroup
fonte
Se você estiver usando o Docker Compose, inicie o contêiner no modo anterior:
fonte