COPY / ADD ตามเงื่อนไขใน Dockerfile?


105

ภายใน 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
Tuan

4
@Tuan - อะไรที่ช่วยทำสิ่งนี้โดยเฉพาะ
ToolmakerSteve

คำตอบ:


26

ขณะนี้ยังไม่รองรับ (เนื่องจากฉันสงสัยว่าอาจทำให้เกิดภาพที่ไม่สามารถทำซ้ำได้เนื่องจาก Dockerfile เดียวกันจะคัดลอกไฟล์หรือไม่ขึ้นอยู่กับการมีอยู่ของไฟล์)

สิ่งนี้ยังคงได้รับการร้องขอในฉบับที่ 13045โดยใช้สัญลักษณ์แทน: " COPY foo/* bar/" not work if no file in foo" (พฤษภาคม 2015)
ตอนนี้จะไม่สามารถใช้งานได้ (กรกฎาคม 2015) ใน Docker แต่เครื่องมือสร้างอื่นเช่นbockerสามารถรองรับสิ่งนี้ได้


35
คำตอบที่ดี แต่ตรรกะของนักเทียบท่า IMO มีข้อบกพร่อง หากคุณเรียกใช้ dockerfile เดียวกันกับ build-context ที่แตกต่างกันคุณจะได้รับรูปภาพที่แตกต่าง ที่คาดหวัง การใช้บริบทการสร้างเดียวกันจะให้ภาพเดียวกัน และหากคุณแทรกคำสั่ง COPY / ADD แบบมีเงื่อนไขใน build-context เดียวกันคุณจะได้ภาพเดียวกัน เพื่อที่จะเช็คเอาท์ นั่นเป็นเพียง 2 เซ็นต์ของฉัน
nathan g

Docker เป็นโครงสร้างพื้นฐานที่ไม่เปลี่ยนรูป สภาพแวดล้อม dev, staging และ prod ควรใกล้เคียงที่สุด 99.99% หากไม่เหมือนกัน ใช้ตัวแปรสภาพแวดล้อม
AndrewMcLagan

3
@AndrewMcLagan จะเกิดอะไรขึ้นหากdevสภาพแวดล้อมfront-end ทำงานกับเซิร์ฟเวอร์ webpack dev และprodสภาพแวดล้อมที่เทียบเท่ากันทำงานกับ/distโฟลเดอร์แบบคงที่? นี่เป็นกรณีในการตั้งค่าส่วนหน้าส่วนใหญ่ในปัจจุบันและเห็นได้ชัดว่าdevและprodไม่สามารถเหมือนกันได้ที่นี่ แล้วจะจัดการยังไงดี?
Jivan

ฉันไม่ได้ใช้นักเทียบท่าเพื่อพัฒนาส่วนหน้าโหนดของฉัน localhost ของ webpack ปกติ: 3000 ฯลฯ ... แม้ว่าจะยังคงบูตสภาพแวดล้อม docker dev ในเครื่องของคุณดังนั้นโหนด / react / angular front end ของคุณจะสื่อสารกับสิ่งที่ทำงานในสภาพแวดล้อมคอนเทนเนอร์นักเทียบท่าปกติของคุณ เช่น APIs, redis, MySQL, mongo, การค้นหาแบบยืดหยุ่นและบริการไมโครอื่น ๆ คุณ .. สามารถ .. เรียกใช้สภาพแวดล้อม Webpack dev ในคอนเทนเนอร์ แต่ฉันรู้สึกว่ามันไกลเกินไป ...
AndrewMcLagan

@Jivan วิธีการใช้อิมเมจ onbuild เพื่อกำหนดคำแนะนำทั่วไปจากนั้นสร้างอิมเมจเฉพาะสำหรับ dev และ prod หาง Hub โหนด repo ดูเหมือนจะมีภาพ onbuild สำหรับแต่ละรุ่นโหนด: hub.docker.com/_/node หรืออาจจะม้วนเองก็ได้
david_i_smith

86

นี่คือวิธีแก้ปัญหาง่ายๆ:

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
คุณสามารถทำสิ่งเดียวกันกับโฟลเดอร์ได้หรือไม่?
Benjamin Toueg

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(โดยที่ไลบรารีที่ใช้ร่วมกันเป็นหน่วยงานที่ไม่รู้จัก)
Adam K Dean

เมื่อคัดลอกไฟล์ที่ยังหลงเหลืออยู่หลายไฟล์ปลายทางต้องเป็นไดเร็กทอรี วิธีนี้จะทำงานอย่างไรเมื่อมีทั้ง foo และไฟล์ซึ่งอาจมีอยู่ * อยู่
melchoir55

1
ดังนั้นคำตอบคือ 'ตรวจสอบให้แน่ใจว่ามีไฟล์' แล้วสาธิตวิธีใช้ตัวดำเนินการ COPY? ฉันไม่เห็นว่าสิ่งนี้เกี่ยวข้องกับคำถามเดิมอย่างไร
derrend

29

ตามที่ระบุไว้ในความคิดเห็นนี้คำตอบของ 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

