Docker ADD vs VOLUME

116

Estou aprendendo o Docker e tenho dúvidas sobre quando e onde usar ADDeVOLUME . Aqui está o que eu acho que ambos fazem:

ADICIONAR

Copie os arquivos para a imagem no momento da construção. A imagem contém todos os arquivos para que você possa implantar com muita facilidade. Por outro lado, a necessidade de construir todas as vezes não parece uma boa ideia no desenvolvimento porque a construção requer que o desenvolvedor execute um comando para reconstruir o contêiner; além disso, construir o contêiner pode ser demorado.

VOLUME

Eu entendo que usando docker run -vvocê pode montar uma pasta de host dentro do seu contêiner, desta forma, você pode facilmente modificar os arquivos e ver o aplicativo no seu contêiner reagir às mudanças. Parece ótimo em desenvolvimento, mas não tenho certeza de como implantar meus arquivos dessa maneira.

Cristian Garcia
fonte
3
Em geral, é melhor a preferir COPYa ADD. Eles são quase iguais, mas ADDtêm algumas habilidades extras para escrever URLs e arquivos compactados que podem ser surpreendentes.
Adrian Mouat
2
@jamesmstone - esse link (e os documentos docker oficiais) recomendam o oposto - use COPY em vez de ADD.
Engenheiro de Software de
oops, você está certo - saúde!
jamesmstone

Respostas:

183

ADICIONAR

A diferença fundamental entre esses dois é que ADDtudo o que você está adicionando, seja uma pasta ou apenas um arquivo, realmente faz parte da imagem . Qualquer pessoa que usar a imagem que você construiu depois terá acesso a tudo que você quiser ADD. Isso é verdadeiro mesmo se você removê-lo posteriormente, porque o Docker funciona em camadas e a ADDcamada ainda existirá como parte da imagem. Para ser claro, você só precisa fazer ADDalgo em tempo de construção e nunca mais ADDem tempo de execução.

Alguns exemplos de casos em que você deseja usar ADD:

  • Você tem alguns requisitos em um arquivo requirements.txt que deseja referenciar e instalar no Dockerfile. Você pode então fazer: ADD ./requirements.txt /requirements.txtseguido porRUN pip install -r /requirements.txt
  • Você deseja usar o código do seu aplicativo como contexto no Dockerfile, por exemplo, se quiser definir o diretório do aplicativo como o diretório de trabalho na sua imagem e ter o comando padrão em um contêiner executado a partir da sua imagem realmente executar o seu aplicativo, você pode fazer:

    ADD ./ /usr/local/git/my_app

    WORKDIR /usr/local/git/my_app

    CMD python ./main.py

VOLUME

O volume, por outro lado, permite que um contêiner executado a partir de sua imagem tenha acesso a algum caminho em qualquer máquina local em que o contêiner esteja sendo executado. Você não pode usar arquivos do seu VOLUMEdiretório no Dockerfile . Qualquer coisa em seu diretório de volume não estará acessível em tempo de compilação, mas estará acessível em tempo de execução .

Alguns exemplos de casos em que você deseja usar VOLUME:

  • O aplicativo que está sendo executado em seu contêiner faz log-in /var/log/my_app. Você deseja que esses logs estejam acessíveis na máquina host e não sejam excluídos quando o contêiner for removido. Você pode fazer isso criando um ponto de montagem em /var/log/my_appadicionando VOLUME /var/log/my_appao Dockerfile e, em seguida, executando o contêiner comdocker run -v /host/log/dir/my_app:/var/log/my_app some_repo/some_image:some_tag
  • Você tem alguns arquivos de configurações locais aos quais deseja que o aplicativo no contêiner tenha acesso. Talvez esses arquivos de configuração sejam diferentes em sua máquina local vs desenvolvimento vs produção. Especialmente se esses arquivos de configuração forem secretos, caso em que você definitivamente não os quer em sua imagem . Uma boa estratégia nesse caso é adicionar VOLUME /etc/settings/my_app_settingsao seu Dockerfile, executar seu contêiner com docker run -v /host/settings/dir:/etc/settings/my_app_settings some_repo/some_image:some_tage certificar-se de que / host / settings / dir existe em todos os ambientes que você espera que seu aplicativo seja executado.
