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.