Làm cách nào để bao gồm các tệp bên ngoài bối cảnh xây dựng của Docker?


462

Làm cách nào tôi có thể bao gồm các tệp từ bên ngoài bối cảnh xây dựng của Docker bằng lệnh "THÊM" trong tệp Docker?

Từ tài liệu Docker:

Đường dẫn phải nằm trong bối cảnh của bản dựng; bạn không thể THÊM ../s Something / s Something, vì bước đầu tiên của quá trình xây dựng docker là gửi thư mục ngữ cảnh (và thư mục con) đến trình nền của docker.

Tôi không muốn cơ cấu lại toàn bộ dự án của mình chỉ để phù hợp với Docker trong vấn đề này. Tôi muốn giữ tất cả các tệp Docker của mình trong cùng một thư mục con.

Ngoài ra, có vẻ như Docker chưa (và có thể chưa bao giờ) hỗ trợ các liên kết tượng trưng: Lệnh Dockerfile ADD không tuân theo các liên kết tượng trưng trên máy chủ # 1676.

Điều khác duy nhất tôi có thể nghĩ đến là bao gồm một bước dựng trước để sao chép các tệp vào bối cảnh xây dựng Docker (và định cấu hình kiểm soát phiên bản của tôi để bỏ qua các tệp đó). Có một cách giải quyết tốt hơn cho điều đó?


