COPY / ADD có điều kiện trong Dockerfile?


103

Bên trong Dockerfiles của tôi, tôi muốn SAO CHÉP một tệp vào hình ảnh của mình nếu nó tồn tại, tệp tin request.txt cho pip có vẻ là một ứng cử viên tốt nhưng làm thế nào để đạt được điều này?

COPY (requirements.txt if test -e requirements.txt; fi) /destination
...
RUN  if test -e requirements.txt; then pip install -r requirements.txt; fi

hoặc là

if test -e requirements.txt; then
    COPY requiements.txt /destination;
fi
RUN  if test -e requirements.txt; then pip install -r requirements.txt; fi

Vui lòng xem tại đây: docs.docker.com/reference/builder
Tuan

4
@Tuấn - Điều gì cụ thể ở liên kết đó giúp làm điều này?
ToolmakerSteve

Câu trả lời:


24

Điều này hiện không được hỗ trợ (vì tôi nghi ngờ nó sẽ dẫn đến hình ảnh không thể tái tạo, vì cùng một Dockerfile sẽ sao chép hoặc không sao chép tệp, tùy thuộc vào sự tồn tại của nó).

Điều này vẫn được yêu cầu, trong số 13045 , sử dụng ký tự đại diện: " COPY foo/* bar/" not work if no file in foo" (tháng 5 năm 2015).
Nó sẽ không được triển khai ngay bây giờ (tháng 7 năm 2015) trong Docker, nhưng một công cụ xây dựng khác như bocker có thể hỗ trợ điều này.


32
câu trả lời tốt, nhưng logic của docker, IMO, bị thiếu sót. nếu bạn chạy cùng một tệp dockerfile với một ngữ cảnh xây dựng khác, bạn sẽ nhận được một hình ảnh khác. điều đó được mong đợi. sử dụng cùng một ngữ cảnh xây dựng sẽ cho cùng một hình ảnh. và nếu bạn chèn các hướng dẫn SAO CHÉP / THÊM có điều kiện trên cùng ngữ cảnh xây dựng, bạn sẽ nhận được cùng một hình ảnh. để kiểm tra. đó chỉ là 2 xu của tôi.
nathan g

Docker là về cơ sở hạ tầng bất biến. Môi trường dev, staging và prod của bạn phải giống nhau đến 99,99% nếu không muốn nói là giống hệt nhau. Sử dụng các biến môi trường.
AndrewMcLagan

3
@AndrewMcLagan, nếu chẳng hạn, devmôi trường front-end chạy với máy chủ webpack dev và prodmôi trường tương đương hoạt động với một /distthư mục tĩnh thì sao? Đây là trường hợp trong hầu hết các thiết lập front-end ngày nay, và hiển nhiên devprodkhông thể giống ở đây. Vậy làm thế nào để đối phó với điều đó?
Jivan

Tôi không sử dụng docker để phát triển giao diện người dùng của nút. Webpack localhost: 3000 vv ... Mặc dù vẫn khởi động môi trường nhà phát triển docker cục bộ của bạn để giao diện người dùng node / react / angle của bạn giao tiếp với bất kỳ thứ gì đang chạy trong môi trường vùng chứa docker thông thường của bạn. Ví dụ: API, redis, MySQL, mongo, tìm kiếm đàn hồi và bất kỳ dịch vụ vi mô nào khác. Bạn ..could .., chạy môi trường webpack dev trong vùng chứa. Nhưng tôi cảm thấy nó quá xa ...
AndrewMcLagan

@Jivan Làm thế nào về việc sử dụng một hình ảnh xây dựng để xác định các hướng dẫn chung và sau đó xây dựng hình ảnh cụ thể cho nhà phát triển và sản phẩm. Kho lưu trữ Docker Hub Node dường như chứa các hình ảnh xây dựng cho từng phiên bản Node: hub.docker.com/_/node . Hoặc có thể bạn có thể cuộn của riêng bạn.
david_i_smith

83

Đây là một giải pháp đơn giản:

COPY foo file-which-may-exist* /target

