Docker - jak skopiować plik z obrazu do hosta?

120

Moje pytanie dotyczy tego pytania o kopiowanie plików z kontenerów na hosty; Mam plik Dockerfile, który pobiera zależności, kompiluje artefakt kompilacji ze źródła i uruchamia plik wykonywalny. Chcę również skopiować artefakt kompilacji (w moim przypadku jest to .zipwyprodukowany przez sbt dist'../ target / `, ale myślę, że to pytanie dotyczy również słoików, plików binarnych itp.

docker cpdziała na kontenerach, a nie na obrazach; czy muszę uruchomić kontener tylko po to, aby pobrać z niego plik? W skrypcie próbowałem uruchomić /bin/bashw tle w trybie interaktywnym, kopiując plik, a następnie zabijając kontener, ale wydaje się to niezdarne. Czy jest lepszy sposób?

Z drugiej strony chciałbym uniknąć rozpakowywania .tarpliku po uruchomieniu docker save $IMAGENAMEtylko po to, aby wyjąć jeden plik (ale wydaje się to obecnie najprostszą, jeśli najwolniejszą opcją).

Użyłbym woluminów dockera, np .:

docker run -v hostdir:out $IMAGENAME /bin/cp/../blah.zip /out

ale pracuję boot2dockerw systemie OSX i nie wiem, jak bezpośrednio pisać do systemu plików hosta Mac (woluminy do odczytu i zapisu są montowane w mojej maszynie wirtualnej boot2docker, co oznacza, że ​​nie mogę łatwo udostępnić skryptu do wyodrębnienia blah.zipz obrazu inni.

znak
źródło

Odpowiedzi:

172

Aby skopiować plik z obrazu, utwórz tymczasowy kontener, skopiuj z niego plik, a następnie usuń go:

id=$(docker create image-name)
docker cp $id:path - > local-tar-file
docker rm -v $id
Igor Bukanov
źródło
Jaką wersję dockera createdodano / usunięto z polecenia (nie ma go w wersji 1.01)
ThorSummoner
2
@ThorSummoner docker createzostał wprowadzony w dockerze
Igor Bukanov
1
Nie wiem, dlaczego ta odpowiedź nie została wybrana jako poprawna.
CentAu
1
Zdecydowanie dobra odpowiedź !!! Nie polega na niczym w pojemniku ... W przypadku zdrapek Golang to jedyny możliwy sposób!
Marcello de Sales
2
jakikolwiek powód, aby skopiować na standardowe wyjście, a następnie skierować go do pliku lokalnego? kiedy to zrobiłem, zrzuciło kilka znaków kontrolnych przed i po zawartości pliku. uruchomienie go bezpośrednio, jak docker cp $id:path > local-tar-filedziałało idealnie.
Yonatan
63

Niestety wydaje się, że nie ma sposobu na kopiowanie plików bezpośrednio z obrazów Dockera. Najpierw musisz utworzyć kontener, a następnie skopiować plik z kontenera.

Jeśli jednak twój obraz zawiera catpolecenie (i tak będzie w wielu przypadkach), możesz to zrobić za pomocą jednego polecenia:

docker run --rm --entrypoint cat yourimage  /path/to/file > path/to/destination

Jeśli twój obraz nie zawiera cat, po prostu utwórz kontener i użyj docker cppolecenia zgodnie z sugestią Igora.

fons
źródło
1
Fantastyczne rozwiązanie. Nie można uzyskać dostępu do mojego kontenera, ponieważ zawiesił się on sekundę po uruchomieniu, ale trzeba było pobrać z niego plik. To działało doskonale.
Mirodinho
23

Znacznie szybszą opcją jest skopiowanie pliku z uruchomionego kontenera do zamontowanego woluminu:

docker run -v $PWD:/opt/mount --rm --entrypoint cp image:version /data/libraries.tgz /opt/mount/libraries.tgz

real 0m0,446s

** VS **

docker run --rm --entrypoint cat image:version /data/libraries.tgz > libraries.tgz

prawdziwe 0m9,014s

Mikko
źródło
Prawdopodobnie ma to więcej wspólnego z tym, że podstawowy system plików wykonuje leniwą / płytką kopię pliku (myślę, że kopiowanie przy zapisie) w pierwszym przykładzie, a nie kopiowanie bajtów pliku w drugim przykładzie. Przydatnym testem byłoby sprawdzenie, czy cat a >bvs cp a bma podobne czasy, jak pokazano tutaj. Ponadto, jeśli ścieżka źródłowa i ścieżka docelowa znajdują się w różnych systemach plików, oba przykłady będą prowadzić do pełnej kopii bajt po bajcie.
KevinOrr
14

Komentarz rodziców już pokazał, jak używać cat . Możesz także użyć smoły w podobny sposób:

docker run yourimage tar -c -C /my/directory subfolder | tar x
wedesoft
źródło
1
Ta odpowiedź polega na kopiowaniu katalogów zamiast plików, o które pyta oryginalne pytanie. Jednak +1, ponieważ działa również z plikami i ma dodatkową funkcję: uprawnienia i ochronę właściciela. Wspaniały!
caligari
5
Właściwie używamdocker run --rm --entrypoint tar _image_ cC _img_directory_ . | tar xvC _host_directory_
caligari
7
docker cp $(docker create registry.example.com/ansible-base:latest):/home/ansible/.ssh/id_rsa ./hacked_ssh_key

chciałem dostarczyć rozwiązanie oparte na jednej linii oparte na czystej funkcjonalności Dockera (bez konieczności bash)

edycja: kontener nie musi nawet być uruchamiany w tym rozwiązaniu

Elytscha Smith
źródło
1

Kolejna (krótka) odpowiedź na ten problem:

docker run -v $PWD:/opt/mount --rm -ti image:version bash -c "cp /source/file /opt/mount/"

Aktualizacja - jak zauważył @Elytscha Smith, działa to tylko wtedy, gdy twój obraz ma wbudowany bash

freedev
źródło
1
* kolejna krótka odpowiedź dla obrazów zawierających plik wykonywalny bash alpine nie jest domyślnie, a obrazy zbudowane od podstaw również nie
Elytscha Smith
0

Używam boot2dockera na MacOS. Zapewniam, że skrypty oparte na „docker cp” są przenośne. Ponieważ każde polecenie jest przekazywane wewnątrz boot2docker, ale następnie strumień binarny jest przekazywany z powrotem do klienta wiersza poleceń docker działającego na twoim Macu. Zatem operacje zapisu z klienta docker są wykonywane wewnątrz serwera i zapisywane z powrotem do wykonującej instancji klienta!

Udostępniam skrypt kopii zapasowej dla woluminów dockera z dowolnym kontenerem docker, który udostępniam, a moje skrypty kopii zapasowych są testowane zarówno na Linuksie, jak i MacOS z boot2docker. Kopie zapasowe można łatwo wymieniać między platformami. Zasadniczo wykonuję następujące polecenie w moim skrypcie:

docker run --name=bckp_for_volume --rm --volumes-from jenkins_jenkins_1 -v /Users/github/jenkins/backups:/backup busybox tar cf /backup/JenkinsBackup-2015-07-09-14-26-15.tar /jenkins

Uruchamia nowy kontener busybox i montuje wielkość mojego kontenera Jenkins o nazwie jenkins_jenkins_1. Cały wolumen jest zapisywany w pliku backups / JenkinsBackup-2015-07-09-14-26-15.tar

Przeniosłem już archiwa między kontenerem Linuksa a moim kontenerem Mac bez żadnych zmian w skrypcie tworzenia kopii zapasowych lub przywracania. Jeśli tego właśnie oczekujesz, cały skrypt znajdziesz tutaj - tutorial: blacklabelops / jenkins

czarne etykiety
źródło
0

Można powiązać lokalną ścieżkę na hoście ze ścieżką w kontenerze, a następnie cpżądane pliki z tą ścieżką na końcu skryptu.

$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app \
  nginx:latest

Wtedy nie ma potrzeby późniejszego kopiowania.

ryanjdillon
źródło