Como incluir arquivos fora do contexto de construção do Docker?

462

Como posso incluir arquivos fora do contexto de construção do Docker usando o comando "ADD" no arquivo do Docker?

Na documentação do Docker:

O caminho deve estar dentro do contexto da construção; não é possível ADICIONAR ../something/something, porque a primeira etapa de uma construção do docker é enviar o diretório de contexto (e subdiretórios) para o daemon do docker.

Não quero reestruturar todo o meu projeto apenas para acomodar o Docker nesta questão. Quero manter todos os meus arquivos do Docker no mesmo subdiretório.

Além disso, parece que o Docker ainda não suporta (e talvez nunca) os links simbólicos: O comando Dockerfile ADD não segue os links simbólicos no host # 1676.

A única outra coisa em que consigo pensar é incluir uma etapa de pré-compilação para copiar os arquivos no contexto de compilação do Docker (e configurar meu controle de versão para ignorar esses arquivos). Existe uma solução melhor para isso?

ben_frankly
fonte
94
Essa deve ser a pior coisa do Docker. Do meu ponto de vista, não existe um "projeto Docker". O Docker é para projetos de remessa. É apenas uma ferramenta. Não quero ter que reconstruir todo o meu projeto para acomodar o docker, adicionando .dockerignore etc. No final do dia, quem sabe quanto tempo o Docker vai durar? Seria ótimo ter uma separação entre código (projeto angular) e qualquer meio de implementá-lo (por exemplo, janela de encaixe). Afinal, não há realmente nenhum benefício em ter um arquivo docker próximo a todo o resto. É só ligar as coisas, a fim de criar uma imagem :(
TigerBear 15/01
3
Sim, este é um grande problema. Estou enfrentando o mesmo problema e tenho um arquivo binário de tamanho maior (já compactado) que não quero copiar para cada contexto de construção do Docker. Prefiro obtê-lo da sua localização atual (fora do contexto de construção do Docker). E não quero mapear um volume em tempo de execução, porque estou tentando copiar / adicionar o arquivo no momento da compilação, descompactar e fazer o que for necessário para que certos binários sejam inseridos na imagem. Desta forma, girar os contêineres é rápido.
feijão Camisa
Eu encontrei uma estrutura boa e eu explicar com detalhes em stackoverflow.com/a/53298446/433814
Marcello de Sales
1
o problema com as construções do docker é o conceito inventado de "contexto". Dockerfiles não são suficientes para definir uma compilação, a menos que sejam colocados em um diretório estratégico (também conhecido como contexto), ou seja, "/" como extremo, para que você possa acessar qualquer caminho (observe que isso não é a coisa certa a ser feita em um projeto em sã ou ..., além disso, torna o docker muito lento, porque o docker verifica todo o contexto no início). Você pode criar uma imagem do Docker com todos os arquivos necessários e FROMcontinuar a partir daí. Eu não alteraria a estrutura do projeto para acomodar o Docker (ou qualquer ferramenta de construção).
Devis L.

Respostas:

411

A melhor maneira de contornar isso é especificar o Dockerfile independentemente do contexto de construção, usando -f.

Por exemplo, este comando dará ao comando ADD acesso a qualquer coisa em seu diretório atual.

docker build -f docker-files/Dockerfile .

Atualização : O Docker agora permite ter o Dockerfile fora do contexto de compilação (corrigido em 18.03.0-ce, https://github.com/docker/cli/pull/886 ). Então você também pode fazer algo como

docker build -f ../Dockerfile .
Cyberwiz
fonte
8
@Ro. você usa a dockerfile:propriedade na build:seção no arquivo Compose docs.docker.com/compose/compose-file/#/compose-file-reference
Emerson Farrugia
3
Recebo "O Dockerfile deve estar dentro do contexto de construção" - eu realmente gostaria de ter um Dockerfile que possa residir abaixo do contexto de construção atual. No seu exemplo, você tem o Dockerfile dentro / abaixo do contexto de compilação atual, o que obviamente funciona.
Alexander Mills
3
Sim, eu só quero um Dockerfile compartilhada que corresponde a vários subdiretórios, todos os quais são "contextos construir"
Alexander Mills
51
Isso resolve o problema do OP de querer ADDum arquivo que esteja fora do diretório de contexto? É isso que estou tentando fazer, mas não acho que o uso -ftorne os arquivos externos adicionáveis.
Sridhar Sarnobat
18
Não pode upvote este o suficiente .. na minha janela de encaixe-compose.yml eu tenho: build: context: .., dockerfile: dir/Dockerfile. Agora meu contexto de construção é o diretório pai!
Mike Gleason Jr. Couturier
51

Costumo encontrar-me utilizando a --build-argopção para esse fim. Por exemplo, depois de colocar o seguinte no Dockerfile:

ARG SSH_KEY
RUN echo "$SSH_KEY" > /root/.ssh/id_rsa

Você pode apenas fazer:

docker build -t some-app --build-arg SSH_KEY="$(cat ~/file/outside/build/context/id_rsa)" .

Mas observe o seguinte aviso na documentação do Docker :

Aviso: Não é recomendável usar variáveis ​​em tempo de construção para transmitir segredos, como chaves do github, credenciais do usuário etc. Os valores das variáveis ​​em tempo de construção são visíveis para qualquer usuário da imagem com o comando docker history.

user2658308
fonte
6
Este é um péssimo conselho sem um grande aviso. Na documentação do Docker: "Aviso: não é recomendável usar variáveis ​​em tempo de construção para transmitir segredos, como chaves do github, credenciais de usuário etc. Os valores das variáveis ​​em tempo de construção são visíveis para qualquer usuário da imagem com o comando histórico do estivador." [1] Em outras palavras, o exemplo dado neste exemplo divulga a chave SSH privada na imagem da janela de encaixe. Em alguns contextos, isso pode ser bom. docs.docker.com/engine/reference/builder/#arg
sheldonh 16/01
3
Finalmente, para superar este problema de segurança, você pode usar técnicas como esmagando ou multicelulares-constrói: vsupalov.com/build-docker-image-clone-private-repo-ssh-key
Jojo
46

No Linux, você pode montar outros diretórios em vez de simbolizá-los

mount --bind olddir newdir

Consulte /superuser/842642 para obter mais detalhes.

Não sei se algo semelhante está disponível para outros sistemas operacionais. Também tentei usar o Samba para compartilhar uma pasta e remontá-la no contexto do Docker, que também funcionou.

Günter Zöchbauer
fonte
2
Somente o root pode ligar diretórios
jjcf89
28

Passei um bom tempo tentando descobrir um bom padrão e como explicar melhor o que está acontecendo com esse suporte a recursos. Percebi que a melhor maneira de explicar era a seguinte ...

  • Dockerfile: verá apenas arquivos em seu próprio caminho relativo
  • Contexto: um local no "espaço" onde os arquivos que você deseja compartilhar e seu Dockerfile serão copiados para

Então, com isso dito, aqui está um exemplo do Dockerfile que precisa reutilizar um arquivo chamado start.sh

Dockerfile

Ele ALWAYSserá carregado a partir de seu caminho relativo, tendo o próprio diretório atual como localreferência aos caminhos que você especificar.

COPY start.sh /runtime/start.sh

arquivos

Considerando essa idéia, podemos pensar em ter várias cópias para os Dockerfiles criando coisas específicas, mas todas elas precisam de acesso ao start.sh.

./all-services/
   /start.sh
   /service-X/Dockerfile
   /service-Y/Dockerfile
   /service-Z/Dockerfile
./docker-compose.yaml

Considerando essa estrutura e os arquivos acima, aqui está um docker-compose.yml

docker-compose.yaml

  • Neste exemplo, seu shareddiretório de contexto é o runtimediretório
    • Mesmo modelo mental aqui, pense que todos os arquivos nesse diretório são movidos para o chamado context.
    • Da mesma forma, basta especificar o Dockerfile que você deseja copiar para o mesmo diretório. Você pode especificar isso usando dockerfile.
  • o diretório em que seu conteúdo principal está localizado é o contexto real a ser definido.

O docker-compose.ymlé o seguinte

version: "3.3"
services:

  service-A
    build:
      context: ./all-service
      dockerfile: ./service-A/Dockerfile

  service-B
    build:
      context: ./all-service
      dockerfile: ./service-B/Dockerfile

  service-C
    build:
      context: ./all-service
      dockerfile: ./service-C/Dockerfile
  • all-serviceé definido como o contexto, o arquivo compartilhado start.shé copiado para lá e o Dockerfile especificado por cada um dockerfile.
  • Cada um deve ser construído à sua maneira, compartilhando o arquivo inicial!

Felicidades!

Marcello de Sales
fonte
1
Seu ponto Dockerfile não é completamente verdade, como apontado pela resposta aceita, se você estiver em uma hierarquia de pastas a/b/c, então sim correndo docker build .em cnão lhe permitirá o acesso ../file-in-b. Mas acho que o entendimento geral geral nisto (ou pelo menos o meu era) é que o contexto é definido pelo local indicado pelo primeiro argumento do comando build, não pelo local do Dockerfile. Portanto, conforme declarado na resposta aceita: from a: docker build -f a/b/c/Dockerfile . significa que agora no Dockerfile .está a pastaa
β.εηοιτ.βε
1
Citando os documentos do Dockerfile: os caminhos dos arquivos e diretórios serão interpretados como relativos à origem do contexto da construção.
Nishant George Agrwal
18

Se você ler a discussão no problema 2745, não apenas o docker poderá suportar links simbólicos, mas também a adição de arquivos fora do seu contexto. Parece ser uma filosofia de design que os arquivos que entram na construção do Docker devem explicitamente fazer parte de seu contexto ou pertencer a uma URL na qual é presumivelmente implantada também com uma versão fixa, para que a construção possa ser repetida com URLs ou arquivos conhecidos fornecidos com o container docker.

Prefiro construir a partir de uma fonte controlada por versão - ou seja, docker build -t stuff http://my.git.org/repo - caso contrário, estou construindo de algum lugar aleatório com arquivos aleatórios.

fundamentalmente, não .... - SvenDowideit, Docker Inc

Apenas minha opinião, mas acho que você deve se reestruturar para separar os repositórios de código e docker. Dessa forma, os contêineres podem ser genéricos e extrair qualquer versão do código no tempo de execução, em vez do tempo de compilação.

Como alternativa, use o docker como seu artefato de implantação de código fundamental e, em seguida, coloque o arquivo docker na raiz do repositório de códigos. se você seguir essa rota, provavelmente faz sentido ter um contêiner de janela de encaixe pai para obter detalhes mais gerais do nível do sistema e um contêiner filho para configuração específica ao seu código.

Usman Ismail
fonte
Por que usar o docker?
lscoughlin
11

Acredito que a solução mais simples seria alterar o próprio 'contexto'.

Então, por exemplo, em vez de dar:

docker build -t hello-demo-app .

que define o diretório atual como o contexto, digamos que você queira o diretório pai como contexto, basta usar:

docker build -t hello-demo-app ..
Anshuman Manral
fonte
6
Eu acho que isso quebra .dockerignore: - \
NullVoxPopuli
Eu desisti do .dockerignore e, em vez disso, criei a pasta docker gerenciada Makefile que contém apenas os arquivos necessários para o contexto da compilação ... Eu só preciso chamar make builde ele puxa todos os arquivos necessários se eles foram atualizados e, em seguida, chama a construção apropriada da docker ... Preciso fazer um trabalho extra, mas funciona perfeitamente porque estou no controle total.
Sahsahae 26/11/19
3

Usando o docker-compose, consegui isso criando um serviço que monta os volumes necessários e confirmando a imagem do contêiner. Em seguida, no serviço subseqüente, confio na imagem confirmada anteriormente, que possui todos os dados armazenados em locais montados. Você precisará copiar esses arquivos para o destino final, pois os diretórios montados no host não são confirmados ao executar um docker commitcomando

Você não precisa usar o docker-composite para fazer isso, mas isso facilita a vida

# docker-compose.yml

version: '3'
  services:
    stage:
      image: alpine
      volumes:
        - /host/machine/path:/tmp/container/path
      command: bash -c "cp -r /tmp/container/path /final/container/path"
    setup:
      image: stage
# setup.sh

# Start "stage" service
docker-compose up stage

# Commit changes to an image named "stage"
docker commit $(docker-compose ps -q stage) stage

# Start setup service off of stage image
docker-compose up setup
Kris Rivera
fonte
1

Eu tive esse mesmo problema em um projeto e em alguns arquivos de dados que não consegui mover dentro do contexto de repo por motivos HIPPA. Acabei usando 2 Dockerfiles. Um cria o aplicativo principal sem o material necessário fora do contêiner e o publica no repositório interno. Em seguida, um segundo arquivo docker extrai essa imagem e adiciona os dados e cria uma nova imagem que é implantada e nunca armazenada em nenhum lugar. Não é o ideal, mas funcionou para meus propósitos de manter informações confidenciais fora do repositório.

Annulet Consulting
fonte
1

Uma solução fácil pode ser simplesmente montar o volume (usando o sinalizador -v ou --mount) no contêiner quando você o executa e acessa os arquivos dessa maneira.

exemplo:

docker run -v /path/to/file/on/host:/desired/path/to/file/in/container/ image_name

para mais informações, consulte: https://docs.docker.com/storage/volumes/

Ben Wex
fonte
Observe que isso só funciona se o volume for uma dependência de tempo de execução. Para dependências de tempo de compilação, docker runé tarde demais.
user3735633 17/02
1

Como é descrito neste problema do GitHub, a compilação realmente acontece /tmp/docker-12345, portanto, um caminho relativo como ../relative-add/some-fileé relativo a /tmp/docker-12345. Seria, portanto, procurar /tmp/relative-add/some-file, o que também é mostrado na mensagem de erro. *

Não é permitido incluir arquivos fora do diretório de compilação, portanto, isso resulta na mensagem "Caminho Proibido". "

Rocksn17
fonte
0

Uma maneira rápida e suja é configurar o contexto da construção em quantos níveis você precisar - mas isso pode ter consequências. Se você estiver trabalhando em uma arquitetura de microsserviços assim:

./Code/Repo1
./Code/Repo2
...

Você pode definir o contexto de construção para o pai Code diretório e acessar tudo, mas acontece que, com um grande número de repositórios, isso pode resultar em um longo período de construção.

Um exemplo de situação pode ser que outra equipe mantenha um esquema de banco de dados Repo1e o código da sua equipe emRepo2 depender disso. Você deseja dockerizar essa dependência com alguns de seus próprios dados iniciais, sem se preocupar com alterações de esquema ou poluir o repositório da outra equipe (dependendo de quais são as alterações, você ainda poderá alterar seus scripts de dados iniciais). A segunda abordagem é hacky, mas contorna a questão das compilações longas:

Crie um script sh (ou ps1) ./Code/Repo2para copiar os arquivos necessários e chamar os comandos do docker que você deseja, por exemplo:

#!/bin/bash
rm -r ./db/schema
mkdir ./db/schema

cp  -r ../Repo1/db/schema ./db/schema

docker-compose -f docker-compose.yml down
docker container prune -f
docker-compose -f docker-compose.yml up --build

No arquivo docker-compose, basta definir o contexto como Repo2raiz e usar o conteúdo do ./db/schemadiretório em seu dockerfile sem se preocupar com o caminho. Lembre-se de que você corre o risco de comprometer acidentalmente esse diretório para o controle de origem, mas as ações de limpeza de script devem ser fáceis o suficiente.

user1007074
fonte
0

No meu caso, meu Dockerfile é escrito como um modelo contendo espaços reservados que estou substituindo por valor real usando meu arquivo de configuração.

Portanto, não pude especificar esse arquivo diretamente, mas inseri-lo na construção do docker da seguinte maneira:

sed "s/%email_address%/$EMAIL_ADDRESS/;" ./Dockerfile | docker build -t katzda/bookings:latest . -f -;

Mas por causa do tubo, o COPYcomando não funcionou. Mas a maneira acima resolve isso -f -(explicitamente dizendo que o arquivo não é fornecido). Fazendo apenas -sem a -fbandeira, o contexto E o Dockerfile não são fornecidos, o que é uma ressalva.

Daniel Katz
fonte
0

O truque é reconhecer que você pode especificar o contexto no comando build para incluir arquivos do diretório pai, se você especificar o caminho do Docker, eu mudaria meu Dockerfile para ficar assim:

...
COPY ./ /dest/
...

Então meu comando build pode ficar assim:

docker built -t TAG -f DOCKER_FILE_PATH CONTEXT

No diretório do projeto

docker built -t username/project[:tag] -f ./docker/Dockerfile .

Do projeto / janela de encaixe

docker built -t username/project[:tag] -f ./docker/Dockerfile ..
daniekpo
fonte