Jak montować woluminy hosta w kontenerach dokowanych w Dockerfile podczas kompilacji

236

Oryginalne pytanie: Jak korzystać z instrukcji VOLUME w Dockerfile?

Rzeczywiste pytanie, które chcę rozwiązać, to - jak zamontować woluminy hosta w kontenerach dokerów w Dockerfile podczas kompilacji, tj. Mając docker run -v /export:/exportmożliwość podczas docker build.

Powodem tego jest dla mnie budowanie rzeczy w Dockerze, nie chcę, aby te ( apt-get install) pamięci podręczne były zamknięte w jednym oknie dokowanym , ale aby je udostępniać / ponownie wykorzystywać. To jest główny powód, dla którego pytam o to pytanie.

Najnowsza aktualizacja:

Przed dokerem v18.09 poprawną odpowiedzią powinna być ta, która zaczyna się od:

Istnieje sposób na zamontowanie woluminu podczas kompilacji, ale nie wymaga on plików Dockerfiles.

Była to jednak źle sformułowana, zorganizowana i poparta odpowiedź. Podczas ponownej instalacji mojego okna dokowanego natknąłem się na następujący artykuł:

Dokuj usługę apt-cacher-ng
https://docs.docker.com/engine/examples/apt-cacher-ng/

To rozwiązanie dokera na to / moje pytanie, nie bezpośrednio, ale pośrednio. Jest to ortodoksyjny sposób, jaki sugeruje nam doker. Przyznaję, że jest lepszy niż ten, o który tutaj pytałem.

Innym sposobem jest nowo zaakceptowana odpowiedź , np. Buildkit w wersji 18.09.

Wybierz, który Ci odpowiada.


Było: Było rozwiązanie - rocker, który nie był z Dockera, ale teraz, kiedy rocker został wycofany, ponownie przywracam odpowiedź na „Niemożliwe” .


Stara aktualizacja: więc odpowiedź brzmi „Niemożliwe”. Mogę zaakceptować to jako odpowiedź, ponieważ wiem, że problem został szeroko omówiony na stronie https://github.com/docker/docker/issues/3156 . Rozumiem, że przenośność jest najważniejszym problemem dla programistów dokerów; ale jako użytkownik dokerów muszę powiedzieć, że jestem bardzo rozczarowany tą brakującą funkcją. Pozwólcie, że zamknę mój argument cytatem ze wspomnianej dyskusji: „ Chciałbym użyć Gentoo jako obrazu podstawowego, ale zdecydowanie nie chcę, aby po zbudowaniu obrazu ponad 1 GB danych z drzewa Portage znajdowało się w żadnej z warstw. mogłyby mieć ładne, kompaktowe pojemniki, gdyby nie gigantyczne drzewo portage, które musiało pojawić się na obrazie podczas instalacji.„Tak, mogę użyć wget lub curl, aby pobrać wszystko, czego potrzebuję, ale fakt, że sama rozważność w zakresie mobilności zmusza mnie do pobrania> 1 GB drzewa Portage za każdym razem, gdy buduję obraz podstawowy Gentoo, nie jest ani wydajny, ani przyjazny dla użytkownika. więcej, repozytorium pakietów BĘDZIE ZAWSZE znajdowało się w katalogu / usr / portage, więc ZAWSZE MOŻNA PRZENOSIĆ pod Gentoo. Znowu szanuję decyzję, ale proszę pozwolić mi wyrazić moje rozczarowanie również w międzyczasie. Dzięki.


Oryginalne pytanie szczegółowo:

Z

Udostępniaj katalogi za pośrednictwem woluminów
http://docker.readthedocs.org/en/v0.7.3/use/working_with_volumes/

mówi, że funkcja Woluminy danych „była dostępna od wersji 1 Docker Remote API”. Mój doker jest w wersji 1.2.0, ale znalazłem przykład podany w powyższym artykule nie działa:

