Come includere file al di fuori del contesto di build di Docker?


462

Come posso includere file al di fuori del contesto di compilazione di Docker usando il comando "AGGIUNGI" nel file Docker?

Dalla documentazione Docker:

Il percorso deve essere all'interno del contesto della build; non puoi AGGIUNGERE ../something/something, perché il primo passo di una build docker è inviare la directory di contesto (e le sottodirectory) al demone docker.

Non voglio ristrutturare il mio intero progetto solo per accogliere Docker in questa materia. Voglio mantenere tutti i miei file Docker nella stessa sottodirectory.

Inoltre, sembra che Docker non supporti ancora (e potrebbe non mai) i collegamenti simbolici: il comando ADD Dockerfile non segue i collegamenti simbolici sull'host # 1676.

L'unica altra cosa a cui riesco a pensare è quella di includere un passaggio di pre-compilazione per copiare i file nel contesto di compilazione Docker (e configurare il mio controllo versione per ignorare quei file). C'è una soluzione migliore per quella?


94
Questa deve essere la cosa peggiore di Docker. Dal mio punto di vista, non esiste un "progetto Docker". Docker è per progetti di spedizione. È solo uno strumento. Non voglio dover ricostruire il mio intero progetto per ospitare docker, aggiungere .dockerignore ecc. Alla fine della giornata, chi sa quanto durerà Docker? Sarebbe bello avere una separazione tra il codice (cioè il progetto angolare) e qualunque mezzo significhi distribuirlo (cioè la finestra mobile). Dopotutto, non c'è davvero alcun vantaggio nell'avere un file docker vicino a tutto il resto.
Sta

3
Sì, questo è un grande svantaggio. Sto affrontando lo stesso problema e ho un file binario di dimensioni maggiori (già compresso) che non voglio copiare in ogni contesto di build Docker. Preferirei provarlo dalla sua posizione corrente (al di fuori del contesto di build Docker). E non voglio mappare un volume in fase di esecuzione, perché sto cercando di COPIARE / AGGIUNGERE il file in fase di compilazione e decomprimere e fare ciò di cui ho bisogno, quindi alcuni binari vengono inseriti nell'immagine. In questo modo la rotazione dei contenitori è rapida.
fagiolo jersey

Ho trovato una buona struttura e spiego con dettagli su stackoverflow.com/a/53298446/433814
Marcello de Sales

1
il problema con le build docker è il concetto inventato di "contesto". I file Docker non sono sufficienti per definire una build, a meno che non siano collocati in una directory strategica (aka contesto), ovvero "/" come un estremo, quindi puoi accedere a qualsiasi percorso (nota che non è la cosa giusta da fare in un progetto sano o ..., inoltre rende le build della finestra mobile molto lente perché la finestra mobile esegue la scansione dell'intero contesto all'avvio). Puoi prendere in considerazione la creazione di un'immagine docker con tutti i file richiesti e di usarlo FROMper continuare da lì. Non cambierei la struttura del progetto per adattarsi a Docker (o qualsiasi strumento di compilazione).
Devis L.

Risposte:


413

Il modo migliore per aggirare questo problema è specificare il Dockerfile indipendentemente dal contesto di compilazione, usando -f.

Ad esempio, questo comando darà al comando ADD l'accesso a qualsiasi cosa nella directory corrente.

docker build -f docker-files/Dockerfile .