Eli
fonte
13
De longe a postagem mais útil que encontrei até agora em ADD e VOLUME
Jasmeet
5
O que acontece se VOLUME for especificado, mas não for fornecido durante a execução do docker (por exemplo, o parâmetro -v xxx está ausente)? É o resp. VOLUME então efetivamente se tornando transiente?
col.panic
Dentro de um Dockerfile, os volumes provavelmente são destinados apenas para persistência e / ou depuração, mas você pode usar a opção de linha de comando de volume para colocar um aplicativo em uma imagem existente (não é necessário Dockerfile) e executá-lo assim docker run -v $HOST_PATH:$CONTAINER_PATH node:latest node $CONTAINER_PATH/app.js.
Chinoto Vokro
bom detalhe de "camada"
stratovarius
27

A VOLUMEinstrução cria um volume de dados em seu contêiner do Docker no tempo de execução. O diretório fornecido como um argumento para VOLUMEé um diretório que ignora o Union File System e é usado principalmente para dados persistentes e compartilhados.

Se você executar docker inspect <your-container>, verá na Mountsseção que há um Sourceque representa o local do diretório no host e um Destinationque representa o local do diretório montado no contêiner. Por exemplo,

"Mounts": [
  {
    "Name": "fac362...80535",
    "Source": "/var/lib/docker/volumes/fac362...80535/_data",
    "Destination": "/webapp",
    "Driver": "local",
    "Mode": "",
    "RW": true,
    "Propagation": ""
  }
]

Aqui estão três casos de uso para docker run -v:

  1. docker run -v /data: Isso é análogo a especificar a VOLUMEinstrução em seu Dockerfile.
  2. docker run -v $host_path:$container_path: Isso permite que você monte a $host_pathpartir de seu host $container_pathem seu contêiner durante o tempo de execução. No desenvolvimento, isso é útil para compartilhar o código-fonte em seu host com o contêiner. Na produção, isso pode ser usado para montar coisas como as informações de DNS do host (encontradas em /etc/resolv.conf) ou segredos no contêiner. Por outro lado, você também pode usar essa técnica para gravar os logs do contêiner em pastas específicas no host. Ambos $host_pathe$container_path devem ser caminhos absolutos.
  3. docker run -v my_volume:$container_path: Isso cria um volume de dados em seu contêiner em $container_pathe nomeia-o my_volume. É essencialmente o mesmo que criar e nomear um volume usando docker volume create my_volume. Nomear um volume como este é útil para um volume de dados de contêiner e um volume de armazenamento compartilhado usando um driver de armazenamento de vários hosts como o Flocker .

Observe que a abordagem de montar uma pasta de host como um volume de dados não está disponível no Dockerfile. Para citar a documentação do docker ,

Nota: Isso não está disponível em um Dockerfile devido à portabilidade e ao propósito de compartilhamento dele. Como o diretório do host é, por natureza, dependente do host, um diretório do host especificado em um Dockerfile provavelmente não funcionaria em todos os hosts.

Agora, se você deseja copiar seus arquivos para contêineres em ambientes que não sejam de desenvolvimento, você pode usar as instruções ADDou COPYem seu Dockerfile. Eles são o que eu geralmente uso para implantação de não desenvolvimento.

ivan.sim
fonte
3
Devo criar 2 arquivos docker? Um para desenvolvimento e outro para implantação?
Cristian Garcia
Acho que não. Não há nada de errado em ter a ADDinstrução em seu Dockerfile, já que ela é executada apenas pelo docker buildcomando. Isso é necessário quando outros constroem seu contêiner pela primeira vez e quando você está pronto para implantá-lo em outros ambientes de não desenvolvimento.
ivan.sim
3
Mas não seria mais eficiente construir uma imagem sem os arquivos e usar o -vcomando para desenvolvimento, e fazer com que outro arquivo docker criasse uma imagem que inclui os arquivos ADDpara implantação?
Cristian Garcia
1
É uma troca que você precisa decidir. Escolha o que funciona para você. Quanto tempo leva a compilação com um ADD? Alguns segundos no total? Se você tiver dois arquivos Dockerfile e os compartilhar com outras pessoas (ou publicá-los no registro do docker ), qual é o padrão? Você terá alguma sobrecarga de manutenção extra para garantir que o Dockerfile padrão certo chegue aos usuários certos. Mas no final do dia, você decide o que funciona melhor para você. Pessoalmente, gosto de garantir que haja um e apenas um Dockerfile para construir meu contêiner.
ivan.sim
11
A propósito, acho que é bom primeiro ADICIONAR e depois sobrescrever esse acréscimo com -v para desenvolvimento. Dessa forma, você não precisará de Dockerfiles separados.
Attila Szeremi