# BUILD-USING:        docker build -t data .
# RUN-USING:          docker run -name DATA data
FROM          busybox
VOLUME        ["/var/volume1", "/var/volume2"]
CMD           ["/usr/bin/true"]

Jaki jest właściwy sposób w Dockerfile do montowania woluminów montowanych na hoście w kontenerach dokerów za pomocą polecenia VOLUME?

$ apt-cache policy lxc-docker
lxc-docker:
  Installed: 1.2.0
  Candidate: 1.2.0
  Version table:
 *** 1.2.0 0
        500 https://get.docker.io/ubuntu/ docker/main amd64 Packages
        100 /var/lib/dpkg/status

$ cat Dockerfile 
FROM          debian:sid

VOLUME        ["/export"]
RUN ls -l /export
CMD ls -l /export

$ docker build -t data .
Sending build context to Docker daemon  2.56 kB
Sending build context to Docker daemon 
Step 0 : FROM          debian:sid
 ---> 77e97a48ce6a
Step 1 : VOLUME        ["/export"]
 ---> Using cache
 ---> 59b69b65a074
Step 2 : RUN ls -l /export
 ---> Running in df43c78d74be
total 0
 ---> 9d29a6eb263f
Removing intermediate container df43c78d74be
Step 3 : CMD ls -l /export
 ---> Running in 8e4916d3e390
 ---> d6e7e1c52551
Removing intermediate container 8e4916d3e390
Successfully built d6e7e1c52551

$ docker run data
total 0

$ ls -l /export | wc 
     20     162    1131

$ docker -v
Docker version 1.2.0, build fa7b24f
xpt
źródło
Najwyraźniej aktualne żądanie funkcji (nie spodziewam się, że zostanie ono wdrożone, ale na wszelki wypadek): docker / docker # 14080
Jesse Glick
w istocie trwa obszerna dyskusja, że ​​nie powinno się zezwalać na łączenie katalogu hosta i katalogu kontenera podczas kompilacji, tj VOLUME ~/host_dir ~/container_dir. Dyskusja jest dość obszerna, czy istnieje krótki sposób na podsumowanie, jaki jest powód?
Charlie Parker

Odpowiedzi:

34

Najpierw odpowiedz „dlaczego nie VOLUMEdziała?” Po zdefiniowaniu VOLUMEw Dockerfile można zdefiniować tylko cel, a nie źródło woluminu. Podczas kompilacji otrzymasz tylko anonimowy wolumin. Ten anonimowy wolumin zostanie zamontowany przy każdym RUNpoleceniu, wstępnie wypełniony zawartością obrazu, a następnie odrzucony na końcu RUNpolecenia. Zapisywane są tylko zmiany w kontenerze, a nie zmiany woluminu.


Ponieważ zadano to pytanie, wydano kilka funkcji, które mogą pomóc. Pierwszy to wieloetapowe kompilacje, które pozwalają zbudować niewydajny pierwszy stopień niewykorzystanego miejsca na dysku i skopiować tylko potrzebne dane wyjściowe do ostatniego wysyłanego etapu. Drugą funkcją jest Buildkit, który radykalnie zmienia sposób budowania obrazów i dodaje nowe możliwości do kompilacji.

W przypadku kompilacji wieloetapowej miałbyś wiele FROMlinii, z których każda rozpoczyna tworzenie oddzielnego obrazu. Domyślnie tylko ostatni obraz jest oznaczony, ale można kopiować pliki z poprzednich etapów. Standardowym zastosowaniem jest posiadanie środowiska kompilatora do tworzenia binarnego lub innego artefaktu aplikacji oraz środowiska wykonawczego jako drugiego etapu kopiowania tego artefaktu. Możesz mieć:

FROM debian:sid as builder
COPY export /export
RUN compile command here >/result.bin

FROM debian:sid
COPY --from=builder /result.bin /result.bin
CMD ["/result.bin"]

Spowodowałoby to kompilację zawierającą tylko wynikowy plik binarny, a nie pełny katalog / export.


