Dockerfile中的条件COPY / ADD?


103

在我的Dockerfile文件中,我想将文件复制到映像中(如果存在),pip的requirements.txt文件似乎是一个不错的选择,但是如何实现呢?

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
Tuan

4
@Tuan-该链接的具体作用是什么?
ToolmakerSteve

Answers:


24

目前尚不支持此功能(因为我怀疑它会导致图像无法复制,因为相同的Dockerfile会根据文件的存在而复制或不复制该文件)。

问题13045中,仍要求使用通配符“ COPY foo/* bar/" not work if no file in foo”(2015年5月)。
暂时(2015年7月)不会在Docker中实现,但其他类似bocker的构建工具可以支持此功能。


32
很好的答案,但是docker逻辑IMO有缺陷。如果您使用不同的构建上下文运行相同的dockerfile,则将获得不同的映像。这是意料之中的。使用相同的构建上下文将给出相同的图像。并且如果在相同的构建上下文中插入有条件的COPY / ADD指令,您将获得相同的图像。以便结帐。那不过是我的2美分。
森·克

Docker是关于不变的基础架构的。如果环境不完全相同,则环境dev,staging和prod应该尽可能接近99.99%。使用环境变量。
AndrewMcLagan

3
@AndrewMcLagan例如,如果前端dev环境与Webpack开发服务器一起运行,而等效prod环境与/dist静态文件夹一起工作,该怎么办?当今大多数前端设置都是这种情况,显然dev并且prod在这里不能相同。那么如何处理呢?
吉万

我不使用docker开发我的节点前端。普通的webpack本地主机:3000等...尽管仍然引导您的本地docker开发环境,所以您的节点/反应/角度前端可以与普通docker容器环境中运行的任何内容进行通信。例如API,redis,MySQL,mongo,弹性搜索和任何其他微服务。您可以在容器中运行Webpack开发环境。但是我觉得这太过分了……
AndrewMcLagan

@Jivan如何使用onbuild映像定义通用说明,然后为dev和prod构建特定的映像。Docker Hub节点存储库似乎包含每个Node版本的onbuild映像:hub.docker.com/_/node。也许您可以自己动手。
david_i_smith

83

这是一个简单的解决方法:

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 2007年

1
因此,答案是“确保有文件”,然后是关于如何使用COPY运算符的演示?我看不出这与原始问题有何关系。
18:31推迟

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
我喜欢这个答案,因为它不仅让我大开眼界,这非常方便,而且似乎也很容易与传入的其他变量集成,例如,如果要基于BUILD_ENV设置标签,或在其中存储某些状态ENV。
DeusXMachina

我刚刚尝试过类似的操作,并得到:来自守护程序的错误响应:Dockerfile解析错误行52:构建阶段的名称无效:“ site_builder _ $ {host_env}”,名称不能以数字开头或包含符号
paulecoyote

9

解决方案

我对基于ENV变量将FOLDER复制到服务器有要求。我拍摄了空的服务器映像。在本地文件夹中创建了所需的部署文件夹结构。然后将以下行添加到DockerFile中,将文件夹复制到容器中。在最后一行中,添加了入口点以在docker启动服务器之前执行initfile.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 = TEST

这些更改在Docker构建期间将文件夹复制到容器。当我们执行docker-compose up时 ,在服务器启动之前将实际所需的文件夹复制或部署到服务器。


8
但是,码头工人图像是分层的。无论您提到的if语句如何,ADD都会将这些内容复制到映像中……
MyUserInStackOverflow

@MyUserInStackOverflow-我认为此“替代方法”的想法是将install和install_test都复制到映像中,但是在运行映像时,仅将那些文件夹之一复制到最终位置。如果可以将两者都放置在图像中,那么这可能是一种合理的技术。
ToolmakerSteve

4

将所有文件复制到一次性目录,手动选择所需的目录,然后丢弃其余的目录。

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

您可以使用依赖于相同解决方案的构建阶段(cp有条件地进行复制)来实现类似目的。通过使用构建阶段,最终图像将不包括initial中的所有内容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,您使用的是什么版本的Docker?这些文档与您所说的docs.docker.com/develop/develop-images/multistage-build/…相矛盾。不应在非最终构建阶段保留这些层。
cdosborn

@cdosburn-我在18.09观察到了这一点。我主要是在谈论第一个示例,分阶段构建应该避免该问题。而且我认为每个FROM阶段现在都在压缩,但是让我第二次猜测我的回忆。我将不得不尝试一些事情。
DeusXMachina

@DeusXMachina,只有第二种解决方案可以减小图像尺寸。
cdosborn

这是我的情况下很好的解决方法。我复制一个,cache然后根据它的缓存内容选择要在脚本文件中执行的操作!
Paschalis

1

尝试了其他想法,但没有一个满足我们的要求。这个想法是为子静态Web应用程序创建基本的nginx图像。出于安全性,优化和标准化方面的原因,基本映像必须能够RUN在由子映像添加的目录上执行命令。基本映像不控制子映像添加哪些目录。假设子图像将COPY在下方的某处提供资源COMMON_DEST_ROOT

这种方法是一种技巧,但是基本映像将支持COPY子映像添加的1到N个目录的指令。 ARG PLACEHOLDER_FILEENV 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指令的硬编码数量。同样也没有办法摆脱在COPY指令中使用的ENV变量。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.