Em resumo: não, suas VOLUME
instruções não estão corretas.
Os Dockerfile VOLUME
especificam um ou mais volumes, conforme os caminhos do contêiner. Mas não permite que o autor da imagem especifique um caminho de host. No lado do host, os volumes são criados com um nome muito semelhante ao ID dentro da raiz do Docker. Na minha máquina é isso /var/lib/docker/volumes
.
Nota: Como o nome gerado automaticamente é extremamente longo e não faz sentido da perspectiva de um ser humano, esses volumes geralmente são chamados de "sem nome" ou "anônimo".
Seu exemplo que usa um '.' O caractere nem será executado na minha máquina, não importa se eu coloco o ponto no primeiro ou no segundo argumento. Recebo esta mensagem de erro:
janela de encaixe: Resposta de erro do daemon: oci erro de tempo de execução: container_linux.go: 265: processo de contêiner inicial causado "process_linux.go: 368: init do contêiner causado \" open / dev / ptmx: arquivo ou diretório \ "".
Sei que o que foi dito até este ponto provavelmente não é muito valioso para alguém que está tentando entender VOLUME
e -v
certamente não fornece uma solução para o que você tenta realizar. Portanto, esperamos que os exemplos a seguir possam esclarecer um pouco mais essas questões.
Minitutorial: Especificando volumes
Dado este arquivo Docker:
FROM openjdk:8u131-jdk-alpine
VOLUME vol1 vol2
(Para o resultado deste minitutorial, não faz diferença se especificarmos vol1 vol2
ou /vol1 /vol2
- não me pergunte por que)
Construa:
docker build -t my-openjdk
Corre:
docker run --rm -it my-openjdk
Dentro do contêiner, execute ls
na linha de comando e você notará que existem dois diretórios; /vol1
e /vol2
.
A execução do contêiner também cria dois diretórios, ou "volumes", no lado do host.
Enquanto o contêiner estiver em execução, execute docker volume ls
na máquina host e você verá algo assim (substituí a parte do meio do nome por três pontos por questões de concisão):
DRIVER VOLUME NAME
local c984...e4fc
local f670...49f0
De volta ao contêiner , execute touch /vol1/weird-ass-file
(cria um arquivo em branco no local mencionado).
Este arquivo está agora disponível na máquina host, em um dos volumes não nomeados, lol. Foram necessárias duas tentativas porque tentei primeiro o primeiro volume listado, mas, eventualmente, encontrei meu arquivo no segundo volume listado, usando este comando na máquina host:
sudo ls /var/lib/docker/volumes/f670...49f0/_data
Da mesma forma, você pode tentar excluir este arquivo no host e ele também será excluído no contêiner.
Nota: A _data
pasta também é chamada de "ponto de montagem".
Saia do contêiner e liste os volumes no host. Eles se foram. Usamos o --rm
sinalizador ao executar o contêiner e essa opção efetivamente elimina não apenas o contêiner na saída, mas também os volumes.
Execute um novo contêiner, mas especifique um volume usando -v
:
docker run --rm -it -v /vol3 my-openjdk
Isso adiciona um terceiro volume e todo o sistema acaba tendo três volumes sem nome. O comando teria travado se tivéssemos especificado apenas -v vol3
. O argumento deve ser um caminho absoluto dentro do contêiner. No lado do host, o novo terceiro volume é anônimo e reside junto com os outros dois volumes /var/lib/docker/volumes/
.
Foi declarado anteriormente que Dockerfile
não é possível mapear para um caminho de host que meio que representa um problema para nós ao tentar trazer arquivos do host para o contêiner durante o tempo de execução. Uma -v
sintaxe diferente resolve esse problema.
Imagine que eu tenho uma subpasta no diretório do projeto ./src
que desejo sincronizar /src
dentro do contêiner. Este comando faz o truque:
docker run -it -v $(pwd)/src:/src my-openjdk
Ambos os lados do :
personagem esperam um caminho absoluto. O lado esquerdo é um caminho absoluto na máquina host, o lado direito é um caminho absoluto dentro do contêiner. pwd
é um comando que "imprime o diretório atual / ativo". Colocar o comando $()
leva o comando entre parênteses, o executa em um subshell e retorna o caminho absoluto para o diretório de nosso projeto.
Juntando tudo, suponha que temos ./src/Hello.java
em nossa pasta de projeto na máquina host com o seguinte conteúdo:
public class Hello {
public static void main(String... ignored) {
System.out.println("Hello, World!");
}
}
Nós construímos este Dockerfile:
FROM openjdk:8u131-jdk-alpine
WORKDIR /src
ENTRYPOINT javac Hello.java && java Hello
Nós executamos este comando:
docker run -v $(pwd)/src:/src my-openjdk
Isso imprime "Olá, mundo!".
A melhor parte é que estamos completamente livres para modificar o arquivo .java com uma nova mensagem para outra saída em uma segunda execução - sem precisar reconstruir a imagem =)
Considerações finais
Eu sou bastante novo no Docker, e o "tutorial" mencionado acima reflete as informações que reuni em um hackathon de três dias na linha de comando. Estou quase envergonhado por não ter conseguido fornecer links para uma documentação clara em inglês que respalda minhas declarações, mas sinceramente acho que isso se deve à falta de documentação e não a esforços pessoais. Sei que os exemplos funcionam conforme anunciado usando minha configuração atual, que é "Windows 10 -> Vagrant 2.0.0 -> Docker 17.09.0-ce".
O tutorial não resolve o problema "como podemos especificar o caminho do contêiner no Dockerfile e permitir que o comando run especifique apenas o caminho do host". Pode haver uma maneira, simplesmente não a encontrei.
Finalmente, tenho a sensação de que especificar VOLUME
no Dockerfile não é apenas incomum, mas provavelmente é uma prática recomendada para nunca ser usada VOLUME
. Por duas razões. A primeira razão pela qual já identificamos: Não podemos especificar o caminho do host - o que é bom, pois os Dockerfiles devem ser muito independentes dos detalhes de uma máquina host. Mas o segundo motivo é que as pessoas podem esquecer de usar a --rm
opção ao executar o contêiner. Pode-se lembrar de remover o recipiente, mas esqueça de remover o volume. Além disso, mesmo com o melhor da memória humana, pode ser uma tarefa assustadora descobrir qual de todos os volumes anônimos é seguro remover.
docker volume prune
pode ser usado para limpar os volumes que não estão anexados à execução de contêineres. Não quer dizer que ele vai ser fácil de distingiush os potencialmente importantes por id sozinho .../
, portanto,vol1
é relativo a/
, o que resolve/vol1
. Se você usarWORKDIR
para especificar um diretório de trabalho diferente/
,vol1
e/vol1
não iria apontar para o mesmo diretório.A especificação de uma
VOLUME
linha em um Dockerfile configura um pouco de metadados na sua imagem, mas é importante como esses metadados são usados.Primeiro, o que essas duas linhas fizeram:
A
WORKDIR
linha lá cria o diretório, se ele não existir, e atualiza alguns metadados da imagem para especificar todos os caminhos relativos, juntamente com o diretório atual para comandos como osRUN
que estarão nesse local. AVOLUME
linha lá especifica dois volumes , um é o caminho relativo.
e o outro é/usr/src/app
, ambos por acaso são do mesmo diretório. Na maioria das vezes, aVOLUME
linha contém apenas um único diretório, mas pode conter vários como você fez, ou pode ser uma matriz formatada em json.Você não pode especificar uma fonte de volume no Dockerfile : uma fonte comum de confusão ao especificar volumes em um Dockerfile está tentando corresponder à sintaxe de tempo de execução de uma fonte e destino no momento da criação da imagem; isso não funcionará . O Dockerfile pode especificar apenas o destino do volume. Seria uma exploração de segurança trivial se alguém pudesse definir a origem de um volume, pois poderia atualizar uma imagem comum no hub docker para montar o diretório raiz no contêiner e iniciar um processo em segundo plano dentro do contêiner como parte de um ponto de entrada que adiciona logins ao / etc / passwd, configura o systemd para iniciar um minerador de bitcoin na próxima reinicialização ou pesquisa no sistema de arquivos por cartões de crédito, SSNs e chaves privadas para enviar para um site remoto.
O que a linha VOLUME faz? Como mencionado, ele define alguns metadados da imagem para dizer que um diretório dentro da imagem é um volume. Como esses metadados são usados? Sempre que você cria um contêiner a partir dessa imagem, o docker força esse diretório a ser um volume. Se você não fornecer um volume em seu comando de execução ou compor arquivo, a única opção para o docker é criar um volume anônimo. Este é um volume nomeado local com um ID exclusivo longo para o nome e nenhuma outra indicação do motivo pelo qual ele foi criado ou quais dados ele contém (volumes anônimos são onde os dados se perdem). Se você substituir o volume, apontando para um volume nomeado ou host, seus dados serão enviados para lá.
VOLUME interrompe as coisas: você não pode desativar um volume depois de definido em um arquivo de encaixe. E mais importante, o
RUN
comando na janela de encaixe é implementado com contêineres temporários. Esses contêineres temporários receberão um volume anônimo temporário. Esse volume anônimo será inicializado com o conteúdo da sua imagem. Quaisquer gravações dentro do contêiner do seuRUN
comando serão feitas nesse volume. Quando oRUN
comando termina, as alterações na imagem são salvas e as alterações no volume anônimo são descartadas. Por esseVOLUME
motivo , eu recomendo fortemente não definir um arquivo Dockerfile interno. Isso resulta em um comportamento inesperado para usuários a jusante da sua imagem que desejam estender a imagem com dados iniciais no local do volume.Como você deve especificar um volume? Para especificar onde você deseja incluir volumes com sua imagem, forneça a
docker-compose.yml
. Os usuários podem modificar isso para ajustar o local do volume ao ambiente local e capturar outras configurações de tempo de execução, como portas de publicação e rede.Alguém deve documentar isso! Eles têm. O Docker inclui avisos sobre o uso do VOLUME em sua documentação no Dockerfile, juntamente com conselhos para especificar a origem no tempo de execução:
fonte
O
VOLUME
comando aDockerfile
é bastante legítimo, totalmente convencional, absolutamente bom de usar e não é preterido de qualquer maneira. Só preciso entender isso.Nós o usamos para apontar para quaisquer diretórios nos quais o aplicativo no contêiner gravará muito. Não usamos
VOLUME
apenas porque queremos compartilhar entre host e contêiner como um arquivo de configuração.O comando simplesmente precisa de um parâmetro; um caminho para uma pasta, em relação a
WORKDIR
se definido, de dentro do contêiner. O docker criará um volume em seu gráfico (/ var / lib / docker) e o montará na pasta no contêiner. Agora, o contêiner terá um local para gravar com alto desempenho. Sem oVOLUME
comando, a velocidade de gravação na pasta especificada será muito lenta porque agora o contêiner está usando suacopy on write
estratégia no próprio contêiner. Acopy on write
estratégia é a principal razão pela qual os volumes existem.Se você montar sobre a pasta especificada pelo
VOLUME
comando, o comando nunca será executado porqueVOLUME
só é executado quando o contêiner é iniciado, mais ou menos comoENV
.Basicamente, com o
VOLUME
comando, você obtém desempenho sem montar externamente nenhum volume. Os dados também serão salvos nas execuções de contêiner, sem montagens externas. Então, quando estiver pronto, basta montar algo sobre ele.Alguns bons exemplos de casos de uso:
- logs
- pastas temporárias
Alguns casos de uso ruins:
- arquivos estáticos
- configurações
- código
fonte
VOLUME
diretórios para configurações. No entanto, depois de montar uma configuração, você precisará montar esse diretório e, portanto, oVOLUME
comando não será executado. Portanto, não faz sentido usar oVOLUME
comando em um diretório especificado para uma configuração. Também inicializar um gráfico de volume com um único arquivo estático de somente leitura é um sério exagero. Por isso, mantenho o que disse, sem necessidade deVOLUME
comando nas configurações.Para entender melhor as
volume
instruções no dockerfile, vamos aprender o uso típico do volume na implementação oficial do arquivo docker do mysql.Referência: https://github.com/docker-library/mysql/blob/3362baccb4352bcf0022014f67c1ec7e6808b8c5/8.0/Dockerfile
O
/var/lib/mysql
é o local padrão do MySQL que armazena arquivos de dados.Ao executar o contêiner de teste apenas para fins de teste, você não pode especificar seu ponto de montagem, por exemplo
então a instância do contêiner mysql usará o caminho de montagem padrão especificado pela
volume
instrução em dockerfile. os volumes são criados com um nome muito semelhante ao ID dentro da raiz do Docker, isso é chamado de volume "sem nome" ou "anônimo". Na pasta do sistema host subjacente / var / lib / docker / volumes.Isso é muito conveniente para fins de teste rápido, sem a necessidade de especificar o ponto de montagem, mas ainda pode obter o melhor desempenho usando o Volume para armazenamento de dados, não a camada de contêiner.
Para um uso formal, você precisará especificar o caminho da montagem usando volume nomeado ou montagem de ligação, por exemplo
O comando monta o diretório / my / own / datadir do sistema host subjacente como / var / lib / mysql dentro do contêiner.O diretório de dados / my / own / datadir não será excluído automaticamente, até o contêiner será excluído.
Uso da imagem oficial do mysql (por favor, verifique a seção "Onde Armazenar Dados"):
Referência: https://hub.docker.com/_/mysql/
fonte
-v
usá-lo sem definir o volume no DockerfileDe qualquer forma, não considero bom o uso do VOLUME, exceto se você estiver criando uma imagem para si e mais ninguém a usará.
Fui impactado negativamente devido ao VOLUME exposto nas imagens de base que estendi e só descobri o problema depois que a imagem já estava em execução, como o wordpress que declara a
/var/www/html
pasta como VOLUME , e isso significava que todos os arquivos foram adicionados ou alterados durante o estágio de construção não é considerado e as alterações ao vivo persistem, mesmo que você não saiba. Existe uma solução alternativa feia para definir o diretório da Web em outro local, mas esta é apenas uma solução ruim para uma necessidade mais simples: basta remover a diretiva VOLUME.Você pode atingir a intenção do volume facilmente usando a
-v
opção, isso não apenas deixa claro quais serão os volumes do contêiner (sem precisar dar uma olhada no Dockerfile e nos Dockerfiles pai), mas também oferece ao consumidor a opção de use o volume ou não.É basicamente ruim usar VOLUMES devido aos seguintes motivos, como dito por esta resposta :
Ter a opção de cancelar a declaração de um volume ajudaria, mas apenas se você conhecer os volumes definidos no arquivo docker que gerou a imagem (e os arquivos docker pai!). Além disso, um VOLUME pode ser adicionado em versões mais recentes de um Dockerfile e interromper as coisas inesperadamente para os consumidores da imagem.
Outra boa explicação ( sobre a imagem do oracle com VOLUME , que foi removida ): https://github.com/oracle/docker-images/issues/640#issuecomment-412647328
Mais casos em que VOLUME quebrou coisas para as pessoas:
Uma solicitação pull para adicionar opções para redefinir propriedades da imagem pai (incluindo VOLUME) foi encerrada e está sendo discutida aqui (e você pode ver vários casos de pessoas afetadas adversamente devido aos volumes definidos nos arquivos de encaixe), que possui um comentário com um bom explicação contra VOLUME:
Eu também considero EXPOSE ruim, mas tem menos efeitos colaterais. A única coisa boa que eu posso ver sobre VOLUME e EXPOSE é sobre documentação, e eu os consideraria bons se eles servissem apenas para isso (sem efeitos colaterais).
TL; DR
Considero que o melhor uso do VOLUME deve ser preterido.
fonte