Buildkit wychodzi z eksperymentu w 18.09. Jest to całkowite przeprojektowanie procesu kompilacji, w tym możliwość zmiany parsera frontendu. Jedna z tych zmian parsera zaimplementowała RUN --mountopcję, która pozwala zamontować katalog pamięci podręcznej dla poleceń uruchamiania. Np. Tutaj jest taki, który montuje niektóre katalogi debiana (z rekonfiguracją obrazu debian, może to przyspieszyć ponowne instalowanie pakietów):

# syntax = docker/dockerfile:experimental
FROM debian:latest
RUN --mount=target=/var/lib/apt/lists,type=cache \
    --mount=target=/var/cache/apt,type=cache \
    apt-get update \
 && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
      git

Dostosowałbyś katalog pamięci podręcznej do dowolnej pamięci podręcznej aplikacji, np. $ HOME / .m2 dla maven lub /root/.cache dla golanga.


TL; DR: Odpowiedź jest tutaj: Dzięki tej RUN --mountskładni możesz również powiązać katalogi montowania tylko do odczytu z kontekstu kompilacji. Folder musi istnieć w kontekście kompilacji i nie jest mapowany z powrotem na hosta lub klienta kompilacji:

# syntax = docker/dockerfile:experimental
FROM debian:latest
RUN --mount=target=/export,type=bind,source=export \
    process export directory here...

Zauważ, że ponieważ katalog jest montowany z kontekstu, jest również montowany tylko do odczytu i nie można wypychać zmian z powrotem do hosta lub klienta. Podczas kompilacji potrzebujesz wersji 18.09 lub nowszej i włącz buildkit za pomocą export DOCKER_BUILDKIT=1.

Jeśli pojawi się błąd, że flaga mount nie jest obsługiwana, oznacza to, że albo nie włączyłeś buildkit z powyższą zmienną, albo że nie włączyłeś eksperymentalnej składni z linią składni na górze pliku Dockerfile wszelkie inne wiersze, w tym komentarze. Pamiętaj, że zmienna przełączająca buildkit będzie działać tylko wtedy, gdy instalacja dokera ma wbudowaną obsługę buildkit, która wymaga wersji 18.09 lub nowszej od Docker, zarówno na kliencie, jak i na serwerze.

BMitch
źródło
2
Niestety, Windows Buildkit nie jest jeszcze obsługiwany w wersji 18.09
Wesley
1
Wygląda na to, że „armhf” również nie obsługuje „mount”.
Mike
2
Dostaję „Odpowiedź błędu od demona: Wiersz błędu analizy pliku Docker xx: Nieznana flaga: zamontowanie” w OSX
ChristoKiwi
1
Wsparcie dla docker-compose nie jest jeszcze dostępne, ale nie trzeba komponować, aby budować obrazy. Wydanie do śledzenia: github.com/moby/buildkit/issues/685
BMitch
2
Dokumentacja na ten temat: github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/…
Drew LeSueur
116

Nie można użyć VOLUMEinstrukcji, aby powiedzieć dokerowi, co należy zamontować. To poważnie zakłóciłoby przenośność. Ta instrukcja mówi dokerowi, że zawartość tych katalogów nie jest wyświetlana w obrazach i można uzyskać do niej dostęp z innych kontenerów za pomocą --volumes-fromparametru wiersza polecenia. Musisz uruchomić kontener, -v /path/on/host:/path/in/containeraby uzyskać dostęp do katalogów z hosta.

Montowanie woluminów hosta podczas kompilacji nie jest możliwe. Brak uprzywilejowanej kompilacji i montowanie hosta poważnie ograniczyłoby przenośność. Możesz spróbować użyć wget lub curl, aby pobrać wszystko, czego potrzebujesz do kompilacji i zainstalować.

