Умовне копіювання / додавання в Dockerfile?


103

Усередині моїх файлів Docker я хотів би КОПІЮВАТИ файл у своє зображення, якщо він існує, файл 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, може це підтримати.


32
хороша відповідь, але логіка докера, IMO, є хибною. якщо запустити один і той же dockerfile з іншим контекстом збірки, ви отримаєте інше зображення. цього варто очікувати. використання одного і того ж контексту побудови дасть однакове зображення. і якщо вставити умовні інструкції КОПІЯ / ДОБАВКА в той самий контекст побудови, ви отримаєте те саме зображення. щоб перевірити. це лише мої 2 копійки, хоча.
nathan g

Докер - це про непорушну інфраструктуру. У вашому середовищі розробники, постановки та продажі повинні бути на 99,99% максимально близькими, якщо не однаковими. Використовуйте змінні середовища.
AndrewMcLagan

3
@AndrewMcLagan, що, якщо, наприклад, devзовнішнє середовище працює із сервером розробників webpack, а еквівалентне prodсередовище працює зі /distстатичною папкою? Це стосується більшості налаштувань на передовій сьогодні, і очевидно, devі prodтут не може бути однаково. То як з цим боротися?
Jivan

Я не використовую докер для розробки передніх кінців вузла. Нормальний web-пакет localhost: 3000 і т.д. ... Хоча все ще завантажуєте локальне середовище розробника docker, тому ваш вузол / реакція / кутовий передній кінець повідомляє про все, що працює у вашому звичайному середовищі контейнера докера. Наприклад, API, Redis, MySQL, Монго, еластичний пошук та будь-який інший мікро-сервіс. Ви ..могли б .. запустити середовище розробників webpack в контейнер. Але я відчуваю, що це занадто далеко ...
AndrewMcLagan

@Jivan Як щодо використання зображення на побудові для визначення загальних інструкцій, а потім побудови конкретних зображень для розробників та продуктів. Здається, репо-вузол Docker Hub Node містить вбудовані зображення для кожної версії вузла: 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

1
Чи можете ви зробити те ж саме з папкою?
Бенджамін Туег

1
@BenjaminToueg: Так, відповідно до документів ви можете копіювати файли, а також папки.
jdhildeb

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? Я не бачу, як це стосується оригінального питання.
деррен

27

Як зазначено в цьому коментарі , відповідь Сантхоша Хікекерура все-таки копіює файл, щоб заархівувати справжню умовну копію, ви можете використовувати цей метод.

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. Встановіть цей var за допомогою невеликого сценарію перед викликом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;

У докер-композиті файлі нижче рядків.

оточення: - BUILD_EVN = TEST

Ці зміни копіюють папку в контейнер під час збирання докера. коли ми виконуємо docker-компонуємо його, копіюємо або розгортаємо на сервері фактично необхідну папку перед запуском сервера.


8
Але зображення докера шаруються. ADD скопіює їх на зображення незалежно від оператора if, про який ви згадали ...
MyUserInStackOverflow

@MyUserInStackOverflow - я думаю, що ідея цього "вирішення" полягає в тому, що і інсталяція, і install_test копіюються в зображення, але коли зображення запущено, лише одна з цих папок копіюється в остаточне місце. Якщо добре, що обидва є десь на зображенні, це може бути розумною технікою.
ToolmakerSteve

4

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

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

Хоча це технічно вирішує проблему, це не зменшує розмір зображення. Якщо ви намагаєтесь умовно скопіювати щось величезне (наприклад, модель глибокої мережі), ви все одно збільшуєте розмір зображення, завдяки тому, як працює накладання fs.
DeusXMachina

@DeusXMachina, яку версію докера ти використовуєш? Документи суперечать тому, що ви говорите docs.docker.com/develop/develop-images/multistage-build/… . Шари не повинні зберігатися на етапі завершення нарощування.
cdosborn

@cdosburn - я спостерігав це 18.09. Я говорив переважно про перший приклад, поетапні побудови повинні уникати цього питання. І я думаю, що кожен етап зі сцени ущільнюється зараз, але ти вдруге здогадаєшся про мій спогад. Мені доведеться експериментувати з деякими речами.
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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.