94
Đây phải là điều tồi tệ nhất về Docker. Theo quan điểm của tôi, không có thứ gọi là "dự án Docker". Docker là dành cho các dự án vận chuyển. Nó chỉ là một công cụ. Tôi không muốn phải xây dựng lại toàn bộ dự án của mình thành docker accomadte, thêm .dockerignore, v.v ... Vào cuối ngày, ai biết Docker sẽ kéo dài bao lâu? Sẽ thật tuyệt khi có một sự tách biệt giữa mã (tức là dự án góc) và bất kỳ phương tiện nào để triển khai nó (tức là docker). Rốt cuộc, thực sự không có lợi ích gì khi có một tập tin docker bên cạnh mọi thứ khác. Nó chỉ kết nối mọi thứ để tạo ra một hình ảnh :(
TigerBear

3
Vâng, đây là một downer lớn. Tôi đang đối mặt với cùng một vấn đề và tôi có một tệp nhị phân có kích thước lớn hơn (đã được nén) mà tôi không muốn sao chép vào mỗi bối cảnh xây dựng Docker. Tôi muốn lấy nó từ vị trí hiện tại của nó (bên ngoài bối cảnh xây dựng Docker). Và tôi không muốn ánh xạ một khối lượng trong thời gian chạy, bởi vì tôi đang cố gắng SAO CHÉP / THÊM tệp vào thời gian xây dựng và giải nén và làm những gì tôi cần để một số nhị phân nhất định được đưa vào hình ảnh. Cách này quay lên các container là nhanh chóng.
áo đậu

Tôi đã tìm thấy một cấu trúc tốt và tôi giải thích chi tiết tại stackoverflow.com/a/53298446/433814
Marcello de Sales

1
vấn đề với các bản dựng docker là khái niệm tạo thành "bối cảnh". Dockerfiles không đủ để xác định một bản dựng, trừ khi chúng được đặt trong một thư mục chiến lược (còn gọi là bối cảnh), tức là "/" là một cực trị, vì vậy bạn có thể truy cập bất kỳ đường dẫn nào (lưu ý rằng đó không phải là điều đúng trong dự án lành mạnh hoặc ..., cộng với nó làm cho docker xây dựng rất chậm vì docker quét toàn bộ bối cảnh khi bắt đầu). Bạn có thể xem xét việc xây dựng hình ảnh docker với tất cả các tệp cần thiết và sử dụng FROMđể tiếp tục từ đó. Tôi sẽ không thay đổi cấu trúc dự án để chứa Docker (hoặc bất kỳ công cụ xây dựng nào).
Devis L.

Câu trả lời:


411

Cách tốt nhất để giải quyết vấn đề này là chỉ định Dockerfile độc ​​lập với bối cảnh xây dựng, sử dụng -f.

Chẳng hạn, lệnh này sẽ cấp cho lệnh ADD quyền truy cập vào bất cứ thứ gì trong thư mục hiện tại của bạn.

docker build -f docker-files/Dockerfile .

Cập nhật : Docker hiện cho phép có Dockerfile bên ngoài bối cảnh xây dựng (được sửa trong 18.03.0-ce, https://github.com/docker/cli/pull/886 ). Vì vậy, bạn cũng có thể làm một cái gì đó như

docker build -f ../Dockerfile .

8
@Ro. bạn sử dụng thuộc dockerfile:tính trong build:phần trong tệp Soạn docs.docker.com/compose/compose-file/#/compose-file-reference
Emerson Farrugia

3
Tôi nhận được "Dockerfile phải nằm trong bối cảnh xây dựng" - Tôi thực sự muốn có một Dockerfile có thể nằm bên dưới bối cảnh xây dựng hiện tại. Trong ví dụ của bạn, bạn có Dockerfile trong / bên dưới bối cảnh xây dựng hiện tại, tất nhiên, hoạt động.
Alexander Mills

3
Vâng, tôi chỉ muốn một Dockerfile được chia sẻ tương ứng với nhiều thư mục con, tất cả đều là "bối cảnh xây dựng"
Alexander Mills

51
Điều này có giải quyết được vấn đề của OP khi muốn ADDmột tệp nằm ngoài thư mục ngữ cảnh không? Đó là những gì tôi đang cố gắng thực hiện nhưng tôi không nghĩ việc sử dụng -flàm cho các tệp bên ngoài có thể thêm được.
Sridhar Sarnobat

18
Không thể upvote này đủ .. trong docker-compose.yml tôi có : build: context: .., dockerfile: dir/Dockerfile. Bây giờ bối cảnh xây dựng của tôi là thư mục cha!
Mike Gl lý do jout Couturier

51

Tôi thường thấy mình sử dụng --build-argtùy chọn cho mục đích này. Ví dụ: sau khi đặt các mục sau vào Dockerfile:

ARG SSH_KEY
RUN echo "$SSH_KEY" > /root/.ssh/id_rsa

Bạn chỉ có thể làm:

docker build -t some-app --build-arg SSH_KEY="$(cat ~/file/outside/build/context/id_rsa)" .

Nhưng lưu ý cảnh báo sau từ tài liệu Docker :

Cảnh báo: Không nên sử dụng các biến thời gian xây dựng để truyền các bí mật như khóa github, thông tin người dùng, v.v. Giá trị biến thời gian xây dựng được hiển thị cho bất kỳ người dùng hình ảnh nào bằng lệnh lịch sử docker.


6
Đây là lời khuyên tồi mà không có một cảnh báo lớn. Từ tài liệu Docker: "Cảnh báo: Không nên sử dụng biến thời gian xây dựng để truyền bí mật như khóa github, thông tin người dùng, v.v. Giá trị biến thời gian xây dựng được hiển thị cho bất kỳ người dùng hình ảnh nào bằng lệnh lịch sử docker." [1] Nói cách khác, ví dụ đưa ra trong ví dụ này tiết lộ khóa SSH riêng tư trong hình ảnh docker. Trong một số bối cảnh, điều đó có thể tốt. docs.docker.com/engine/reference/builder/#arg
sheldonh

3
Cuối cùng, để khắc phục vấn đề bảo mật này, bạn có thể sử dụng các kỹ thuật như squash hoặc multistage-builds: vsupalov.com/build-docker-image-clone-private-repo-ssh-key
Jojo

46

Trên Linux, bạn có thể gắn các thư mục khác thay vì liên kết chúng

mount --bind olddir newdir

Xem /superuser/842642 để biết thêm chi tiết.

Tôi không biết nếu một cái gì đó tương tự có sẵn cho các hệ điều hành khác. Tôi cũng đã thử sử dụng Samba để chia sẻ một thư mục và đưa nó vào bối cảnh Docker cũng hoạt động.


2
Chỉ root mới có thể liên kết các thư mục
jjcf89

28

Tôi đã dành thời gian tốt để cố gắng tìm ra một mô hình tốt và cách giải thích rõ hơn những gì đang diễn ra với tính năng hỗ trợ này. Tôi nhận ra rằng cách tốt nhất để giải thích nó như sau ...

  • Dockerfile: Sẽ chỉ thấy các tệp trong đường dẫn tương đối của chính nó
  • Bối cảnh: một vị trí trong "không gian" nơi các tệp bạn muốn chia sẻ và Dockerfile của bạn sẽ được sao chép vào

Vì vậy, như đã nói, đây là một ví dụ về Dockerfile cần sử dụng lại một tệp có tên start.sh

Dockerfile

ALWAYSsẽ tải từ đường dẫn tương đối của nó, có thư mục hiện tại của chính nó làm localtham chiếu đến các đường dẫn bạn chỉ định.

COPY start.sh /runtime/start.sh

Các tập tin

Xem xét ý tưởng này, chúng ta có thể nghĩ đến việc có nhiều bản sao cho Dockerfiles xây dựng những thứ cụ thể, nhưng tất cả chúng đều cần quyền truy cập vào start.sh.

./all-services/
   /start.sh
   /service-X/Dockerfile
   /service-Y/Dockerfile
   /service-Z/Dockerfile
./docker-compose.yaml

Xem xét cấu trúc này và các tệp ở trên, đây là một docker-compose.yml

docker-compose.yaml

  • Trong ví dụ này, shareddir ngữ cảnh của bạn là runtimedir.
    • Cùng một mô hình tinh thần ở đây, hãy nghĩ rằng tất cả các tệp trong thư mục này được chuyển sang cái gọi là context.
    • Tương tự, chỉ cần xác định Dockerfile mà bạn muốn sao chép vào cùng một thư mục đó. Bạn có thể chỉ định rằng sử dụng dockerfile.
  • thư mục chứa nội dung chính của bạn là bối cảnh thực tế sẽ được đặt.

Các docker-compose.ymlthực hiện như sau

version: "3.3"
services:

  service-A
    build:
      context: ./all-service
      dockerfile: ./service-A/Dockerfile

  service-B
    build:
      context: ./all-service
      dockerfile: ./service-B/Dockerfile

  service-C
    build:
      context: ./all-service
      dockerfile: ./service-C/Dockerfile
  • all-serviceđược đặt làm bối cảnh, tệp chia sẻ start.shđược sao chép ở đó cũng như Dockerfile được chỉ định bởi mỗi tệp dockerfile.
  • Mỗi người được xây dựng theo cách riêng của họ, chia sẻ tệp bắt đầu!

Chúc mừng!


1
Quan điểm của bạn về Dockerfile không hoàn toàn đúng, như được chỉ ra bởi câu trả lời được chấp nhận, nếu bạn đang ở trong một hệ thống phân cấp thư mục a/b/c, thì có chạy docker build .trong csẽ không cho phép bạn truy cập ../file-in-b. Nhưng, tôi nghĩ rằng sự hiểu lầm chung trong điều này (hoặc ít nhất là của tôi) là bối cảnh được xác định bởi vị trí được nêu bởi đối số đầu tiên của lệnh xây dựng, chứ không phải bởi vị trí của Dockerfile. Vì vậy, như đã nêu trong câu trả lời được chấp nhận: từ a: docker build -f a/b/c/Dockerfile . có nghĩa là trong Dockerfile .hiện là thư mụca
β.εηablesιτ.

1
Trích dẫn từ tài liệu Dockerfile: đường dẫn của tệp và thư mục sẽ được hiểu là liên quan đến nguồn gốc của bối cảnh của bản dựng.
Nishant George Agrwal

18

Nếu bạn đọc các cuộc thảo luận trong vấn đề 2745, không chỉ docker có thể không bao giờ hỗ trợ các liên kết tượng trưng, ​​họ có thể không bao giờ hỗ trợ thêm các tệp bên ngoài ngữ cảnh của bạn. Dường như là một triết lý thiết kế rằng các tệp đi vào xây dựng docker rõ ràng phải là một phần trong bối cảnh của nó hoặc là từ một URL có thể được triển khai với phiên bản cố định để bản dựng có thể lặp lại với các URL hoặc tệp nổi tiếng được gửi cùng với docker container.

Tôi thích xây dựng từ một nguồn được kiểm soát phiên bản - tức là docker build -t Stuff http://my.git.org/repo - nếu không tôi đang xây dựng từ một nơi ngẫu nhiên với các tệp ngẫu nhiên.

về cơ bản, không .... - SvenDowideit, Docker Inc

Chỉ là ý kiến ​​của tôi nhưng tôi nghĩ bạn nên cơ cấu lại để tách ra các kho chứa mã và docker. Bằng cách đó, các thùng chứa có thể chung chung và kéo theo bất kỳ phiên bản mã nào trong thời gian chạy thay vì thời gian xây dựng.

Ngoài ra, sử dụng docker làm tạo tác triển khai mã cơ bản của bạn và sau đó bạn đặt dockerfile vào thư mục gốc của kho lưu trữ mã. nếu bạn đi tuyến đường này có thể có ý nghĩa để có một thùng chứa cha mẹ để biết thêm chi tiết cấp độ hệ thống chung và một thùng chứa con để thiết lập cụ thể cho mã của bạn.


Tại sao lại sử dụng docker?
lscoughlin

11

Tôi tin rằng cách giải quyết đơn giản hơn sẽ là thay đổi 'bối cảnh'.

Vì vậy, ví dụ, thay vì cho:

docker build -t hello-demo-app .

trong đó đặt thư mục hiện tại làm bối cảnh, giả sử bạn muốn thư mục mẹ làm bối cảnh, chỉ cần sử dụng:

docker build -t hello-demo-app ..

6
Tôi nghĩ rằng điều này phá vỡ .dockerignore: - \
NullVoxPopuli

Tôi đã từ bỏ .dockerignore và thay vào đó, tôi đã tạo thư mục docker được quản lý Makefile chỉ chứa các tệp cần thiết cho bối cảnh xây dựng ... Tôi chỉ cần gọi make buildvà nó sẽ lấy tất cả các tệp cần thiết nếu chúng được cập nhật và sau đó gọi là xây dựng docker phù hợp ... Tôi cần phải làm thêm, nhưng nó hoạt động hoàn hảo vì tôi hoàn toàn kiểm soát.
Sahsahae

5

Bạn cũng có thể tạo một tarball về những gì hình ảnh cần trước tiên và sử dụng nó làm bối cảnh của bạn.

https://docs.docker.com/engine/reference/commandline/build/#/tarball-contexts


Mẹo tuyệt vời! Tôi phát hiện ra bạn thậm chí có thể cung cấp docker xây dựng tarball như bối cảnh trên stdin : tar zc /dir1 /dir2 |docker build -. Điều này rất hữu ích trong trường hợp của tôi.
Xé Olsen

3

Sử dụng docker-compose, tôi đã thực hiện điều này bằng cách tạo ra một dịch vụ gắn kết các khối lượng mà tôi cần và cam kết hình ảnh của container. Sau đó, trong dịch vụ tiếp theo, tôi dựa vào hình ảnh đã cam kết trước đó, có tất cả dữ liệu được lưu trữ tại các vị trí được gắn. Sau đó, bạn sẽ phải sao chép các tệp này vào đích cuối cùng của chúng, vì các thư mục được gắn trên máy chủ không được cam kết khi chạy docker commitlệnh

Bạn không phải sử dụng docker-compose để thực hiện điều này, nhưng nó giúp cuộc sống dễ dàng hơn một chút

# docker-compose.yml

version: '3'
  services:
    stage:
      image: alpine
      volumes:
        - /host/machine/path:/tmp/container/path
      command: bash -c "cp -r /tmp/container/path /final/container/path"
    setup:
      image: stage
# setup.sh

# Start "stage" service
docker-compose up stage

# Commit changes to an image named "stage"
docker commit $(docker-compose ps -q stage) stage

# Start setup service off of stage image
docker-compose up setup

1

Tôi gặp vấn đề tương tự với một dự án và một số tệp dữ liệu mà tôi không thể di chuyển trong bối cảnh repo vì lý do HIPPA. Tôi đã kết thúc bằng cách sử dụng 2 Dockerfiles. Người ta xây dựng ứng dụng chính mà không có những thứ tôi cần bên ngoài container và xuất bản nó lên repo nội bộ. Sau đó, một dockerfile thứ hai kéo hình ảnh đó và thêm dữ liệu và tạo một hình ảnh mới sau đó được triển khai và không bao giờ được lưu trữ ở bất cứ đâu. Không lý tưởng, nhưng nó hoạt động cho mục đích của tôi là giữ thông tin nhạy cảm ra khỏi repo.


1

Một cách giải quyết dễ dàng có thể chỉ đơn giản là gắn âm lượng (sử dụng cờ -v hoặc --mount) vào thùng chứa khi bạn chạy nó và truy cập các tệp theo cách đó.

thí dụ:

docker run -v /path/to/file/on/host:/desired/path/to/file/in/container/ image_name

để biết thêm xem: https://docs.docker.com/st Storage / volume /


Lưu ý rằng điều này chỉ hoạt động nếu âm lượng là một phụ thuộc thời gian chạy. Đối với phụ thuộc thời gian xây dựng, docker runlà quá muộn.
dùng3735633

1

Như được mô tả trong này vấn đề GitHub xây dựng thực sự xảy ra trong /tmp/docker-12345, do đó, một đường dẫn tương đối như ../relative-add/some-filelà liên quan đến /tmp/docker-12345. Do đó, nó sẽ tìm kiếm /tmp/relative-add/some-file, cũng được hiển thị trong thông báo lỗi. *

Không được phép bao gồm các tệp từ bên ngoài thư mục bản dựng, do đó, điều này dẫn đến thông báo "Đường dẫn bị cấm". "


0

Một cách nhanh chóng và bẩn thỉu là thiết lập bối cảnh xây dựng lên bao nhiêu cấp độ bạn cần - nhưng điều này có thể gây ra hậu quả. Nếu bạn đang làm việc trong một kiến ​​trúc microservice trông như thế này:

./Code/Repo1
./Code/Repo2
...

Bạn có thể đặt bối cảnh xây dựng vào Codethư mục mẹ và sau đó truy cập mọi thứ, nhưng hóa ra với một số lượng lớn các kho lưu trữ, điều này có thể dẫn đến việc xây dựng mất nhiều thời gian.

Một tình huống ví dụ có thể là một nhóm khác duy trì một lược đồ cơ sở dữ liệu Repo1và mã nhóm của bạn Repo2phụ thuộc vào điều này. Bạn muốn cập nhật sự phụ thuộc này với một số dữ liệu hạt giống của riêng bạn mà không phải lo lắng về thay đổi lược đồ hoặc gây ô nhiễm kho lưu trữ của nhóm khác (tùy thuộc vào những thay đổi nào bạn vẫn có thể phải thay đổi tập lệnh dữ liệu hạt giống của mình) Cách tiếp cận thứ hai là hacky nhưng xoay quanh vấn đề xây dựng dài:

Tạo tập lệnh sh (hoặc ps1) ./Code/Repo2để sao chép các tệp bạn cần và gọi các lệnh docker mà bạn muốn, ví dụ:

#!/bin/bash
rm -r ./db/schema
mkdir ./db/schema

cp  -r ../Repo1/db/schema ./db/schema

docker-compose -f docker-compose.yml down
docker container prune -f
docker-compose -f docker-compose.yml up --build

Trong tệp soạn thảo docker , chỉ cần đặt bối cảnh là Repo2root và sử dụng nội dung của ./db/schemathư mục trong dockerfile của bạn mà không phải lo lắng về đường dẫn. Hãy nhớ rằng bạn sẽ có nguy cơ vô tình cam kết thư mục này vào kiểm soát nguồn, nhưng các hành động dọn dẹp kịch bản phải đủ dễ dàng.


0

Trong trường hợp của tôi, Dockerfile của tôi được viết giống như một mẫu chứa các phần giữ chỗ mà tôi sẽ thay thế bằng giá trị thực bằng tệp cấu hình của mình.

Vì vậy, tôi không thể chỉ định tệp này trực tiếp nhưng chuyển nó vào bản dựng docker như thế này:

sed "s/%email_address%/$EMAIL_ADDRESS/;" ./Dockerfile | docker build -t katzda/bookings:latest . -f -;

Nhưng vì đường ống, COPYlệnh không hoạt động. Nhưng cách trên giải quyết nó bằng cách -f -(nói rõ ràng tệp không được cung cấp). Chỉ thực hiện -mà không có -fcờ, bối cảnh VÀ Dockerfile không được cung cấp mà là một cảnh báo.


0

Mẹo nhỏ là nhận ra rằng bạn có thể chỉ định ngữ cảnh trong lệnh xây dựng để bao gồm các tệp từ thư mục mẹ nếu bạn chỉ định đường dẫn Docker, tôi sẽ thay đổi Dockerfile của mình thành như sau:

...
COPY ./ /dest/
...

Sau đó, lệnh xây dựng của tôi có thể trông như thế này:

docker built -t TAG -f DOCKER_FILE_PATH CONTEXT

Từ thư mục dự án

docker built -t username/project[:tag] -f ./docker/Dockerfile .

Từ dự án / bến tàu

docker built -t username/project[:tag] -f ./docker/Dockerfile ..
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.