Andreas Steffan
źródło
2
Dzięki. Pytanie poprawione. Właściwe pytanie, które chcę rozwiązać, to - jak zamontować woluminy hosta w kontenerach dokerów w Dockerfile podczas kompilacji. Dzięki.
xpt
2
Niemożliwe. Zobacz poprawioną odpowiedź.
Andreas Steffan,
3
Mogę docenić „potencjalne” złe skutki uboczne przenoszenia, ale istnieje również uzasadniony przypadek użycia tej opcji. W moim przypadku chciałbym móc powiedzieć użytkownikom, aby „przenieśli się do katalogu i uruchomili polecenie„ docker run ””, mając $ (PWD) zamontowany w jakimś katalogu kontenera. $ (PWD) zapewnia utrzymanie przenośności. Chociaż może to być przypadek narożny, bardzo pomogłoby mi to, gdy dystrybuuję środowiska wykonawcze dla skryptów dostarczanych przez użytkowników.
ntwrkguru
64

AKTUALIZACJA: Ktoś po prostu nie przyjmie nie jako odpowiedzi i bardzo mi się podoba, szczególnie w przypadku tego konkretnego pytania.

DOBRA WIADOMOŚĆ, Teraz jest sposób -

Rozwiązaniem jest Rocker: https://github.com/grammarly/rocker

John Yani powiedział : „IMO rozwiązuje wszystkie słabe punkty Dockerfile, dzięki czemu nadaje się do rozwoju”.

Biegun

https://github.com/grammarly/rocker

Wprowadzając nowe polecenia, Rocker ma na celu rozwiązanie następujących przypadków użycia, które są bolesne dla zwykłego Dockera:

  1. Montuj woluminy wielokrotnego użytku na etapie kompilacji, więc narzędzia zarządzania zależnościami mogą używać pamięci podręcznej między kompilacjami.
  2. Współdziel klucze ssh z kompilacją (do pobierania prywatnych repozytoriów itp.), Nie pozostawiając ich na obrazie wynikowym.
  3. Twórz i uruchamiaj aplikacje na różnych obrazach, aby móc łatwo przenosić artefakt z jednego obrazu na drugi, najlepiej mieć tę logikę w jednym pliku Docker.
  4. Oznaczaj / pchaj obrazy bezpośrednio z Dockerfiles.
  5. Przekaż zmienne z komendy budowania powłoki, aby można je było zastąpić plikiem Docker.

I więcej. Są to najbardziej krytyczne problemy, które blokowały naszą adopcję Dockera w gramatyce.

Aktualizacja: Rocker został wycofany, zgodnie z oficjalnym repozytorium projektu na Github

Na początku 2018 r. Ekosystem kontenerowy jest znacznie bardziej dojrzały niż trzy lata temu, kiedy ten projekt został zainicjowany. Teraz niektóre z najważniejszych i wyjątkowych funkcji klawisza można łatwo pokryć kompilacją dokera lub innymi dobrze obsługiwanymi narzędziami, chociaż niektóre funkcje pozostają unikalne dla klawisza. Więcej informacji na stronie https://github.com/grammarly/rocker/issues/199 .

xpt
źródło
Próbuję użyć Rockera do rozwiązania problemu nr 1, ale polecenie mount nie działa, a utworzony obraz nie zawiera folderu hosta. Moje polecenie montowania Dockerfile wygląda tak - MOUNT ~/code/docker-app-dev/new-editor/:/src/a moje polecenie budowania Rocker jest to - rocker build -f Dockerfile .. Co robię źle?
Yaron Idan
Może spróbuj użyć prawdziwej ścieżki hosta? ~jest metaznakiem powłoki Bourne'a.
Jesse Glick
Rocker buildnie zezwala na docker runopcje wiersza polecenia, więc obecnie nie zezwala na takie rzeczy --privileged.
Monty Wild,
Cześć @xpt, czy możemy dostać kolejną aktualizację, ponieważ rocker jest już wycofany
Shardj
Teraz, gdy rocker został wycofany, ponownie przywracam odpowiedź na „Niemożliwe”. Zobacz OP i wybraną odpowiedź.
xpt
14