Đảm bảo footồn tại, vì COPYcần ít nhất một nguồn hợp lệ.

Nếu file-which-may-existcó, nó cũng sẽ được sao chép.

LƯU Ý: Bạn nên cẩn thận để đảm bảo rằng ký tự đại diện của bạn không chọn các tệp khác mà bạn không định sao chép. Để cẩn thận hơn, bạn có thể sử dụng file-which-may-exist?thay thế ( ?chỉ khớp với một ký tự duy nhất).

Hoặc thậm chí tốt hơn, sử dụng một lớp ký tự như thế này để đảm bảo rằng chỉ một tệp có thể được khớp:

COPY foo file-which-may-exis[t] /target

1
Bạn có thể làm điều tương tự với một thư mục?
Benjamin Toueg

1
@BenjaminToueg: Có, theo tài liệu, bạn có thể sao chép tệp cũng như thư mục.
jdhildeb

2
Điều này hoạt động tuyệt vời. Đối với các tệp có nhiều đích, tôi đã sao chép vào một thư mục tạm thời và sau đó di chuyển chúng đến nơi cần thiết. 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(nơi thư viện được chia sẻ là thực thể không xác định.)
Adam K Dean

Khi sao chép nhiều tệp còn tồn tại, đích đến phải là một thư mục. Điều này hoạt động như thế nào khi cả foo và tệp của bạn-mà-có-thể-tồn tại * đều tồn tại?
melchoir55

1
Vì vậy, câu trả lời là 'hãy chắc chắn rằng có một tệp' và sau đó là một cuộc trình diễn về cách sử dụng toán tử COPY? Tôi không hiểu điều này liên quan như thế nào đến câu hỏi ban đầu.
derrend 23/09/18

27

Như đã nêu trong nhận xét này , câu trả lời của Santhosh Hirekerur vẫn sao chép tệp, để lưu trữ bản sao có điều kiện thực sự, bạn có thể sử dụng phương pháp này.

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

Các ONBUILDhướng dẫn đảm bảo rằng tệp chỉ được sao chép nếu "nhánh" được chọn bởi BUILD_ENV. Đặt var này bằng cách sử dụng một tập lệnh nhỏ trước khi gọidocker build


2
Tôi thích câu trả lời này vì nó mở ra không chỉ cho tôi ONBUILD, siêu tiện dụng mà còn có vẻ dễ dàng nhất để tích hợp với các biến khác được chuyển vào, ví dụ: nếu bạn muốn đặt thẻ dựa trên BUILD_ENV hoặc lưu trữ một số trạng thái trong VIV.
DeusXMachina

Tôi vừa thử một cái gì đó như vậy và nhận được: Lỗi phản hồi từ daemon: Lỗi phân tích cú pháp Dockerfile dòng 52: tên không hợp lệ cho giai đoạn xây dựng: "site_builder _ $ {host_env}", tên không thể bắt đầu bằng số hoặc chứa ký hiệu
paulecoyote

9

Làm việc xung quanh Giải pháp

Tôi có yêu cầu sao chép FOLDER vào máy chủ dựa trên Biến ENV. Tôi đã lấy hình ảnh máy chủ trống. đã tạo cấu trúc thư mục triển khai cần thiết tại trong thư mục cục bộ. sau đó thêm dòng dưới đây vào DockerFile sao chép thư mục vào vùng chứa. Tôi n dòng cuối cùng đã thêm điểm vào để thực thi init file.sh trước khi docker khởi động máy chủ.

#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"]

Sau đó, tạo tệp custom-init.sh cục bộ với tập lệnh giống như bên dưới

#!/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;

Trong docker-soạn tệp bên dưới các dòng.

môi trường: - BUILD_EVN = TEST

Những thay đổi này sao chép thư mục vào vùng chứa trong quá trình xây dựng docker. khi chúng tôi thực thi docker-soạn nó sao chép hoặc triển khai thư mục yêu cầu thực tế đến máy chủ trước khi máy chủ khởi động.


