Условное КОПИРОВАНИЕ / ДОБАВЛЕНИЕ в Dockerfile?

103

Внутри моих файлов Dockerfiles я хотел бы скопировать файл в свое изображение, если он существует, файл requirements.txt для pip кажется хорошим кандидатом, но как этого добиться?

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

или

if test -e requirements.txt; then
    COPY requiements.txt /destination;
fi
RUN  if test -e requirements.txt; then pip install -r requirements.txt; fi
Дерренд
источник
См. Здесь: docs.docker.com/reference/builder
Туан,
4
@Tuan - Что конкретно в этой ссылке помогает в этом?
ToolmakerSteve

Ответы:

24

В настоящее время это не поддерживается (поскольку я подозреваю, что это приведет к невоспроизводимому изображению, поскольку тот же Dockerfile будет копировать файл или нет, в зависимости от его существования).

Это все еще запрашивается в выпуске 13045 с использованием подстановочных знаков: « COPY foo/* bar/" not work if no file in foo» (май 2015 г.).
В настоящее время (июль 2015 г.) он не будет реализован в Docker, но другой инструмент сборки, такой как bocker, может это поддерживать.

VonC
источник
33
хороший ответ, но логика докеров, ИМО, ошибочна. если вы запустите тот же файл докеров с другим контекстом сборки, вы получите другой образ. этого и следовало ожидать. использование одного и того же контекста сборки даст то же изображение. и если вы вставите условные инструкции COPY / ADD в тот же контекст сборки, вы получите то же изображение. так что проверено. это всего лишь мои 2 цента.
nathan g 06
Docker - это неизменная инфраструктура. Среды dev, staging и prod должны быть на 99,99% как можно ближе, если не идентичны. Используйте переменные среды.
AndrewMcLagan
3
@AndrewMcLagan, что, если, например, интерфейсная devсреда работает с сервером разработки webpack, а эквивалентная prodсреда работает со /distстатической папкой? Так обстоит дело в большинстве конфигураций внешнего интерфейса сегодня, и, очевидно, devи prodне может быть таким же здесь. Итак, как с этим бороться?
Дживан
Я не использую докер для разработки внешних интерфейсов узла. Обычный локальный хост webpack: 3000 и т. Д. Хотя по-прежнему загружается локальная среда разработки докеров, поэтому ваш интерфейс node / response / angular взаимодействует со всем, что работает в вашей обычной среде контейнера докеров. Например, API, redis, MySQL, mongo, эластичный поиск и любые другие микросервисы. Вы .. можете .. запустить среду разработки webpack в контейнере. Но я чувствую, что это слишком далеко ...
Эндрю МакЛаган
@Jivan Как насчет использования образа onbuild для определения общих инструкций, а затем создания конкретных образов для разработчиков и продуктов. Репозиторий узла Docker Hub, по-видимому, содержит образы сборки для каждой версии узла: hub.docker.com/_/node . Или, может быть, вы могли бы свернуть свою собственную.
david_i_smith
84

Вот простой способ:

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

Убедитесь, что fooсуществует, поскольку COPYнужен хотя бы один действительный источник.

Если file-which-may-existприсутствует, он также будет скопирован.

ПРИМЕЧАНИЕ. Следует позаботиться о том, чтобы подстановочный знак не захватил другие файлы, которые вы не собираетесь копировать. Чтобы быть более осторожным, вы можете использовать file-which-may-exist?вместо ( ?соответствует только одному символу).

Или, что еще лучше, используйте такой класс символов, чтобы обеспечить сопоставление только одного файла:

COPY foo file-which-may-exis[t] /target
jdhildeb
источник
1
Можете ли вы сделать то же самое с папкой?
Benjamin Toueg 03
1
@BenjaminToueg: Да, согласно документации, вы можете копировать как файлы, так и папки.
jdhildeb 04
2
Это прекрасно работает. Файлы с несколькими адресатами я скопировал во временный каталог, а затем переместил в нужное место. 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(где общая библиотека - это неизвестная сущность.)
Адам К. Дин
При копировании нескольких существующих файлов местом назначения должен быть каталог. Как это работает, если существуют и foo, и ваш файл, который может существовать *?
melchoir55
1
Итак, ответ - «убедитесь, что есть файл», а затем демонстрация того, как использовать оператор COPY? Я не понимаю, как это связано с исходным вопросом.
derrend
27

Как указано в этом комментарии , ответ Santhosh Hirekerur по-прежнему копирует файл, чтобы заархивировать истинную условную копию, вы можете использовать этот метод.

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

В ONBUILDинструкции гарантирует , что файл только копируются , если «ветвь» выбирается BUILD_ENV. Установите эту переменную с помощью небольшого скрипта перед вызовомdocker build

Сию
источник
2
Мне нравится этот ответ, потому что он открыл мне глаза не только на ONBUILD, что очень удобно, но также кажется, что его проще всего интегрировать с другими переданными переменными, например, если вы хотите установить тег на основе BUILD_ENV или сохранить какое-то состояние в ENV.
DeusXMachina,
Я просто попробовал что-то подобное и получил: Ответ об ошибке от демона: строка ошибки синтаксического анализа Dockerfile 52: недопустимое имя для этапа сборки: "site_builder _ $ {host_env}", имя не может начинаться с числа или содержать символы
paulecoyote,
9

Обойти решение

У меня было требование скопировать FOLDER на сервер на основе переменных ENV. Я взял пустой образ сервера. создал необходимую структуру папок развертывания в локальной папке. затем добавьте строку ниже в DockerFile, скопируйте папку в контейнер. В последней строке добавлена ​​точка входа для выполнения init file.sh перед запуском сервера докером.

#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"]

Затем создайте файл custom-init.sh локально с помощью скрипта, как показано ниже

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

В файле docker-compose под строками.

среда: - BUILD_EVN = ТЕСТ

Эти изменения копируют папку в контейнер во время сборки докера. когда мы выполняем docker-compose up, он копирует или развертывает фактическую необходимую папку на сервере перед запуском сервера.

Santhosh Hirekerur
источник
8
Но образы докеров многослойны. ADD скопирует их в изображение независимо от оператора if, который вы упомянули ...
MyUserInStackOverflow
@MyUserInStackOverflow - я думаю, что идея этого «обходного пути» заключается в том, что и install, и install_test копируются в образ, но когда образ запускается, только одна из этих папок копируется в окончательное расположение. Если это нормально, что оба они где-то на изображении, это может быть разумным методом.
ToolmakerSteve
5

Скопируйте все файлы в одноразовый каталог, вручную выберите тот, который вам нужен, а остальные выбросьте.

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

Вы можете добиться чего-то похожего, используя этапы сборки, которые основаны на том же решении, используя cpдля условного копирования. Используя этап сборки, ваше окончательное изображение не будет включать весь контент из исходного 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
cdosborn
источник
Хотя это технически решает проблему, это не уменьшает размер изображения. Если вы пытаетесь условно скопировать что-то огромное (например, модель глубокой сети), вы все равно увеличиваете размер изображения из-за того, как работает overlay fs.
DeusXMachina,
@DeusXMachina, какую версию докера вы используете? Документы противоречат тому, что вы говорите docs.docker.com/develop/develop-images/multistage-build/… . Слои не должны сохраняться от незавершенного этапа сборки.
cdosborn
@cdosburn - я наблюдал это 18.09. Я говорил в основном о первом примере, поэтапные сборки должны избегать этой проблемы. И я думаю, что каждый этап FROM теперь компактифицируется, но вы заставили меня задуматься над своим воспоминанием. Придется поэкспериментировать с некоторыми вещами.
DeusXMachina
@DeusXMachina, верно только второе решение уменьшает размер изображения.
cdosborn
это хороший обходной путь для моего случая. Я копирую cacheи, в зависимости от того, что это за кеш, выбираю, что делать в файлах сценария!
Пасхалис
1

Пробовали другие идеи, но ни одна не соответствовала нашим требованиям. Идея состоит в том, чтобы создать базовый образ nginx для дочерних статических веб-приложений. По соображениям безопасности, оптимизации и стандартизации базовый образ должен иметь возможность выполнять RUNкоманды в каталогах, добавленных дочерними образами. Базовое изображение не определяет, какие каталоги добавляются дочерними изображениями. Предполагается, что дочерние изображения будут COPYгде-то под ресурсами COMMON_DEST_ROOT.

Этот подход является взломом, но идея состоит в том, что базовый образ будет поддерживать COPYинструкции для каталогов от 1 до N, добавленных дочерним изображением. ARG PLACEHOLDER_FILEи ENV UNPROVIDED_DESTиспользуются для удовлетворения <src>и <dest>требования к любой COPYинструкции не требуется.

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

У этого решения есть очевидные недостатки, такие как фиктивное PLACEHOLDER_FILEи жестко запрограммированное количество поддерживаемых инструкций COPY. Также нет способа избавиться от переменных ENV, которые используются в инструкции COPY.

brianNotBob
источник