Istnieje sposób na zamontowanie woluminu podczas kompilacji, ale nie wymaga on plików Dockerfiles.

Techniką byłoby utworzenie kontenera z dowolnej bazy, której chcesz użyć (zamontowanie woluminów w kontenerze z -vopcją), uruchomienie skryptu powłoki, aby wykonać prace związane z budowaniem obrazu, a następnie zatwierdzenie kontenera jako obrazu po zakończeniu .

To nie tylko pominie nadmiar plików, których nie chcesz (jest to również dobre dla bezpiecznych plików, takich jak pliki SSH), ale także tworzy pojedynczy obraz. Ma to swoje wady: polecenie commit nie obsługuje wszystkich instrukcji Dockerfile i nie pozwala ci wybrać, kiedy przerwałeś, jeśli musisz edytować skrypt kompilacji.

AKTUALIZACJA:

Na przykład,

CONTAINER_ID=$(docker run -dit ubuntu:16.04)
docker cp build.sh $CONTAINER_ID:/build.sh
docker exec -t $CONTAINER_ID /bin/sh -c '/bin/sh /build.sh'
docker commit $CONTAINER_ID $REPO:$TAG
docker stop $CONTAINER_ID
Keith Mason
źródło
6
+1 Czy mógłbyś bardziej szczegółowo rozwinąć instrukcje w drugim akapicie. Na przykład, jeśli baza jest, debian:wheezya skrypt powłoki to build.sh, jakich konkretnych instrukcji byś użył?
Drux
6

Podczas uruchamiania kontenera tworzony jest katalog na hoście i montowany w kontenerze. Możesz dowiedzieć się, z jakim to katalogiem

$ docker inspect --format "{{ .Volumes }}" <ID>
map[/export:/var/lib/docker/vfs/dir/<VOLUME ID...>]

Jeśli chcesz zamontować katalog z hosta w swoim kontenerze, musisz użyć -vparametru i określić katalog. W twoim przypadku byłoby to:

docker run -v /export:/export data

Więc użyjesz folderu hosts w swoim kontenerze.

Behe
źródło
1
Dzięki. Pytanie poprawione. Właściwe pytanie, które chcę rozwiązać, to - jak zamontować woluminy hosta w kontenerach dokerów w Dockerfile podczas kompilacji. Dzięki.
xpt
Proszę nie korygować swoich pytań w tak drastyczny sposób . To powoduje, że moje pytanie jest nieważne, chociaż było całkowicie poprawne przed wprowadzeniem zmian. Zamiast tego rozważ zadanie nowego pytania.
Behe
11
Oryginalne pytanie : Jak korzystać z instrukcji VOLUME w Dockerfile? To wciąż jest na początku pytania, nawet dzisiaj. Twoja odpowiedź dotyczyła czasu wykonywania , a moje pytanie zawsze dotyczyło czasu kompilacji , do czego służy plik Docker.
xpt
4

Myślę, że możesz zrobić to, co chcesz, uruchamiając kompilację za pomocą polecenia dokera, które samo jest uruchamiane w kontenerze dokera. Zobacz Docker może teraz działać w Docker | Blog Docker . Zastosowano taką technikę, która faktycznie uzyskała dostęp do zewnętrznego okna dokowanego za pomocą kontenera, np. Podczas badania, jak utworzyć najmniejszy możliwy kontener Docker | Blog Xebia .

Kolejnym istotnym artykułem jest Optymalizacja obrazów dokerów | CenturyLink Labs , który wyjaśnia, że ​​jeśli skończysz pobierać rzeczy podczas kompilacji, możesz uniknąć marnowania miejsca na ostatecznym obrazie, pobierając, budując i usuwając pobieranie w jednym kroku RUN.

nealmcb
źródło
3

To brzydkie, ale udało mi się to pozornie:

Plik Docker:

FROM foo
COPY ./m2/ /root/.m2
RUN stuff

