Dockerfile 是否有条件复制/增加副本?

在我的 Dockerfiles 中,如果存在一个文件,我希望将它复制到我的映像中,pip 的 Requments.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
144607 次浏览

目前还不支持这种做法(因为我怀疑它会导致一个不可重现的图像,因为相同的 Dockerfile 会复制或不复制文件,这取决于它的存在)。

第13045期中,仍然需要使用通配符: “ COPY foo/* bar/" not work if no file in foo”(2015年5月)。
目前(2015年7月)还不能在 Docker 中实现,但是像 译自: 美国《每日邮报》网站(https://github.com/ice/bocker)这样的构建工具可以支持这一点。


2021年

COPY source/. /source/对我来说是可以工作的(例如,当目录为空或不为空时复制目录,如“ 在“ COPY failed: no source files were specified”上将目录复制到 docker build 中,无论是空的还是非空的”)

二零二二年

我的建议是:

# syntax=docker/dockerfile:1.2


RUN --mount=type=bind,source=jars,target=/build/jars \
find /build/jars -type f -name '*.jar' -maxdepth 1  -print0 \
| xargs -0 --no-run-if-empty --replace=source cp --force source >"${INSTALL_PATH}/modules/"

这种方法可行:

COPY jars/*.jar "${INSTALL_PATH}/modules/"

但是,如果没有找到 *.jar,则不复制 *.jar,而不抛出错误。

解决方案

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

然后使用脚本在本地创建 自定义 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 = 测试

这些更改会在 Docker 生成期间将文件夹复制到容器。当我们执行 码头工人,冷静点时,它会在服务器启动之前将实际需要的文件夹复制或部署到服务器。

这里有一个简单的解决办法:

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

此评论所述,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之前使用一个小脚本设置这个变量

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

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

我们试了其他的办法,但没有一个符合我们的要求。这个想法是为子静态 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 变量。

我想我想出了一个有效的解决方案与这个 Dockerfile

FROM alpine
COPy always_exist_on_host.txt .
COPY *sometimes_exist_on_host.txt .

always_exist_on_host.txt文件将始终被复制到映像中,当 sometimes_exist_on_host.txt文件不存在时,构建不会失败。此外,当 sometimes_exist_on_host.txt文件确实存在时,它将复制该文件。

例如:

.
├── Dockerfile
└── always_exist_on_host.txt

构建成功

docker build . -t copy-when-exists --no-cache
[+] Building 1.0s (7/7) FINISHED
=> [internal] load .dockerignore                                                                                                                      0.0s
=> => transferring context: 2B                                                                                                                        0.0s
=> [internal] load build definition from Dockerfile                                                                                                   0.0s
=> => transferring dockerfile: 36B                                                                                                                    0.0s
=> [internal] load metadata for docker.io/library/alpine:latest                                                                                       1.0s
=> [internal] load build context                                                                                                                      0.0s
=> => transferring context: 43B                                                                                                                       0.0s
=> CACHED [1/2] FROM docker.io/library/alpine@sha256:c0e9560cda118f9ec63ddefb4a173a2b2a0347082d7dff7dc14272e7841a5b5a                                 0.0s
=> [2/2] COPY always_exist_on_host.txt *sometimes_exist_on_host.txt .                                                                                 0.0s
=> exporting to image                                                                                                                                 0.0s
=> => exporting layers                                                                                                                                0.0s
=> => writing image sha256:e7d02c6d977f43500dbc1c99d31e0a0100bb2a6e5301d8cd46a19390368f4899                                                           0.0s

.
├── Dockerfile
├── always_exist_on_host.txt
└── sometimes_exist_on_host.txt

构建仍然成功

docker build . -t copy-when-exists --no-cache
[+] Building 1.0s (7/7) FINISHED
=> [internal] load build definition from Dockerfile                                                                                                   0.0s
=> => transferring dockerfile: 36B                                                                                                                    0.0s
=> [internal] load .dockerignore                                                                                                                      0.0s
=> => transferring context: 2B                                                                                                                        0.0s
=> [internal] load metadata for docker.io/library/alpine:latest                                                                                       0.9s
=> [internal] load build context                                                                                                                      0.0s
=> => transferring context: 91B                                                                                                                       0.0s
=> CACHED [1/2] FROM docker.io/library/alpine@sha256:c0e9560cda118f9ec63ddefb4a173a2b2a0347082d7dff7dc14272e7841a5b5a                                 0.0s
=> [2/2] COPY always_exist_on_host.txt *sometimes_exist_on_host.txt .                                                                                 0.0s
=> exporting to image                                                                                                                                 0.0s
=> => exporting layers                                                                                                                                0.0s
=> => writing image sha256:4c88e2ffa77ebf6869af3c7ca2a0cfb9461979461fc3ae133709080b5abee8ff                                                           0.0s
=> => naming to docker.io/library/copy-when-exists                                                                                                    0.0s

我还有其他解决办法。其思想是触摸构建上下文中的文件,并在 Dockerfile 中使用 copy 语句。如果该文件存在,它只会创建一个空文件,并且 docker 构建不会失败。如果已经有一个文件,它只会更改时间戳。

touch requirements.txt

还有 Dockerfile

FROM python:3.9
COPY requirements.txt .