ฉันเพิ่งลองทำแบบนั้นและได้รับ: Error response from daemon: Dockerfile parse error line 52: invalid name for build stage: "site_builder _ $ {host_env}" ชื่อไม่สามารถขึ้นต้นด้วยตัวเลขหรือมีสัญลักษณ์
paulecoyote

10

หลีกเลี่ยงโซลูชัน

ฉันมีข้อกำหนดเกี่ยวกับการคัดลอก FOLDER ไปยังเซิร์ฟเวอร์ตามตัวแปร ENV ฉันเอาภาพเซิร์ฟเวอร์ที่ว่างเปล่า สร้างโครงสร้างโฟลเดอร์การปรับใช้ที่จำเป็นในโฟลเดอร์ภายในเครื่อง จากนั้นเพิ่มบรรทัดด้านล่างลงในDockerFileคัดลอกโฟลเดอร์ไปยังคอนเทนเนอร์ ฉันn บรรทัดสุดท้ายเพิ่มจุดเข้าเพื่อเรียกใช้งาน 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 = TEST

การเปลี่ยนแปลงเหล่านี้คัดลอกโฟลเดอร์ไปยังคอนเทนเนอร์ระหว่างการสร้างนักเทียบท่า เมื่อเราเรียกใช้งานDocker-compose ให้คัดลอกหรือปรับใช้โฟลเดอร์ที่จำเป็นจริงไปยังเซิร์ฟเวอร์ก่อนที่เซิร์ฟเวอร์จะเริ่มทำงาน


8
แต่ภาพนักเทียบท่าจะเป็นเลเยอร์ ADD จะคัดลอกสิ่งเหล่านี้ไปยังรูปภาพโดยไม่คำนึงถึงคำสั่ง if ที่คุณกล่าวถึง ...
MyUserInStackOverflow

@MyUserInStackOverflow - ฉันคิดว่าแนวคิดของ "วิธีแก้ปัญหา" นี้คือทั้ง install และ install_test จะถูกคัดลอกลงในอิมเมจ แต่เมื่อเรียกใช้รูปภาพโฟลเดอร์เหล่านั้นเพียงโฟลเดอร์เดียวเท่านั้นที่จะถูกคัดลอกไปยังตำแหน่งสุดท้าย หากเป็นเรื่องปกติที่ทั้งคู่อยู่ที่ไหนสักแห่งในภาพนี่อาจเป็นเทคนิคที่สมเหตุสมผล
ToolmakerSteve

5

คัดลอกไฟล์ทั้งหมดไปยัง dir ที่ถูกทิ้งเลือกไฟล์ที่คุณต้องการทิ้งส่วนที่เหลือ

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

แม้ว่าวิธีนี้จะช่วยแก้ปัญหาได้ในทางเทคนิค แต่ก็ไม่ได้ลดขนาดของภาพลง หากคุณพยายามคัดลอกสิ่งที่มีขนาดใหญ่ตามเงื่อนไข (เช่นโมเดลเครือข่ายแบบลึก) คุณยังคงขยายขนาดของรูปภาพเนื่องจากวิธีการทำงานของโอเวอร์เลย์
DeusXMachina

@DeusXMachina คุณใช้นักเทียบท่ารุ่นอะไร เอกสารขัดแย้งกับสิ่งที่คุณกำลังจะบอกว่าdocs.docker.com/develop/develop-images/multistage-build/... ไม่ควรเก็บรักษาเลเยอร์จากขั้นตอนการสร้างที่ไม่ใช่ขั้นสุดท้าย
cdosborn

@cdosburn - ฉันได้สังเกตสิ่งนี้เมื่อวันที่ 18.09 น. ฉันกำลังพูดถึงตัวอย่างแรกเป็นส่วนใหญ่การสร้างฉากควรหลีกเลี่ยงปัญหานั้น และฉันคิดว่าทุกขั้นตอนจาก FROM กระชับตอนนี้ แต่คุณให้ฉันเดาความทรงจำของฉันเป็นครั้งที่สอง ฉันจะต้องทดลองกับบางสิ่ง
DeusXMachina

@DeusXMachina เพียงวิธีที่สองเท่านั้นที่จะลดขนาดภาพ
cdosborn

นั่นเป็นวิธีแก้ปัญหาที่ดีสำหรับกรณีของฉัน ฉันคัดลอกcacheและขึ้นอยู่กับแคชที่ฉันเลือกว่าจะทำอะไรในไฟล์สคริปต์!
Paschalis

1

ลองใช้แนวคิดอื่น ๆ แต่ไม่มีสิ่งใดตรงตามความต้องการของเรา แนวคิดคือการสร้างอิมเมจ nginx พื้นฐานสำหรับแอปพลิเคชันเว็บแบบคงที่สำหรับเด็ก ด้วยเหตุผลด้านความปลอดภัยการปรับให้เหมาะสมและมาตรฐานอิมเมจพื้นฐานต้องสามารถRUNสั่งการบนไดเร็กทอรีที่เพิ่มโดยอิมเมจลูก อิมเมจพื้นฐานไม่ได้ควบคุมว่าจะเพิ่มไดเร็กทอรีใดโดยอิมเมจย่อย อัสสัมชัเป็นภาพเด็กจะทรัพยากรที่ไหนสักแห่งภายใต้COPYCOMMON_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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.