8
Nhưng hình ảnh docker được phân lớp. Thanh sẽ sao chép các đến hình ảnh không phụ thuộc vào câu lệnh if mà bạn đề cập ...
MyUserInStackOverflow

@MyUserInStackOverflow - Tôi nghĩ ý tưởng của "giải pháp thay thế" này là cả install và install_test đều được sao chép vào hình ảnh, nhưng khi hình ảnh được chạy, chỉ một trong số các thư mục đó được sao chép vào vị trí cuối cùng. Nếu ổn cả hai đều ở đâu đó trong ảnh, thì đây có thể là một kỹ thuật hợp lý.
ToolmakerSteve

4

Sao chép tất cả các tệp vào một dir tiện lợi, tự tay chọn tệp bạn muốn, loại bỏ phần còn lại.

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

Bạn có thể đạt được điều gì đó tương tự bằng cách sử dụng các giai đoạn xây dựng, dựa trên cùng một giải pháp, sử dụng cpđể sao chép có điều kiện. Bằng cách sử dụng giai đoạn xây dựng, hình ảnh cuối cùng của bạn sẽ không bao gồm tất cả nội dung từ ban đầu 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

Mặc dù về mặt kỹ thuật điều này giải quyết được vấn đề nhưng nó không làm giảm kích thước của hình ảnh. Nếu bạn đang cố gắng sao chép có điều kiện thứ gì đó lớn (như mô hình mạng sâu), bạn vẫn tăng kích thước của hình ảnh, do cách hoạt động của lớp phủ fs.
DeusXMachina

@DeusXMachina, bạn đang sử dụng phiên bản docker nào? Tài liệu mâu thuẫn với những gì bạn đang nói docs.docker.com/develop/develop-images/multistage-build/… . Các lớp không nên được bảo quản từ giai đoạn xây dựng không phải là cuối cùng.
cdosborn

@cdosburn - Tôi đã quan sát thấy điều này vào ngày 18.09. Tôi đã nói chủ yếu về ví dụ đầu tiên, các bản dựng theo giai đoạn nên tránh vấn đề đó. Và tôi nghĩ rằng mọi giai đoạn FROM đều được tổng hợp lại ngay bây giờ, nhưng bạn có thể đoán được hồi ức của tôi lần thứ hai. Tôi sẽ phải thử nghiệm một số thứ.
DeusXMachina

@DeusXMachina, chỉ có giải pháp thứ hai giảm kích thước hình ảnh.
cdosborn

đó là cách giải quyết tốt cho trường hợp của tôi. Tôi sao chép một cachevà tùy thuộc vào nó, bộ nhớ cache tôi chọn làm gì trong các tệp script!
Paschalis

1

Đã thử các ý tưởng khác, nhưng không có ý tưởng nào đáp ứng yêu cầu của chúng tôi. Ý tưởng là tạo hình ảnh nginx cơ sở cho các ứng dụng web tĩnh con. Vì lý do bảo mật, tối ưu hóa và tiêu chuẩn hóa, hình ảnh cơ sở phải có khả năng ra RUNlệnh trên các thư mục được thêm bởi hình ảnh con. Hình ảnh cơ sở không kiểm soát thư mục nào được thêm vào bởi hình ảnh con. Giả định là hình ảnh con sẽ COPYtài nguyên ở đâu đó COMMON_DEST_ROOT.

Cách tiếp cận này là một cuộc tấn công, nhưng ý tưởng là hình ảnh cơ sở sẽ hỗ trợ COPYhướng dẫn cho 1 đến N thư mục được thêm bằng hình ảnh con. ARG PLACEHOLDER_FILEENV UNPROVIDED_DESTđược sử dụng để đáp ứng <src><dest>yêu cầu cho bất kỳ COPYhướng dẫn nào không cần thiết.

#
# 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

Giải pháp này có những thiếu sót rõ ràng như PLACEHOLDER_FILEsố lượng lệnh COPY giả và mã hóa cứng được hỗ trợ. Ngoài ra, không có cách nào để loại bỏ các biến ENV được sử dụng trong lệnh COPY.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.