Kopiowanie plików z kontenera Docker na host

1707

Zastanawiam się nad użyciem Dockera do zbudowania moich zależności na serwerze Continuous Integration (CI), aby nie musiałem instalować wszystkich środowisk wykonawczych i bibliotek na samych agentach.

Aby to osiągnąć, musiałbym skopiować artefakty kompilacji wbudowane w kontener z powrotem do hosta. Czy to jest możliwe?

użytkownik2668128
źródło
wy może jak moje metody hakerów tutaj: stackoverflow.com/a/55876794/990618
Colin Lamarre
1
Prawidłowa i aktualna odpowiedź kapitana dokera na dole odpowiedzi.
burtsevyg

Odpowiedzi:

2942

Aby skopiować plik z kontenera na host, możesz użyć polecenia

docker cp <containerId>:/file/path/within/container /host/path/target

Oto przykład:

$ sudo docker cp goofy_roentgen:/out_read.jpg .

Tutaj goofy_roentgen to nazwa kontenera, którą otrzymałem z następującego polecenia:

$ sudo docker ps

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                                            NAMES
1b4ad9311e93        bamos/openface      "/bin/bash"         33 minutes ago      Up 33 minutes       0.0.0.0:8000->8000/tcp, 0.0.0.0:9000->9000/tcp   goofy_roentgen

Możesz także użyć (części) identyfikatora kontenera . Poniższe polecenie jest równoważne pierwszemu

$ sudo docker cp 1b4a:/out_read.jpg .
skrzyp
źródło
42
Oto przydatny sposób, aby w swojej najnowszej pojemnika, jeśli jesteś po prostu za pomocą docker o temp Linux środowiska: docker ps -alq.
Josh Habdas
37
to polecenie cp działa tak jak jest w przypadku kopiowania drzew katalogów (nie tylko jednego pliku).
ecoe
88
W nowszych wersjach doker ty może skopiować dwukierunkowo (host do kontenera lub pojemnika na żądanie) zdocker cp ...
Freedom_Ben
9
Musiałem docker cp -Lskopiować dowiązania symboliczne
Harrison Powers
24
UWAGA: kontener nie musi być uruchomiony, aby użyć polecenia cp. Przydatny, jeśli twój pojemnik ciągle się zawiesza.
Martlark
219

Nie musisz używać docker run.

Możesz to zrobić za pomocą docker create.

Z dokumentów :

docker createPolecenie tworzy warstwę do zapisu kontenera po określonym obrazem i przygotowuje ją do prowadzenia określonego polecenia. Identyfikator kontenera jest następnie drukowany na STDOUT. Jest to podobne do docker run -dtego, że kontener nigdy się nie uruchamia.

Możesz więc:

docker create -ti --name dummy IMAGE_NAME bash
docker cp dummy:/path/to/file /dest/to/file
docker rm -f dummy

Tutaj nigdy nie uruchamiasz kontenera. To wyglądało na korzystne dla mnie.

Ishan Bhatt
źródło
19
To wymaga więcej pozytywnych opinii. Idealne, gdy trzeba tylko zbudować coś w kontenerze, a następnie skopiować wyniki.
Honza Kalfus
4
@HonzaKalfus Zgadzam się, że to musi być wyższe. Właśnie o to mi chodziło. Użyłem tego, aby móc budować niektóre pliki binarne przy użyciu znanego środowiska (amazon linux w określonej wersji). był w stanie stworzyć skrypt powłoki, który w pełni zbudował okno dokowane i wyodrębnił z niego wynikowy plik binarny! Doskonały.
Mark
1
Jest -tiwymagane i bashwymagane?
jII
@ jII, zrobiłem to, ponieważ później dokonuję na nim uruchomić. W prostych przypadkach nie jest to potrzebne, ale tutaj też nie szkodzi.
Ishan Bhatt
Czy w jakiś sposób można używać symboli wieloznacznych? Mam na myśli ... Nie znam dokładnej nazwy pliku, który muszę skopiować, ponieważ ma on numer wersji.
juzzlin
87

Zamontuj „wolumin” i skopiuj tam artefakty:

mkdir artifacts
docker run -i -v ${PWD}/artifacts:/artifacts ubuntu:14.04 sh << COMMANDS
# ... build software here ...
cp <artifact> /artifacts
# ... copy more artifacts into `/artifacts` ...
COMMANDS

Następnie, gdy kompilacja się kończy, a kontener już nie działa, skopiował już artefakty z kompilacji do artifactskatalogu na hoście.

Edytować