imageBuild.sh:

docker build . -t barImage
container="$(docker run -d barImage)"
rm -rf ./m2
docker cp "$container:/root/.m2" ./m2
docker rm -f "$container"

Mam kompilację Java, która pobiera wszechświat do katalogu /root/.m2 i robiłem to za każdym razem . imageBuild.shkopiuje zawartość tego folderu na host po kompilacji iDockerfile kopiuje je z powrotem do obrazu dla następnej kompilacji.

Jest to coś w rodzaju działania woluminu (tzn. Utrzymuje się między kompilacjami).

MatrixManAtYrService
źródło
Jest to realne rozwiązanie dla ciągłej integracji opartej na Docker, czyli CI. Skonfiguruj biblioteki i kompilatory i uruchom komendę make za pomocą poleceń Dockerfile, uruchom obraz w sposób trywialny, aby utworzyć kontener, a na koniec skopiuj żądany artefakt, np. .Deb. Wydaje się, że działa, dzięki za opublikowanie tego.
chrisinmtown,
To rozwiązanie pozostawia obraz ze WSZYSTKIMI plikami w ./m2/ - tym, którego potrzebujesz i tym, którego nie potrzebujesz - i może to prowadzić do OGROMNYCH obrazów produkcyjnych, co nie jest pożądane! Po podłączeniu do zewnętrznego katalogu zależności skopiowane zostaną tylko potrzebne pliki do obrazu.
Marko Krajnc
Jeśli zamierzasz opublikować obraz, prawdopodobnie najlepiej po prostu zaczekaj i pozwól maven za każdym razem pobrać własne zależności. Ten hack ma sens tylko wtedy, gdy wystawiasz obraz do testowania - obraz, z którym użytkownicy końcowi nigdy się nie zetkną.
MatrixManAtYrService
1

Oto uproszczona wersja 2-etapowego podejścia z wykorzystaniem kompilacji i zatwierdzania, bez skryptów powłoki. To wymaga:

  1. Budowanie obrazu częściowo, bez tomów
  2. Uruchamianie kontenera z woluminami , wprowadzanie zmian, a następnie zatwierdzanie wyniku, zastępując oryginalną nazwę obrazu.

Przy stosunkowo niewielkich zmianach dodatkowy krok dodaje tylko kilka sekund do czasu kompilacji.

Gruntownie:

docker build -t image-name . # your normal docker build

# Now run a command in a throwaway container that uses volumes and makes changes:
docker run -v /some:/volume --name temp-container image-name /some/post-configure/command

# Replace the original image with the result:
# (reverting CMD to whatever it was, otherwise it will be set to /some/post-configure/command)   
docker commit --change="CMD bash" temp-container image-name 

# Delete the temporary container:
docker rm temp-container

W moim przypadku chcę wstępnie wygenerować plik maven toolchains.xml, ale wiele moich instalacji JDK znajduje się na woluminie, który nie jest dostępny do czasu uruchomienia. Niektóre z moich obrazów nie są kompatybilne ze wszystkimi JDKS, więc muszę przetestować kompatybilność w czasie kompilacji i warunkowo wypełnić plik toolchains.xml. Pamiętaj, że nie potrzebuję przenośnego obrazu, nie publikuję go w Docker Hub.

Akom
źródło
1

Jak wielu już odpowiedziało, montowanie woluminów hosta podczas kompilacji nie jest możliwe. Chciałbym tylko dodać docker-composesposób, myślę, że byłoby miło mieć, głównie do użytku w programowaniu / testowaniu

Plik Docker

FROM node:10
WORKDIR /app
COPY . .
RUN npm ci
CMD sleep 999999999

docker-compose.yml

version: '3'
services:
  test-service:
    image: test/image
    build:
      context: .
      dockerfile: Dockerfile
    container_name: test
    volumes:
      - ./export:/app/export
      - ./build:/app/build

I poprowadź swój kontener docker-compose up -d --build

Jegor Zaremba
źródło