Jak dołączyć pliki poza kontekstem kompilacji Dockera?

461

Jak mogę dołączyć pliki spoza kontekstu kompilacji Dockera za pomocą polecenia „DODAJ” w pliku Dockera?

Z dokumentacji dokera:

Ścieżka musi znajdować się w kontekście kompilacji; nie można DODAĆ ../something/something, ponieważ pierwszym krokiem kompilacji dokera jest wysłanie katalogu kontekstowego (i podkatalogów) do demona dokera.

Nie chcę zrestrukturyzować całego mojego projektu tylko po to, by uwzględnić Dockera w tej sprawie. Chcę przechowywać wszystkie moje pliki Docker w tym samym podkatalogu.

Wygląda również na to, że Docker jeszcze nie obsługuje (i może nigdy nie obsługuje) dowiązań symbolicznych: polecenie Dockerfile ADD nie podąża za dowiązaniami symbolicznymi na hoście # 1676.

Jedyną rzeczą, o której mogę pomyśleć, jest krok poprzedzający kompilację, aby skopiować pliki do kontekstu kompilacji Dockera (i skonfigurować kontrolę wersji, aby ignorowała te pliki). Czy istnieje lepsze obejście tego problemu?

ben_frankly
źródło
94
To musi być najgorsza rzecz w Docker. Z mojego punktu widzenia nie ma czegoś takiego jak „projekt Docker”. Docker jest przeznaczony do projektów wysyłkowych. To tylko narzędzie. Nie chcę odbudowywać całego projektu, aby dostosować dokera, dodając .dockerignore itp. Na koniec, kto wie, jak długo potrwa Docker? Byłoby wspaniale mieć separację między kodem (tj. Projektem kątowym) a wszelkimi środkami do jego wdrożenia (tj. Dokerem). W końcu naprawdę nie ma żadnej korzyści z posiadania pliku dokującego obok wszystkiego innego. Po prostu wszystko łączy w celu stworzenia obrazu :(
TigerBear
3
Tak, to jest duży minus. Mam do czynienia z tym samym problemem i mam większy plik binarny (już skompresowany), którego nie chcę kopiować do każdego kontekstu kompilacji Dockera. Wolę źródła z jego bieżącej lokalizacji (poza kontekstem kompilacji Docker). I nie chcę mapować woluminu w czasie wykonywania, ponieważ próbuję KOPIOWAĆ / DODAĆ plik w czasie kompilacji i rozpakować i zrobić to, czego potrzebuję, aby niektóre pliki binarne zostały zapisane w obrazie. W ten sposób rozpędzenie pojemników jest szybkie.
dżersejowa fasola
Znalazłem dobrą strukturę i wyjaśniam ze szczegółami na stackoverflow.com/a/53298446/433814
Marcello de Sales
1
problemem z kompilacjami dokerów jest wymyślona koncepcja „kontekstu”. Pliki dokerów nie są wystarczające do zdefiniowania kompilacji, chyba że zostaną umieszczone w katalogu strategicznym (inaczej kontekst), tj. „/” Jako skrajność, aby można było uzyskać dostęp do dowolnej ścieżki (zwróć uwagę, że nie jest to właściwe w rozsądnym projekcie albo ..., a dodatkowo powoduje, że kompilacja dokera jest bardzo wolna, ponieważ docker skanuje cały kontekst na początku). Możesz rozważyć zbudowanie obrazu dokera ze wszystkimi wymaganymi plikami i FROMkontynuowanie od tego momentu . Nie zmieniłbym struktury projektu w celu dostosowania do Dockera (ani żadnych narzędzi do budowania).
Devis L.

Odpowiedzi:

412

Najlepszym sposobem obejścia tego problemu jest określenie pliku Docker niezależnie od kontekstu kompilacji za pomocą opcji -f.

Na przykład to polecenie zapewni dostęp do polecenia ADD do dowolnego elementu w bieżącym katalogu.

docker build -f docker-files/Dockerfile .

Aktualizacja : Docker pozwala teraz mieć plik Docker poza kontekstem kompilacji (naprawiony w 18.03.0-ce, https://github.com/docker/cli/pull/886 ). Możesz także zrobić coś takiego

docker build -f ../Dockerfile .
Cyberwiz
źródło
8
@Ro. korzystasz z tej dockerfile:właściwości w build:sekcji w pliku zredaguj docs.docker.com/compose/compose-file/#/compose-file-reference
Emerson Farrugia
3
Pojawia się komunikat „Plik Docker musi znajdować się w kontekście kompilacji” - naprawdę chciałbym mieć jeden plik Docker, który może znajdować się poniżej bieżącego kontekstu kompilacji. W twoim przykładzie masz plik Docker w / poniżej bieżącego kontekstu kompilacji, co oczywiście działa.
Alexander Mills,
3
Tak, chcę tylko udostępniony plik Docker, który odpowiada wielu podkatalogom, z których wszystkie są „kontekstami kompilacji”
Alexander Mills,
50
Czy to rozwiązuje problem polegający na tym, że OP chce ADDmieć plik spoza katalogu kontekstowego? To właśnie próbuję zrobić, ale nie sądzę, aby korzystanie z -fzewnętrznych plików było możliwe.
Sridhar Sarnobat,
18
Nie można upvote to tyle .. w moim dokowanym-compose.yml mam: build: context: .., dockerfile: dir/Dockerfile. Teraz moim kontekstem kompilacji jest katalog nadrzędny!
Mike Gleason jr Couturier,
51

Często używam tej --build-argopcji do tego celu. Na przykład po umieszczeniu następującego pliku w Dockerfile:

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

Możesz po prostu zrobić:

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

Zwróć jednak uwagę na następujące ostrzeżenie z dokumentacji Docker :

Ostrzeżenie: Nie zaleca się używania zmiennych czasu kompilacji do przekazywania sekretów, takich jak klucze github, poświadczenia użytkownika itp. Wartości zmiennych kompilacji są widoczne dla każdego użytkownika obrazu za pomocą polecenia historii dokera.

użytkownik2658308
źródło
6
To kiepska rada bez wielkiego ostrzeżenia. Z dokumentacji Dockera: „Ostrzeżenie: Nie zaleca się używania zmiennych czasu kompilacji do przekazywania sekretów, takich jak klucze github, poświadczenia użytkownika itp. Wartości zmiennych kompilacji są widoczne dla każdego użytkownika obrazu za pomocą polecenia historii dokera”. [1] Innymi słowy, przykład podany w tym przykładzie ujawnia prywatny klucz SSH na obrazie dokera. W niektórych kontekstach może to być w porządku. docs.docker.com/engine/reference/builder/#arg
sheldonh
3
Wreszcie, aby rozwiązać ten problem bezpieczeństwa, możesz użyć technik takich jak squash lub kompilacje wielostopniowe: vsupalov.com/build-docker-image-clone-private-repo-ssh-key
Jojo
46

W systemie Linux możesz montować inne katalogi zamiast ich symbolicznie

mount --bind olddir newdir

Aby uzyskać więcej informacji, zobacz /superuser/842642

Nie wiem, czy coś podobnego jest dostępne dla innych systemów operacyjnych. Próbowałem też przy użyciu Samby udostępnić folder i ponownie zainstalować go w kontekście Dockera, który również działał.

Günter Zöchbauer
źródło
2
Tylko root może powiązać katalogi
jjcf89,
28

Spędziłem dobry czas, próbując znaleźć dobry wzór i jak lepiej wyjaśnić, co się dzieje z tą obsługą funkcji. Uświadomiłem sobie, że najlepszym sposobem na wyjaśnienie tego jest ...

  • Plik Docker: będzie widział pliki tylko pod własną ścieżką względną
  • Kontekst: miejsce w „przestrzeni”, w którym pliki, które chcesz udostępnić i plik Docker zostaną skopiowane

Tak więc powiedziawszy, oto przykład Dockerfile, który musi ponownie użyć pliku o nazwie start.sh

Plik Docker

To ALWAYSbędzie ładować z jego względnej ścieżki, posiadające aktualną dir samego siebie jako localodniesienie do ścieżek można określić.

COPY start.sh /runtime/start.sh

Akta

Biorąc pod uwagę ten pomysł, możemy pomyśleć o posiadaniu wielu kopii pliku Docker, który tworzy określone rzeczy, ale wszystkie one potrzebują dostępu do start.sh.

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

Biorąc pod uwagę tę strukturę i powyższe pliki, oto docker-compose.yml

docker-compose.yaml

  • W tym przykładzie katalogiem sharedkontekstowym jest runtimekatalog.
    • Ten sam model mentalny tutaj, pomyśl, że wszystkie pliki w tym katalogu są przeniesione do tzw context.
    • Podobnie, po prostu określ plik Docker, który chcesz skopiować do tego samego katalogu. Możesz to określić za pomocą dockerfile.
  • katalog, w którym znajduje się główna zawartość, to kontekst, który należy ustawić.

docker-compose.ymlJest następujący

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-servicejest ustawiony jako kontekst, plik współdzielony start.shjest tam kopiowany, a także plik Docker określony przez każdy z nich dockerfile.
  • Każdy zostanie zbudowany na swój własny sposób, udostępniając plik startowy!

Twoje zdrowie!

Marcello de Sales
źródło
1
Punkt na Dockerfile nie jest całkowicie prawdziwe, jak wskazano przez przyjętą odpowiedź, jeśli jesteś w hierarchii folderów a/b/c, a następnie tak działa docker build .na cnie pozwoli Ci uzyskać dostęp ../file-in-b. Myślę jednak, że ogólne błędne uzasadnienie w tym (lub przynajmniej moim) było to, że kontekst jest zdefiniowany przez lokalizację podaną w pierwszym argumencie polecenia kompilacji, a nie przez lokalizację pliku Docker. Tak jak stwierdzono w zaakceptowanej odpowiedzi: from a: docker build -f a/b/c/Dockerfile . oznacza, że ​​w Dockerfile .jest teraz foldera
β.εηοιτ.βε
1
Cytowanie z dokumentów Dockerfile: ścieżki plików i katalogów będą interpretowane jako względne względem źródła kontekstu kompilacji.
Nishant George Agrwal
18

Jeśli przeczytasz dyskusję w numerze 2745, nie tylko doker może nigdy nie obsługiwać dowiązań symbolicznych, ale nigdy nie może obsługiwać dodawania plików poza twoim kontekstem. Wydaje się, że filozofią projektu jest to, że pliki, które wchodzą do kompilacji dokera, powinny jawnie stanowić część kontekstu lub pochodzić z adresu URL, w którym prawdopodobnie są wdrażane również w wersji stałej, aby kompilacja była powtarzalna przy dobrze znanych adresach URL lub plikach dostarczanych z pojemnik dokera.

Wolę budować ze źródła kontrolowanego wersją - tj. Kompilacja dokera -t rzeczy http://my.git.org/repo - w przeciwnym razie buduję z przypadkowego miejsca z losowymi plikami.

zasadniczo nie .... - SvenDowideit, Docker Inc

Tylko moja opinia, ale myślę, że powinieneś dokonać restrukturyzacji, aby oddzielić kod i repozytoria dokerów. W ten sposób kontenery mogą być ogólne i pobierać dowolną wersję kodu w czasie wykonywania, a nie w czasie kompilacji.

Alternatywnie, użyj Dockera jako podstawowego artefaktu wdrażania kodu, a następnie umieść plik Dockera w katalogu głównym repozytorium kodu. jeśli wybierzesz tę trasę, prawdopodobnie warto mieć nadrzędny kontener dokujący, aby uzyskać bardziej ogólne szczegóły na poziomie systemu, i kontener podrzędny, aby skonfigurować specyficzne dla kodu.

Usman Ismail
źródło
Po co więc w ogóle korzystać z dokera?
lscoughlin
11

Uważam, że prostszym obejściem jest zmiana samego „kontekstu”.

Na przykład zamiast dawać:

docker build -t hello-demo-app .

który ustawia bieżący katalog jako kontekst, powiedzmy, że chcesz katalog macierzysty jako kontekst, po prostu użyj:

docker build -t hello-demo-app ..
Anshuman Manral
źródło
6
Myślę, że to psuje .dockerignore: - \
NullVoxPopuli
Zrezygnowałem z .dockerignore i zamiast tego utworzyłem zarządzany folder dokowania Makefile, który zawiera tylko pliki potrzebne do kontekstu kompilacji ... Muszę tylko zadzwonić make buildi pobiera wszystkie potrzebne pliki, jeśli zostały zaktualizowane, a następnie wywołuje odpowiednią kompilację dokera ... Muszę wykonać dodatkową pracę, ale działa bezbłędnie, ponieważ mam pełną kontrolę.
Sahsahae
3

Korzystając z docker-compose, osiągnąłem to, tworząc usługę, która montuje potrzebne mi woluminy i zatwierdzając obraz kontenera. Następnie w kolejnej usłudze polegam na wcześniej zatwierdzonym obrazie, który zawiera wszystkie dane przechowywane w zamontowanych lokalizacjach. Będziesz wtedy musiał skopiować te pliki do ich ostatecznego miejsca docelowego, ponieważ katalogi montowane na hoście nie są zatwierdzane podczas uruchamiania docker commitpolecenia

Aby to osiągnąć, nie musisz używać kompilatora dokerów, ale ułatwia to życie

# 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
Kris Rivera
źródło
1

Miałem ten sam problem z projektem i niektórymi plikami danych, których nie mogłem przenieść w kontekście repozytorium z powodów HIPPA. Skończyło się na 2 plikach Docker. Jeden buduje główną aplikację bez potrzebnych mi elementów poza kontenerem i publikuje ją w wewnętrznym repozytorium. Następnie drugi plik dokera pobiera ten obraz i dodaje dane i tworzy nowy obraz, który jest następnie wdrażany i nigdy nigdzie nie przechowywany. Nie jest to idealne, ale zadziałało w moich celach, aby ukryć poufne informacje poza repozytorium.

Doradztwo Annulet
źródło
1

Łatwym obejściem może być po prostu zamontowanie woluminu (za pomocą flagi -v lub --mount) po uruchomieniu go i dostęp do plików w ten sposób.

przykład:

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

więcej informacji: https://docs.docker.com/storage/volumes/

Ben Wex
źródło
Pamiętaj, że działa to tylko wtedy, gdy wolumin jest zależny od środowiska wykonawczego. Zależności od czasu kompilacji docker runjest za późno.
user3735633
1

Jak opisano w tym problemie GitHub, kompilacja faktycznie się dzieje /tmp/docker-12345, więc względna ścieżka podobna ../relative-add/some-filedo /tmp/docker-12345. W ten sposób szukałby /tmp/relative-add/some-file, co jest również wyświetlane w komunikacie o błędzie. *

Dołączanie plików spoza katalogu kompilacji jest niedozwolone, więc powoduje to wyświetlenie komunikatu „Zakazana ścieżka”.

Rocksn17
źródło
0

Jednym szybkim i brudnym sposobem jest ustawienie kontekstu kompilacji na tyle poziomów, ile potrzebujesz - ale może to mieć konsekwencje. Jeśli pracujesz w architekturze mikrousług, która wygląda następująco:

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

Możesz ustawić kontekst kompilacji na Codekatalog nadrzędny, a następnie uzyskać dostęp do wszystkiego, ale okazuje się, że przy dużej liczbie repozytoriów może to spowodować długi czas kompilacji.

Przykładem może być sytuacja, w której inny zespół utrzymuje schemat bazy danych, Repo1a kod twojego zespołu Repo2zależy od tego. Chcesz dokować tę zależność przy pomocy niektórych własnych danych źródłowych, nie martwiąc się o zmiany schematu lub zanieczyszczenie repozytorium drugiego zespołu (oczywiście w zależności od tego, jakie zmiany mogą być konieczne, musisz zmienić skrypty danych źródłowych). Drugie podejście jest zuchwałe, ale omija kwestię długich wersji:

Utwórz skrypt sh (lub ps1), ./Code/Repo2aby skopiować potrzebne pliki i wywołać żądane polecenia dokera, na przykład:

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

W pliku skompilowania dokera po prostu ustaw kontekst jako Repo2root i użyj zawartości ./db/schemakatalogu w pliku dokera, nie martwiąc się o ścieżkę. Pamiętaj, że ryzykujesz przypadkowym przekazaniem tego katalogu do kontroli źródła, ale akcje czyszczenia skryptów powinny być dość łatwe.

użytkownik1007074
źródło
0

W moim przypadku mój plik Docker jest zapisany jak szablon zawierający symbole zastępcze, które zastępuję rzeczywistą wartością za pomocą mojego pliku konfiguracyjnego.

Nie mogłem więc bezpośrednio określić tego pliku, ale potokować go w kompilacji dokera w następujący sposób:

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

Ale z powodu potoku COPYpolecenie nie działało. Ale powyższy sposób rozwiązuje to przez -f -(jawnie mówiąc, plik nie został dostarczony). Wykonując tylko -bez -fflagi, kontekst ORAZ plik Docker nie są udostępniane, co stanowi zastrzeżenie.

Daniel Katz
źródło
0

Sztuką jest rozpoznanie, że możesz określić kontekst w komendzie kompilacji, aby dołączyć pliki z katalogu nadrzędnego, jeśli podasz ścieżkę Dockera, zmieniłbym mój plik Docker tak, aby wyglądał następująco:

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

Wtedy moje polecenie kompilacji może wyglądać następująco:

docker built -t TAG -f DOCKER_FILE_PATH CONTEXT

Z katalogu projektu

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

Z projektu / dokera

docker built -t username/project[:tag] -f ./docker/Dockerfile ..
daniekpo
źródło