Ostrzeżenie: po wykonaniu tej czynności możesz napotkać problemy z identyfikatorem użytkownika dokera odpowiadającym identyfikatorowi bieżącego działającego użytkownika. Oznacza to, że pliki w /artifactsbędą wyświetlane jako posiadane przez użytkownika z identyfikatorem UID użytkownika użytego w kontenerze dokera. Rozwiązaniem może być użycie identyfikatora UID użytkownika wywołującego:

docker run -i -v ${PWD}:/working_dir -w /working_dir -u $(id -u) \
    ubuntu:14.04 sh << COMMANDS
# Since $(id -u) owns /working_dir, you should be okay running commands here
# and having them work. Then copy stuff into /working_dir/artifacts .
COMMANDS
djhaskin987
źródło
7
W rzeczywistości można użyć chownpolecenia, aby dopasować identyfikator użytkownika i identyfikator grupy na komputerze hosta.
Dimchansky
@Frondor Patrz odniesienie do konfiguracji woluminu docs.docker.com/compose/compose-file/…
djhaskin987
Już to zrobił i to nie zadziała. Gdy kontener po raz pierwszy skopiuje pliki do woluminu, następnym razem wolumin nie będzie już pusty i pliki nie zostaną zastąpione nowszymi. Kontener nadaje pierwszeństwo plikom hosta (tym, które zostały skopiowane przy pierwszym zamontowaniu obrazu kontenera).
Frondor,
brzmi jak coś, co może być własnym pytaniem SO @Frondor
djhaskin987
1
Kupuję ci kumpel piwa! Dzięki!
Dimitar Vukman
27

Zamontuj wolumin, skopiuj artefakty, dostosuj identyfikator właściciela i identyfikator grupy:

mkdir artifacts
docker run -i --rm -v ${PWD}/artifacts:/mnt/artifacts centos:6 /bin/bash << COMMANDS
ls -la > /mnt/artifacts/ls.txt
echo Changing owner from \$(id -u):\$(id -g) to $(id -u):$(id -u)
chown -R $(id -u):$(id -u) /mnt/artifacts
COMMANDS
Dimczański
źródło
24

TLDR;

$ docker run --rm -iv${PWD}:/host-volume my-image sh -s <<EOF
chown $(id -u):$(id -g) my-artifact.tar.xz
cp -a my-artifact.tar.xz /host-volume
EOF

Opis

docker runz woluminem hosta, chownartefaktem, cpartefaktem do woluminu hosta:

$ docker build -t my-image - <<EOF
> FROM busybox
> WORKDIR /workdir
> RUN touch foo.txt bar.txt qux.txt
> EOF
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM busybox
 ---> 00f017a8c2a6
Step 2/3 : WORKDIR /workdir
 ---> Using cache
 ---> 36151d97f2c9
Step 3/3 : RUN touch foo.txt bar.txt qux.txt
 ---> Running in a657ed4f5cab
 ---> 4dd197569e44
Removing intermediate container a657ed4f5cab
Successfully built 4dd197569e44

$ docker run --rm -iv${PWD}:/host-volume my-image sh -s <<EOF
chown -v $(id -u):$(id -g) *.txt
cp -va *.txt /host-volume
EOF
changed ownership of '/host-volume/bar.txt' to 10335:11111
changed ownership of '/host-volume/qux.txt' to 10335:11111
changed ownership of '/host-volume/foo.txt' to 10335:11111
'bar.txt' -> '/host-volume/bar.txt'
'foo.txt' -> '/host-volume/foo.txt'
'qux.txt' -> '/host-volume/qux.txt'

$ ls -n
total 0
-rw-r--r-- 1 10335 11111 0 May  7 18:22 bar.txt
-rw-r--r-- 1 10335 11111 0 May  7 18:22 foo.txt
-rw-r--r-- 1 10335 11111 0 May  7 18:22 qux.txt

Ta sztuczka działa, ponieważ chownwywoływanie w heredoc zajmuje$(id -u):$(id -g) wartości spoza działającego kontenera; tzn. host dokera.

Korzyści to:

  • nie musisz docker container run --namelubdocker container create --name wcześniej
  • nie musisz docker container rmpóźniej
rubicks
źródło
2
Wybrany za porównanie cpodpowiedzi opartych na wolumenie. Również w przypadku idpodstępu własnościowego czasami jest to prawdziwy ból głowy
Marc Ghorayeb
18

Większość odpowiedzi nie wskazuje, że kontener musi uruchomić się, zanim docker cpzadziała:

docker build -t IMAGE_TAG .
docker run -d IMAGE_TAG
CONTAINER_ID=$(docker ps -alq)
# If you do not know the exact file name, you'll need to run "ls"
# FILE=$(docker exec CONTAINER_ID sh -c "ls /path/*.zip")
docker cp $CONTAINER_ID:/path/to/file .
docker stop $CONTAINER_ID
cmcginty
źródło
3
BTW, to, czy kontener musi / może być uruchomiony / zatrzymany / albo wydaje się zależeć od typu hosta / techniki wirtualizacji . Obecny dokument dokera mówi „POJEMNIK może być działającym lub zatrzymanym kontenerem”. Wiele miejsc w SO, w tym komentarz do zaakceptowanej odpowiedzi, mówi „działa to również na zatrzymanym kontenerze”. Pod Windows Hyper-V, najwyraźniej konieczne jest zatrzymanie kontenera przed skopiowaniem pliku .
ToolmakerSteve
Kopiowanie działa również, gdy pojemnik jest zatrzymany.
Luke W
17

Jeśli nie masz działającego kontenera, tylko obraz i zakładając, że chcesz skopiować tylko plik tekstowy, możesz zrobić coś takiego:

docker run the-image cat path/to/container/file.txt > path/to/host/file.txt
rakbero
źródło
7

Publikuję to dla każdego, kto korzysta z Docker na Maca. Oto, co zadziałało dla mnie:

 $ mkdir mybackup # local directory on Mac

 $ docker run --rm --volumes-from <containerid> \
    -v `pwd`/mybackup:/backup \  
    busybox \                   
    cp /data/mydata.txt /backup 

Zauważ, że kiedy montuję, -vten backupkatalog jest tworzony automatycznie.

Mam nadzieję, że kiedyś to się przyda. :)

Paweł
źródło
Jeśli używasz komponowania dokowania, woluminy od są przestarzałe w wersji 3 i późniejszych.
mulg0r
Aby dodać do komentarza mulg0r, zobacz stackoverflow.com/a/45495380/199364 - w v.3 umieszczasz volumespolecenie w katalogu głównym config.yml, aby woluminy były dostępne dla wielu kontenerów.
ToolmakerSteve
5

Jeśli chcesz tylko pobrać plik z obrazu (zamiast działającego kontenera), możesz to zrobić:

docker run --rm <image> cat <source> > <local_dest>

Spowoduje to wyświetlenie kontenera, zapisanie nowego pliku, a następnie usunięcie kontenera. Wadą jest jednak to, że uprawnienia do plików i data modyfikacji nie zostaną zachowane.

sg
źródło
5

Wraz z wydaniem Dockera 19.03 możesz pominąć tworzenie kontenera, a nawet budowanie obrazu. W kompilacjach opartych na BuildKit dostępna jest opcja zmiany miejsca docelowego. Możesz użyć tego do zapisania wyników kompilacji do lokalnego katalogu zamiast do obrazu. Np. Oto wersja binarna go:

$ ls
Dockerfile  go.mod  main.go

$ cat Dockerfile
FROM golang:1.12-alpine as dev
RUN apk add --no-cache git ca-certificates
RUN adduser -D appuser
WORKDIR /src
COPY . /src/
CMD CGO_ENABLED=0 go build -o app . && ./app

FROM dev as build
RUN CGO_ENABLED=0 go build -o app .
USER appuser
CMD [ "./app" ]

FROM scratch as release
COPY --from=build /etc/passwd /etc/group /etc/
COPY --from=build /src/app /app
USER appuser
CMD [ "/app" ]

FROM scratch as artifact
COPY --from=build /src/app /app

FROM release

Z powyższego pliku Docker tworzę artifactscenę, która zawiera tylko pliki, które chcę wyeksportować. A nowo wprowadzona --outputflaga pozwala mi zapisać je do lokalnego katalogu zamiast obrazu. Należy to wykonać za pomocą silnika BuildKit dostarczanego z wersją 19.03:

$ DOCKER_BUILDKIT=1 docker build --target artifact --output type=local,dest=. .
[+] Building 43.5s (12/12) FINISHED
 => [internal] load build definition from Dockerfile                                                                              0.7s
 => => transferring dockerfile: 572B                                                                                              0.0s
 => [internal] load .dockerignore                                                                                                 0.5s
 => => transferring context: 2B                                                                                                   0.0s
 => [internal] load metadata for docker.io/library/golang:1.12-alpine                                                             0.9s
 => [dev 1/5] FROM docker.io/library/golang:1.12-alpine@sha256:50deab916cce57a792cd88af3479d127a9ec571692a1a9c22109532c0d0499a0  22.5s
 => => resolve docker.io/library/golang:1.12-alpine@sha256:50deab916cce57a792cd88af3479d127a9ec571692a1a9c22109532c0d0499a0       0.0s
 => => sha256:1ec62c064901392a6722bb47a377c01a381f4482b1ce094b6d28682b6b6279fd 155B / 155B                                        0.3s
 => => sha256:50deab916cce57a792cd88af3479d127a9ec571692a1a9c22109532c0d0499a0 1.65kB / 1.65kB                                    0.0s
 => => sha256:2ecd820bec717ec5a8cdc2a1ae04887ed9b46c996f515abc481cac43a12628da 1.36kB / 1.36kB                                    0.0s
 => => sha256:6a17089e5a3afc489e5b6c118cd46eda66b2d5361f309d8d4b0dcac268a47b13 3.81kB / 3.81kB                                    0.0s
 => => sha256:89d9c30c1d48bac627e5c6cb0d1ed1eec28e7dbdfbcc04712e4c79c0f83faf17 2.79MB / 2.79MB                                    0.6s
 => => sha256:8ef94372a977c02d425f12c8cbda5416e372b7a869a6c2b20342c589dba3eae5 301.72kB / 301.72kB                                0.4s
 => => sha256:025f14a3d97f92c07a07446e7ea8933b86068d00da9e252cf3277e9347b6fe69 125.33MB / 125.33MB                               13.7s
 => => sha256:7047deb9704134ff71c99791be3f6474bb45bc3971dde9257ef9186d7cb156db 125B / 125B                                        0.8s
 => => extracting sha256:89d9c30c1d48bac627e5c6cb0d1ed1eec28e7dbdfbcc04712e4c79c0f83faf17                                         0.2s
 => => extracting sha256:8ef94372a977c02d425f12c8cbda5416e372b7a869a6c2b20342c589dba3eae5                                         0.1s
 => => extracting sha256:1ec62c064901392a6722bb47a377c01a381f4482b1ce094b6d28682b6b6279fd                                         0.0s
 => => extracting sha256:025f14a3d97f92c07a07446e7ea8933b86068d00da9e252cf3277e9347b6fe69                                         5.2s
 => => extracting sha256:7047deb9704134ff71c99791be3f6474bb45bc3971dde9257ef9186d7cb156db                                         0.0s
 => [internal] load build context                                                                                                 0.3s
 => => transferring context: 2.11kB                                                                                               0.0s
 => [dev 2/5] RUN apk add --no-cache git ca-certificates                                                                          3.8s
 => [dev 3/5] RUN adduser -D appuser                                                                                              1.7s
 => [dev 4/5] WORKDIR /src                                                                                                        0.5s
 => [dev 5/5] COPY . /src/                                                                                                        0.4s
 => [build 1/1] RUN CGO_ENABLED=0 go build -o app .                                                                              11.6s
 => [artifact 1/1] COPY --from=build /src/app /app                                                                                0.5s
 => exporting to client                                                                                                           0.1s
 => => copying files 10.00MB                                                                                                      0.1s

Po zakończeniu kompilacji appplik binarny został wyeksportowany:

$ ls
Dockerfile  app  go.mod  main.go

$ ./app
Ready to receive requests on port 8080

Docker ma inne opcje --outputflagi opisane w repozytorium BuildKit: https://github.com/moby/buildkit#output

BMitch
źródło
standardowa pamięć podręczna kompilacji nieużywana do kompilacji z wyjściem, jest zła
burtsevyg
@burtsevyg Buildkit to inny program budujący, wykorzystujący inne środowisko pamięci podręcznej. Jest o wiele bardziej wydajny w buforowaniu.
BMitch
dziękuję, poprawię moje węzły kompilacji.
burtsevyg,
4

Bardziej ogólnym rozwiązaniem jest wtyczka CloudBees do Jenkinsa do wbudowania w pojemnik Docker . Możesz wybrać obraz do użycia z rejestru Docker lub zdefiniować plik Docker do kompilacji i użycia.

Zamontuje obszar roboczy do kontenera jako wolumin (z odpowiednim użytkownikiem), ustawi go jako katalog roboczy, wykona dowolne polecenia (wewnątrz kontenera). Aby to zrobić, możesz również użyć wtyczki dokującej przepływu pracy (jeśli wolisz kod niż interfejs użytkownika) za pomocą komendy image.inside () {}.

Zasadniczo wszystko to, upieczone na serwerze CI / CD, a potem trochę.

BobMcGee
źródło
4

