Dockerfile 中的条件 COPY/ADD?
Conditional COPY/ADD in Dockerfile?
在我的 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
目前不支持此功能(因为我怀疑它会导致不可复制的图像,因为同一个 Docker 文件是否会复制该文件,具体取决于它的存在)。
在 issue 13045 中仍然要求使用通配符:“COPY foo/* bar/" not work if no file in foo
”(2015 年 5 月)。
目前(2015 年 7 月)它不会在 Docker 中实现,但另一个构建工具如 bocker 可以支持它。
变通解决方案
我要求根据 ENV 变量将文件夹复制到服务器。我拍摄了空的服务器图像。在本地文件夹中创建所需的部署文件夹结构。然后将下面的行添加到 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"]
然后使用如下脚本在本地创建 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 构建期间将文件夹复制到容器。当我们执行 docker-compose up 时,它会在服务器启动之前将实际需要的文件夹复制或部署到服务器。
这是一个简单的解决方法:
COPY foo file-which-may-exist* /target
确保 foo
存在,因为 COPY
至少需要一个有效来源。
如果 file-which-may-exist
存在,它也会被复制。
注意:
您应该注意确保您的通配符不会选择您不打算复制的其他文件。为了更加小心,您可以改用 file-which-may-exist?
(?
只匹配一个字符)。
或者更好的是,使用这样的字符class来确保只能匹配一个文件:
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
指令确保仅当 "branch" 被 BUILD_ENV
选择时才复制文件。在调用 docker build
之前使用一个小脚本设置这个 var
将所有文件复制到一个一次性目录,手动选择一个你想要的,丢弃其余的。
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
尝试了其他的想法,但是none满足了我们的要求。这个想法是为子静态 Web 应用程序创建基础 nginx 图像。出于安全、优化和标准化的原因,基础映像必须能够对子映像添加的目录执行 RUN
命令。基础镜像不控制子镜像添加哪些目录。假设子图像将 COPY
资源位于 COMMON_DEST_ROOT
.
下的某处
这种方法是 hack,但想法是基础映像将支持 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 指令数。也没有办法摆脱 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 中的复制语句。如果该文件存在,它只会创建一个空文件并且 docker 构建不会失败。如果已经有一个文件,它只会更改时间戳。
touch requirements.txt
和 Dockerfile
FROM python:3.9
COPY requirements.txt .
在我的 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
目前不支持此功能(因为我怀疑它会导致不可复制的图像,因为同一个 Docker 文件是否会复制该文件,具体取决于它的存在)。
在 issue 13045 中仍然要求使用通配符:“COPY foo/* bar/" not work if no file in foo
”(2015 年 5 月)。
目前(2015 年 7 月)它不会在 Docker 中实现,但另一个构建工具如 bocker 可以支持它。
变通解决方案
我要求根据 ENV 变量将文件夹复制到服务器。我拍摄了空的服务器图像。在本地文件夹中创建所需的部署文件夹结构。然后将下面的行添加到 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"]
然后使用如下脚本在本地创建 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 构建期间将文件夹复制到容器。当我们执行 docker-compose up 时,它会在服务器启动之前将实际需要的文件夹复制或部署到服务器。
这是一个简单的解决方法:
COPY foo file-which-may-exist* /target
确保 foo
存在,因为 COPY
至少需要一个有效来源。
如果 file-which-may-exist
存在,它也会被复制。
注意:
您应该注意确保您的通配符不会选择您不打算复制的其他文件。为了更加小心,您可以改用 file-which-may-exist?
(?
只匹配一个字符)。
或者更好的是,使用这样的字符class来确保只能匹配一个文件:
COPY foo file-which-may-exis[t] /target
如
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
指令确保仅当 "branch" 被 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
尝试了其他的想法,但是none满足了我们的要求。这个想法是为子静态 Web 应用程序创建基础 nginx 图像。出于安全、优化和标准化的原因,基础映像必须能够对子映像添加的目录执行 RUN
命令。基础镜像不控制子镜像添加哪些目录。假设子图像将 COPY
资源位于 COMMON_DEST_ROOT
.
这种方法是 hack,但想法是基础映像将支持 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 指令数。也没有办法摆脱 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 中的复制语句。如果该文件存在,它只会创建一个空文件并且 docker 构建不会失败。如果已经有一个文件,它只会更改时间戳。
touch requirements.txt
和 Dockerfile
FROM python:3.9
COPY requirements.txt .