Aggiornamento : Docker ora consente di disporre del file Docker al di fuori del contesto di compilazione (risolto in 18.03.0-ce, https://github.com/docker/cli/pull/886 ). Quindi puoi anche fare qualcosa del genere

docker build -f ../Dockerfile .

8
@Ro. si utilizza la dockerfile:proprietà nella build:sezione nel file Compose docs.docker.com/compose/compose-file/#/compose-file-reference
Emerson Farrugia

3
Ottengo "Il Dockerfile deve essere all'interno del contesto di compilazione" - Mi piacerebbe davvero avere un Dockerfile che può risiedere al di sotto del contesto di compilazione corrente. Nel tuo esempio hai il Dockerfile all'interno / sotto l'attuale contesto di compilazione, che ovviamente funziona.
Alexander Mills,

3
Sì, voglio solo un Dockerfile condiviso che corrisponda a più sottodirectory, tutte "contesti di costruzione"
Alexander Mills,

51
Questo risolve il problema del PO di voler ADDun file che si trova al di fuori della directory di contesto? Questo è quello che sto cercando di fare, ma non credo che l'uso -fdi file esterni possa essere aggiunto.
Sridhar Sarnobat,

18
Non posso upvote questo basta .. nella mia finestra mobile-compose.yml ho: build: context: .., dockerfile: dir/Dockerfile. Ora il mio contesto di build è la directory principale!
Mike Gleason jr Couturier,

51

Mi trovo spesso a utilizzare l' --build-argopzione per questo scopo. Ad esempio dopo aver inserito quanto segue nel Dockerfile:

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

Puoi semplicemente fare:

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

Ma nota il seguente avviso dalla documentazione Docker :

Avvertenza: non è consigliabile utilizzare le variabili del tempo di costruzione per il passaggio di segreti come chiavi github, credenziali utente ecc. I valori delle variabili del tempo di costruzione sono visibili a qualsiasi utente dell'immagine con il comando cronologia finestra mobile.


6
Questo è un consiglio scadente senza un grande avvertimento. Dalla documentazione Docker: "Avvertenza: non è consigliabile utilizzare le variabili del tempo di costruzione per il passaggio di segreti come chiavi github, credenziali utente ecc. I valori delle variabili del tempo di costruzione sono visibili a qualsiasi utente dell'immagine con il comando cronologia docker." [1] In altre parole, l'esempio fornito in questo esempio rivela la chiave SSH privata nell'immagine docker. In alcuni contesti, potrebbe andare bene. docs.docker.com/engine/reference/builder/#arg
sheldonh,

3
Infine, per ovviare a questo problema di sicurezza, potresti usare tecniche come squash o build multistadio: vsupalov.com/build-docker-image-clone-private-repo-ssh-key
Jojo

46

Su Linux puoi montare altre directory invece di collegarle simbolicamente

mount --bind olddir newdir

Vedi /superuser/842642 per maggiori dettagli.

Non so se qualcosa di simile è disponibile per altri sistemi operativi. Ho anche provato a usare Samba per condividere una cartella e rimontarla nel contesto Docker che ha funzionato.


2
Solo root può associare le directory
jjcf89,

28

Ho trascorso un buon tempo cercando di capire un buon modello e come spiegare meglio cosa sta succedendo con questo supporto per le funzionalità. Mi sono reso conto che il modo migliore per spiegarlo era il seguente ...

  • Dockerfile: vedrà solo i file nel proprio percorso relativo
  • Contesto: un posto nello "spazio" in cui verranno copiati i file che si desidera condividere e il Dockerfile

Quindi, detto questo, ecco un esempio del Dockerfile che deve riutilizzare un file chiamato start.sh

Dockerfile

Si ALWAYScaricherà dal suo percorso relativo, avendo la directory corrente di se stesso come il localriferimento ai percorsi specificati.

COPY start.sh /runtime/start.sh

File

Considerando questa idea, possiamo pensare di avere più copie per i Dockerfile che costruiscono cose specifiche, ma tutti hanno bisogno dell'accesso a start.sh.

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

Considerando questa struttura e i file sopra, ecco un docker-compose.yml

finestra mobile-compose.yaml

  • In questo esempio, la shareddirectory di contesto è la runtimedirectory.
    • Lo stesso modello mentale qui, pensa che tutti i file sotto questa dir vengono spostati sul cosiddetto context.
    • Allo stesso modo, basta specificare il Dockerfile che si desidera copiare nella stessa directory. Puoi specificarlo usando dockerfile.
  • la directory in cui si trova il contenuto principale è il contesto effettivo da impostare.

Il docker-compose.ymlè il seguente

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-serviceviene impostato come contesto, il file condiviso start.shviene copiato lì e anche il file Docker specificato da ciascuno dockerfile.
  • Ognuno può essere costruito a modo suo, condividendo il file di avvio!

Saluti!


1
Il tuo punto su Dockerfile non è completamente vero, come indicato dalla risposta accettata, se ti trovi in ​​una gerarchia di cartelle a/b/c, quindi l'esecuzione docker build .in esecuzione cnon ti consentirà di accedere ../file-in-b. Ma penso che il malinteso generale in questo (o almeno il mio fosse) sia che il contesto è definito dalla posizione dichiarata dal primo argomento del comando build, non dalla posizione del Dockerfile. Quindi, come indicato nella risposta accettata: from a: docker build -f a/b/c/Dockerfile . significa che nel Dockerfile .è ora la cartellaa
β.εηοιτ.βε

1
Citando dai documenti Dockerfile: i percorsi di file e directory saranno interpretati come relativi alla fonte del contesto della build.
Nishant George Agrwal,

18

Se leggi la discussione nel numero 2745, non solo la finestra mobile potrebbe non supportare mai i collegamenti simbolici, ma potrebbe non supportare l'aggiunta di file al di fuori del tuo contesto. Sembra essere una filosofia di progettazione secondo cui i file che vanno nella build docker dovrebbero esplicitamente far parte del suo contesto o provenire da un URL in cui è presumibilmente distribuito anche con una versione fissa in modo che la build sia ripetibile con URL ben noti o file forniti con il contenitore docker.

Preferisco costruire da una fonte controllata dalla versione - cioè docker build -t stuff http://my.git.org/repo - altrimenti sto costruendo da un posto casuale con file casuali.

fondamentalmente no ... - SvenDowideit, Docker Inc

Solo la mia opinione, ma penso che dovresti ristrutturare per separare i repository di codice e docker. In questo modo i contenitori possono essere generici e inserire qualsiasi versione del codice in fase di esecuzione anziché in fase di compilazione.

In alternativa, utilizzare la finestra mobile come artefatto di distribuzione del codice fondamentale e quindi inserire il file docker nella radice del repository di codice. se segui questa strada probabilmente ha senso avere un contenitore docker principale per dettagli più generali a livello di sistema e un contenitore figlio per un'installazione specifica per il tuo codice.


Perché usare la finestra mobile allora?
lscoughlin,

11

Credo che la soluzione più semplice sarebbe quella di cambiare il "contesto" stesso.

Quindi, ad esempio, invece di dare:

docker build -t hello-demo-app .

che imposta la directory corrente come contesto, supponiamo che tu voglia la directory principale come contesto, basta usare:

docker build -t hello-demo-app ..

6
Penso che questo rompa .dockerignore: - \
NullVoxPopuli

Ho rinunciato a .dockerignore e invece ho creato la cartella docker gestita da Makefile che contiene solo i file necessari per il contesto di build ... Ho solo bisogno di chiamare make builde tira tutti i file necessari se sono stati aggiornati e quindi chiama la docker build appropriata ... Devo fare un lavoro extra, ma funziona perfettamente perché ho il pieno controllo.
Sahsahae,


3

Usando docker-compose, ho realizzato questo creando un servizio che monta i volumi di cui ho bisogno e commette l'immagine del contenitore. Quindi, nel servizio successivo, mi affido all'immagine precedentemente impegnata, che ha tutti i dati memorizzati in posizioni montate. Dovrai quindi copiare questi file nella loro destinazione finale, poiché le directory montate sull'host non vengono impegnate durante l'esecuzione di un docker commitcomando

Non è necessario utilizzare docker-compose per farlo, ma rende la vita un po 'più semplice

# 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

1

Ho avuto lo stesso problema con un progetto e alcuni file di dati che non sono stato in grado di spostare all'interno del contesto repository per motivi HIPPA. Ho finito per usare 2 Dockerfile. Uno crea l'applicazione principale senza le cose di cui avevo bisogno al di fuori del contenitore e le pubblica nel repository interno. Quindi un secondo file docker estrae quell'immagine e aggiunge i dati e crea una nuova immagine che viene quindi distribuita e mai archiviata da nessuna parte. Non è l'ideale, ma ha funzionato per i miei scopi di mantenere le informazioni sensibili fuori dal repository.


1

Una soluzione semplice potrebbe essere quella di montare semplicemente il volume (usando il flag -v o --mount) sul contenitore quando lo si esegue e si accede ai file in quel modo.

esempio:

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

per maggiori informazioni: https://docs.docker.com/storage/volumes/


Si noti che funziona solo se il volume è una dipendenza runtime. Per le dipendenze del tempo di costruzione, docker runè troppo tardi.
user3735633

1

Come è descritto in questo problema di GitHub in cui si verifica effettivamente la compilazione /tmp/docker-12345, quindi un percorso relativo come ../relative-add/some-fileè relativo /tmp/docker-12345. Quindi cercherebbe /tmp/relative-add/some-file, che è anche mostrato nel messaggio di errore. *

Non è consentito includere file al di fuori della directory di compilazione, quindi questo porta al messaggio "Percorso proibito". "


0

Un modo rapido e sporco è impostare il contesto di costruzione su tutti i livelli di cui hai bisogno, ma ciò può avere conseguenze. Se lavori in un'architettura di microservizi simile a questa:

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

È possibile impostare il contesto di compilazione sulla Codedirectory principale e quindi accedere a tutto, ma si scopre che con un numero elevato di repository, ciò può comportare che la compilazione richieda molto tempo.

Una situazione di esempio potrebbe essere quella in cui un altro team mantiene uno schema del database Repo1e inserisce il codice del proprio teamRepo2 dipende da questo. Si desidera agganciare questa dipendenza con alcuni dei propri dati seed senza preoccuparsi delle modifiche dello schema o inquinare il repository dell'altro team (a seconda di quali cambiamenti si potrebbero ancora cambiare gli script dei dati seed ovviamente) Il secondo approccio è disordinato ma aggira il problema delle build lunghe:

Creare uno script sh (o ps1) ./Code/Repo2per copiare i file necessari e richiamare i comandi docker desiderati, ad esempio:

#!/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

Nel file componi docker, imposta semplicemente il contesto come Repo2root e usa il contenuto della ./db/schemadirectory nel tuo file docker senza preoccuparti del percorso. Tenere presente che si corre il rischio di commettere accidentalmente questa directory sul controllo del codice sorgente, ma le azioni di pulizia dello scripting dovrebbero essere abbastanza facili.


0

Nel mio caso, il mio Dockerfile è scritto come un modello contenente segnaposto che sto sostituendo con un valore reale usando il mio file di configurazione.

Quindi non ho potuto specificare direttamente questo file ma inserirlo nella build docker in questo modo:

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

Ma a causa della pipe, il COPYcomando non ha funzionato. Ma il modo precedente risolve -f -(dicendo esplicitamente il file non fornito). Facendo solo -senza la -fbandiera, il contesto E il Dockerfile non sono forniti, il che è un avvertimento.


0

Il trucco è riconoscere che puoi specificare il contesto nel comando build per includere i file dalla directory principale se specifichi il percorso Docker, cambierei il mio Dockerfile in questo modo:

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

Quindi il mio comando build può apparire così:

docker built -t TAG -f DOCKER_FILE_PATH CONTEXT

Dalla directory del progetto

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

Dal progetto / finestra mobile

docker built -t username/project[:tag] -f ./docker/Dockerfile ..
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.