Użyłem PowerShell (Admin) z tym poleceniem.

docker cp {container id}:{container path}/error.html  C:\\error.html

Przykład

docker cp ff3a6608467d:/var/www/app/error.html  C:\\error.html
Khachornchit Songsaen
źródło
2

Inną dobrą opcją jest najpierw zbudowanie kontenera, a następnie uruchomienie go za pomocą flagi -c z interpreterem powłoki w celu wykonania niektórych poleceń

docker run --rm -i -v <host_path>:<container_path> <mydockerimage> /bin/sh -c "cp -r /tmp/homework/* <container_path>"

Powyższe polecenie robi to:

-i = uruchom kontener w trybie interaktywnym

--rm = usunął pojemnik po wykonaniu.

-v = udostępnił folder jako wolumin ze ścieżki hosta do ścieżki kontenera.

Wreszcie, / bin / sh -c pozwala wprowadzić polecenie jako parametr, które to polecenie skopiuje twoje pliki zadań domowych do ścieżki kontenera.

Mam nadzieję, że ta dodatkowa odpowiedź może ci pomóc

Yor Jaggy
źródło
1

Utwórz katalog danych w systemie hosta (poza kontenerem) i podłącz go do katalogu widocznego z wnętrza kontenera. To umieszcza pliki w znanej lokalizacji w systemie hosta i ułatwia dostęp do plików narzędziom i aplikacjom w systemie hosta

docker run -d -v /path/to/Local_host_dir:/path/to/docker_dir docker_image:tag
Niewinny Anigbo
źródło
4
To pozwala wstrzyknąć katalog i jego zawartość z hosta do kontenera. Nie pozwala kopiować plików z kontenera z powrotem na host.
BMitch
Czy robi to, jeśli folder hosta ma bardzo szerokie uprawnienia?
giorgiosironi,
0

Utwórz ścieżkę, w której chcesz skopiować plik, a następnie użyj:

docker run -d -v hostpath:dockerimag
Chandra Pal
źródło
0

Możesz użyć bindzamiast, volumejeśli chcesz zamontować tylko jeden folder, nie tworząc specjalnego magazynu dla kontenera:

  1. Zbuduj swój obraz za pomocą tagu:

    docker build . -t <image>

  2. Uruchom obraz i powiąż bieżący katalog $ (pwd), w którym przechowuje app.py, i zamapuj go na / root / example / wewnątrz kontenera.

    docker run --mount type=bind,source="$(pwd)",target=/root/example/ <image> python app.py

zytfo
źródło
0

Można to również zrobić w zestawie SDK, na przykład python. Jeśli masz już zbudowany kontener, możesz wyszukać nazwę za pomocą konsoli (docker ps -a ). Nazwa wydaje się być pewnym połączeniem naukowca i przymiotnika (tj. „Zrelaksowany_pasteur”).

Sprawdź help(container.get_archive):

Help on method get_archive in module docker.models.containers:

get_archive(path, chunk_size=2097152) method of docker.models.containers.Container instance
    Retrieve a file or folder from the container in the form of a tar
    archive.

    Args:
        path (str): Path to the file or folder to retrieve
        chunk_size (int): The number of bytes returned by each iteration
            of the generator. If ``None``, data will be streamed as it is
            received. Default: 2 MB

    Returns:
        (tuple): First element is a raw tar data stream. Second element is
        a dict containing ``stat`` information on the specified ``path``.

    Raises:
        :py:class:`docker.errors.APIError`
            If the server returns an error.

    Example:

        >>> f = open('./sh_bin.tar', 'wb')
        >>> bits, stat = container.get_archive('/bin/sh')
        >>> print(stat)
        {'name': 'sh', 'size': 1075464, 'mode': 493,
         'mtime': '2018-10-01T15:37:48-07:00', 'linkTarget': ''}
        >>> for chunk in bits:
        ...    f.write(chunk)
        >>> f.close()

Więc wtedy coś takiego wyciągnie się ze wskazanej ścieżki (/ wyjścia) w kontenerze na maszynę hosta i rozpakuje tar.

import docker
import os
import tarfile

# Docker client
client = docker.from_env()
#container object
container = client.containers.get("relaxed_pasteur")
#setup tar to write bits to
f = open(os.path.join(os.getcwd(),"output.tar"),"wb")
#get the bits
bits, stat = container.get_archive('/output')
#write the bits
for chunk in bits:
    f.write(chunk)
f.close()
#unpack
tar = tarfile.open("output.tar")
tar.extractall()
tar.close()
John Drinane
źródło