COPIA condizionale / AGGIUNGI in Dockerfile?


103

All'interno del mio Dockerfile vorrei COPIARE un file nella mia immagine se esiste, il file requirements.txt per pip sembra un buon candidato, ma come si otterrebbe?

COPY (requirements.txt if test -e requirements.txt; fi) /destination
...
RUN  if test -e requirements.txt; then pip install -r requirements.txt; fi

o

if test -e requirements.txt; then
    COPY requiements.txt /destination;
fi
RUN  if test -e requirements.txt; then pip install -r requirements.txt; fi

Si prega di consultare qui: docs.docker.com/reference/builder
Tuan

4
@Tuan - Cosa in particolare a quel link aiuta a farlo?
ToolmakerSteve

Risposte:


24

Questo non è attualmente supportato (poiché sospetto che porterebbe a un'immagine non riproducibile, poiché lo stesso Dockerfile copierebbe o meno il file, a seconda della sua esistenza).

Ciò è ancora richiesto, nel numero 13045 , utilizzando i caratteri jolly: " COPY foo/* bar/" not work if no file in foo" (maggio 2015).
Non sarà implementato per ora (luglio 2015) in Docker, ma un altro strumento di compilazione come Bocker potrebbe supportarlo.


32
buona risposta, ma la logica del docker, IMO, è difettosa. se esegui lo stesso dockerfile con un contesto di compilazione diverso, otterrai un'immagine diversa. c'è da aspettarselo. l'utilizzo dello stesso contesto di compilazione darà la stessa immagine. e se inserisci le istruzioni di COPIA / AGGIUNTA condizionale nello stesso contesto di compilazione, otterrai la stessa immagine. in modo che controlli. sono solo i miei 2 centesimi però.
nathan g

Docker riguarda l'infrastruttura immutabile. I tuoi ambienti di sviluppo, staging e prod dovrebbero essere il 99,99% il più vicino possibile se non identici. Usa le variabili d'ambiente.
AndrewMcLagan

3
@AndrewMcLagan e se, ad esempio, un devambiente front-end viene eseguito con un server di sviluppo webpack e l' prodambiente equivalente funziona con una /distcartella statica? Questo è il caso della maggior parte delle configurazioni front-end oggi, e ovviamente deve prodnon può essere lo stesso qui. Allora come affrontarlo?
Jivan

Non utilizzo docker per sviluppare i front-end dei nodi. Il normale webpack localhost: 3000 ecc ... Sebbene avvii ancora il tuo ambiente docker dev locale in modo che il tuo front-end node / react / angular comunichi con tutto ciò che è in esecuzione nel tuo normale ambiente di container docker. Ad esempio API, redis, MySQL, mongo, ricerca elastica e qualsiasi altro micro servizio. Potresti ... eseguire un ambiente di sviluppo webpack in un container. Ma sento che è troppo lontano ...
AndrewMcLagan

@Jivan Che ne dici di usare un'immagine onbuild per definire le istruzioni comuni e poi costruire immagini specifiche per dev e prod. Il repository Docker Hub Node sembra contenere immagini onbuild per ogni versione del nodo: hub.docker.com/_/node . O forse potresti rotolare il tuo.
david_i_smith

83

Ecco una semplice soluzione alternativa:

COPY foo file-which-may-exist* /target

Assicurati che fooesista, poiché COPYnecessita di almeno una fonte valida.

Se file-which-may-existè presente, verrà anche copiato.

NOTA: assicurati che il carattere jolly non raccolga altri file che non intendi copiare. Per stare più attenti, potresti usare file-which-may-exist?invece ( ?corrisponde a un solo carattere).

O ancora meglio, usa una classe di caratteri come questa per assicurarti che solo un file possa essere abbinato:

COPY foo file-which-may-exis[t] /target

1
Puoi fare la stessa cosa con una cartella?
Benjamin Toueg

1
@BenjaminToueg: Sì, secondo i documenti puoi copiare file e cartelle.
jdhildeb

2
Funziona alla grande. Per i file con più destinazioni, ho copiato in una directory temporanea e poi li ho spostati dove necessario. COPY --from=docker /usr/bin/docker /usr/lib/libltdl.so* /tmp/docker/ RUN mv /tmp/docker/docker /usr/bin/docker RUN mv /tmp/docker/libltdl.so.7 /usr/lib/libltdl.so.7 || true(dove la libreria condivisa è l'entità sconosciuta.)
Adam K Dean

Quando si copiano più file esistenti, la destinazione deve essere una directory. Come funziona quando esistono sia foo che il tuo file-che-potrebbe-esistere *?
melchoir55

1
Quindi la risposta è "assicurati che ci sia un file" e poi una dimostrazione su come utilizzare l'operatore COPY? Non riesco a vedere come questo si collega alla domanda originale.
Derrend

27

Come affermato da questo commento , la risposta di Santhosh Hirekerur copia ancora il file, per archiviare una vera copia condizionale, puoi utilizzare questo metodo.

ARG BUILD_ENV=copy

FROM alpine as build_copy
ONBUILD COPY file /file

FROM alpine as build_no_copy
ONBUILD RUN echo "I don't copy"

FROM build_${BUILD_ENV}
# other stuff

Le ONBUILDistruzioni assicurano che il file venga copiato solo se il "ramo" è selezionato da BUILD_ENV. Imposta questa var usando un piccolo script prima di chiamaredocker build


2
Mi piace questa risposta perché mi ha aperto gli occhi non solo su ONBUILD, che è molto utile, ma sembra anche la più facile da integrare con altre variabili passate, ad esempio se vuoi impostare il tag in base a BUILD_ENV, o memorizzare uno stato in ENV.
DeusXMachina

Ho appena provato qualcosa del genere e ho ottenuto: Risposta di errore dal daemon: Dockerfile parse error line 52: nome non valido per la fase di compilazione: "site_builder _ $ {host_env}", il nome non può iniziare con un numero o contenere simboli
paulecoyote

9

Soluzione

Avevo requisiti per copiare FOLDER sul server in base alle variabili ENV. Ho preso l'immagine del server vuota. ha creato la struttura della cartella di distribuzione richiesta in nella cartella locale. quindi aggiunto sotto la riga a DockerFile copia la cartella nel contenitore. I n ultima riga punto di ingresso aggiunto per eseguire file.sh init prima finestra mobile avviare il server.

#below lines added to integrate testing framework
RUN mkdir /mnt/conf_folder
ADD install /mnt/conf_folder/install
ADD install_test /mnt/conf_folder/install_test
ADD custom-init.sh /usr/local/bin/custom-init.sh
ENTRYPOINT ["/usr/local/bin/custom-init.sh"]

Quindi crea il file custom-init.sh in locale con uno script simile a quello riportato di seguito

#!/bin/bash
if [ "${BUILD_EVN}" = "TEST" ]; then
    cp -avr /mnt/conf_folder/install_test/* /mnt/wso2das-3.1.0/
else
    cp -avr /mnt/conf_folder/install/* /mnt/wso2das-3.1.0/
fi;

Nel file docker-compose sotto le righe.

ambiente: - BUILD_EVN = TEST

Queste modifiche copiano la cartella nel contenitore durante la creazione della finestra mobile. quando eseguiamo docker-compose , copia o distribuisce la cartella richiesta effettiva sul server prima dell'avvio del server.


8
Ma le immagini docker sono stratificate. ADD li copierà nell'immagine indipendentemente
dall'istruzione

@MyUserInStackOverflow - Penso che l'idea di questa "soluzione alternativa" sia che sia install che install_test vengono copiati nell'immagine, ma quando l'immagine viene eseguita, solo una di queste cartelle viene copiata nella posizione finale. Se va bene che entrambi siano da qualche parte nell'immagine, questa potrebbe essere una tecnica ragionevole.
ToolmakerSteve

4

Copia tutti i file in una directory usa e getta, scegli a mano quello che desideri, elimina il resto.

COPY . /throwaway
RUN cp /throwaway/requirements.txt . || echo 'requirements.txt does not exist'
RUN rm -rf /throwaway

Puoi ottenere qualcosa di simile usando le fasi di compilazione, che si basa sulla stessa soluzione, usando la cpcopia condizionale. Utilizzando una fase di costruzione, l'immagine finale non includerà tutto il contenuto dall'iniziale COPY.

FROM alpine as copy_stage
COPY . .
RUN mkdir /dir_for_maybe_requirements_file
RUN cp requirements.txt /dir_for_maybe_requirements_file &>- || true

FROM alpine
# Must copy a file which exists, so copy a directory with maybe one file
COPY --from=copy_stage /dir_for_maybe_requirements_file /
RUN cp /dir_for_maybe_requirements_file/* . &>- || true
CMD sh

Sebbene questo risolva tecnicamente il problema, non diminuisce le dimensioni dell'immagine. Se stai cercando di copiare in modo condizionale qualcosa di enorme (come un modello di rete profonda), aumenti comunque la dimensione dell'immagine, a causa del modo in cui funziona l'overlay fs.
DeusXMachina

@DeusXMachina, quale versione di finestra mobile stai utilizzando? I documenti contraddicono ciò che stai dicendo docs.docker.com/develop/develop-images/multistage-build/… . Gli strati non devono essere preservati da una fase di costruzione non finale.
cdosborn

@cdosburn - L'ho osservato il 18.09. Stavo parlando principalmente del primo esempio, le build messe in scena dovrebbero evitare questo problema. E penso che ogni fase FROM si compatti ora, ma mi fai indovinare secondo i miei ricordi. Dovrò sperimentare alcune cose.
DeusXMachina

@DeusXMachina, a destra solo la seconda soluzione riduce le dimensioni dell'immagine.
cdosborn

questa è una bella soluzione per il mio caso. Copia un cachee, a seconda di cosa è la cache, scelgo cosa fare nei file di script!
Paschalis

1

Ho provato le altre idee, ma nessuna ha soddisfatto i nostri requisiti. L'idea è quella di creare un'immagine nginx di base per applicazioni web statiche figlio. Per motivi di sicurezza, ottimizzazione e standardizzazione, l'immagine di base deve essere in grado di eseguire RUNcomandi su directory aggiunte da immagini figlio. L'immagine di base non controlla quali directory vengono aggiunte dalle immagini figlio. Il presupposto è che le immagini del bambino saranno COPYrisorse da qualche parte sotto COMMON_DEST_ROOT.

Questo approccio è un trucco, ma l'idea è che l'immagine di base supporterà le COPYistruzioni per le directory da 1 a N aggiunte dall'immagine figlia. ARG PLACEHOLDER_FILEe ENV UNPROVIDED_DESTsono utilizzati per soddisfare <src>e <dest>requisiti per qualsiasi COPYistruzione non necessaria.

#
# base-image:01
#
FROM nginx:1.17.3-alpine
ENV UNPROVIDED_DEST=/unprovided
ENV COMMON_DEST_ROOT=/usr/share/nginx/html
ONBUILD ARG PLACEHOLDER_FILE
ONBUILD ARG SRC_1
ONBUILD ARG DEST_1
ONBUILD ARG SRC_2
ONBUILD ARG DEST_2
ONBUILD ENV SRC_1=${SRC_1:-PLACEHOLDER_FILE}
ONBUILD ENV DEST_1=${DEST_1:-${UNPROVIDED_DEST}}
ONBUILD ENV SRC_2=${SRC_2:-PLACEHOLDER_FILE}
ONBUILD ENV DEST_2=${DEST_2:-${UNPROVIDED_DEST}}

ONBUILD COPY ${SRC_1} ${DEST_1}
ONBUILD COPY ${SRC_2} ${DEST_2}

ONBUILD RUN sh -x \
    #
    # perform operations on COMMON_DEST_ROOT
    #
    && chown -R limited:limited ${COMMON_DEST_ROOT} \
    #
    # remove the unprovided dest
    #
    && rm -rf ${UNPROVIDED_DEST}

#
# child image
#
ARG PLACEHOLDER_FILE=dummy_placeholder.txt
ARG SRC_1=app/html
ARG DEST_1=/usr/share/nginx/html/myapp
FROM base-image:01

Questa soluzione presenta evidenti svantaggi come il numero fittizio PLACEHOLDER_FILEe hardcoded di istruzioni COPY supportate. Inoltre, non è possibile eliminare le variabili ENV utilizzate nell'istruzione COPY.

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.