Menyalin file dari wadah Docker ke host

1709

Saya sedang berpikir untuk menggunakan Docker untuk membangun dependensi saya pada server Continuous Integration (CI), sehingga saya tidak perlu menginstal semua runtimes dan pustaka pada agen itu sendiri.

Untuk mencapai ini saya perlu menyalin artefak yang dibangun di dalam wadah kembali ke host. Apakah itu mungkin?

pengguna2668128
sumber
kalian mungkin menyukai metode hacker saya di sini: stackoverflow.com/a/55876794/990618
colin lamarre
1
Jawaban yang benar dan aktual dari buruh pelabuhan di bagian bawah jawaban.
burtsevyg

Jawaban:

2951

Untuk menyalin file dari wadah ke host, Anda dapat menggunakan perintah

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

Ini sebuah contoh:

$ sudo docker cp goofy_roentgen:/out_read.jpg .

Di sini goofy_roentgen adalah nama kontainer yang saya dapatkan dari perintah berikut:

$ 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

Anda juga dapat menggunakan (bagian dari) ID Kontainer . Perintah berikut ini setara dengan yang pertama

$ sudo docker cp 1b4a:/out_read.jpg .
creack
sumber
42
Berikut adalah cara praktis untuk mendapatkan wadah terbaru Anda jika Anda hanya menggunakan buruh pelabuhan untuk lingkungan Linux temp:: docker ps -alq.
Josh Habdas
37
perintah cp ini berfungsi apa adanya untuk menyalin pohon direktori juga (bukan hanya satu file).
Ecoe
88
Dalam versi yang lebih baru dari buruh pelabuhan Anda dapat menyalin bidirectionally (host to container atau container to host) dengandocker cp ...
Freedom_Ben
9
Saya perlu docker cp -Lmenyalin symlink
Harrison Powers
24
CATATAN: wadah tidak harus berjalan untuk menggunakan perintah cp. Berguna jika wadah Anda terus-menerus mogok.
Martlark
219

Anda tidak perlu menggunakan docker run.

Anda bisa melakukannya dengan docker create.

Dari dokumen :

The docker createPerintah menciptakan wadah lapisan ditulisi atas gambar yang ditentukan dan mempersiapkan untuk menjalankan perintah tertentu. ID kontainer kemudian dicetak ke STDOUT. Ini mirip dengan docker run -dkecuali wadah tidak pernah dimulai.

Jadi, Anda bisa melakukan:

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

Di sini, Anda tidak pernah memulai wadah. Itu terlihat bermanfaat bagi saya.

Ishan Bhatt
sumber
19
Ini membutuhkan lebih banyak upvotes. Bagus untuk ketika Anda hanya perlu membangun sesuatu dalam wadah dan kemudian menyalin hasilnya.
Honza Kalfus
4
@HonzaKalfus Saya setuju ini harus lebih tinggi. Inilah tepatnya yang saya cari. Saya menggunakan ini sehingga saya bisa membangun beberapa file biner menggunakan lingkungan yang dikenal (amazon linux pada versi tertentu). mampu membuat skrip shell yang sepenuhnya membangun buruh pelabuhan dan mengekstrak biner yang dihasilkan darinya! Sempurna.
Markus
1
Apakah -tiwajib dan bashwajib?
Juli
@ jII, saya telah melakukannya karena nanti, saya menjalankan buruh pelabuhan. Dalam kasus-kasus sederhana, itu tidak diperlukan tetapi tidak membahayakan di sini juga.
Ishan Bhatt
Apakah mungkin menggunakan wildcard? Maksud saya ... saya tidak tahu nama pasti dari file yang saya perlu salin karena memiliki nomor versi di dalamnya.
juzzlin
87

Pasang "volume" dan salin artefak ke sana:

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

Kemudian ketika build selesai dan container tidak lagi berjalan, ia telah menyalin artefak dari build ke artifactsdirektori pada host.

Edit

Peringatan: Ketika Anda melakukan ini, Anda dapat mengalami masalah dengan id pengguna pengguna buruh pelabuhan yang cocok dengan id pengguna pengguna yang berjalan saat ini. Artinya, file dalam /artifactsakan ditampilkan sebagai milik pengguna dengan UID dari pengguna yang digunakan di dalam wadah buruh pelabuhan. Cara mengatasi hal ini adalah dengan menggunakan UID pengguna panggilan:

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
sumber
7
Sebenarnya Anda dapat menggunakan chownperintah untuk mencocokkan id pengguna dan id grup di mesin host.
Dimchansky
@Frondor Lihat volume referensi referensi docs.docker.com/compose/compose-file/…
djhaskin987
Sudah, dan itu tidak akan berhasil. Setelah wadah menyalin file ke volume untuk pertama kalinya, kali berikutnya, volume tidak kosong lagi dan file tidak ditimpa oleh yang lebih baru. Kontainer memberi prioritas pada file host (yang disalin saat pertama kali Anda memasang gambar kontainer).
Frondor
Kedengarannya seperti sesuatu yang bisa menjadi pertanyaan SO sendiri @Frondor
djhaskin987
1
Saya membelikanmu bir! Terima kasih!
Dimitar Vukman
27

Pasang volume, salin artefak, sesuaikan id pemilik dan id grup:

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
Dimchansky
sumber
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

Deskripsi

docker rundengan volume host, chownartefak, cpartefak ke volume host:

$ 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

Trik ini berhasil karena chowndoa dalam heredoc dibutuhkan$(id -u):$(id -g) nilai dari luar container yang sedang berjalan; yaitu tuan rumah buruh pelabuhan.

Manfaatnya adalah:

  • Anda tidak harus docker container run --nameataudocker container create --name sebelumnya
  • Anda tidak harus docker container rmmengejarnya
rubik
sumber
2
Terpilih untuk perbandingan antara cpdan jawaban berbasis volume. Juga, untuk idtrik kepemilikan, kadang
Marc Ghorayeb
18

Sebagian besar jawaban tidak menunjukkan bahwa wadah harus dijalankan sebelum docker cpberfungsi:

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
sumber
3
BTW, apakah wadah keharusan / mungkin akan berjalan / berhenti / baik tampaknya tergantung pada jenis host / virtualisasi-teknik . Dok buruh pelabuhan saat ini mengatakan "WADAH dapat menjadi wadah berjalan atau berhenti." Beberapa tempat di SO, termasuk komentar pada jawaban yang diterima, mengatakan "ini juga berfungsi pada wadah yang dihentikan". Di bawah Windows Hyper-V, tampaknya perlu untuk menghentikan wadah sebelum menyalin file .
ToolmakerSteve
Menyalin juga berfungsi ketika wadah dihentikan.
Luke W
17

Jika Anda tidak memiliki wadah yang sedang berjalan, hanya sebuah gambar, dan dengan asumsi Anda ingin menyalin hanya file teks, Anda bisa melakukan sesuatu seperti ini:

docker run the-image cat path/to/container/file.txt > path/to/host/file.txt
kankerbero
sumber
7

Saya memposting ini untuk siapa saja yang menggunakan Docker untuk Mac. Inilah yang bekerja untuk saya:

 $ mkdir mybackup # local directory on Mac

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

Perhatikan bahwa ketika saya gunung menggunakan -vyang backupdirektori secara otomatis dibuat.

Saya harap ini berguna bagi seseorang suatu hari nanti. :)

Paul
sumber
Jika Anda menggunakan komposisi buruh pelabuhan, volume-dari tidak lagi digunakan dalam versi 3 dan setelahnya.
mulg0r
Untuk menambahkan komentar mulg0r, lihat stackoverflow.com/a/45495380/199364 - dalam v.3, Anda menempatkan volumesperintah di root config.yml, agar volume dapat diakses oleh banyak kontainer.
ToolmakerSteve
5

Jika Anda hanya ingin menarik file dari gambar (alih-alih wadah yang sedang berjalan) Anda dapat melakukan ini:

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

Ini akan memunculkan wadah, menulis file baru, lalu menghapus wadah. Namun satu kelemahannya adalah bahwa izin file dan tanggal modifikasi tidak akan dipertahankan.

sg
sumber
5

Dengan rilis Docker 19.03, Anda dapat melewati pembuatan wadah dan bahkan membangun gambar. Ada opsi dengan buildKit berbasis build untuk mengubah tujuan output. Anda dapat menggunakan ini untuk menulis hasil build ke direktori lokal Anda daripada menjadi gambar. Misalnya, ini adalah build dari binary 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

Dari Dockerfile di atas, saya sedang membangun artifactpanggung yang hanya menyertakan file yang ingin saya ekspor. Dan --outputbendera yang baru diperkenalkan memungkinkan saya menulisnya ke direktori lokal, bukan gambar. Ini perlu dilakukan dengan mesin BuildKit yang dikirimkan dengan 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

Setelah pembangunan selesai appbiner diekspor:

$ ls
Dockerfile  app  go.mod  main.go

$ ./app
Ready to receive requests on port 8080

Docker memiliki opsi lain untuk --outputbendera yang didokumentasikan dalam repo BuildKit hulu mereka: https://github.com/moby/buildkit#output

BMitch
sumber
cache build standar tidak digunakan untuk build dengan output, itu buruk
burtsevyg
@burtsevyg Buildkit adalah pembangun yang berbeda, menggunakan lingkungan cache yang berbeda. Ini jauh lebih efisien cache.
BMitch
terima kasih saya akan meningkatkan node build saya.
burtsevyg
4

Sebagai solusi yang lebih umum, ada plugin CloudBees untuk Jenkins untuk dibangun di dalam wadah Docker . Anda dapat memilih gambar untuk digunakan dari registri Docker atau menentukan Dockerfile untuk dibangun dan digunakan.

Ini akan memasang ruang kerja ke dalam wadah sebagai volume (dengan pengguna yang sesuai), mengaturnya sebagai direktori kerja Anda, melakukan perintah apa pun yang Anda minta (di dalam wadah). Anda juga dapat menggunakan plugin docker-workflow (jika Anda lebih suka kode daripada UI) untuk melakukan ini, dengan perintah image.inside () {}.

Pada dasarnya semua ini, dimasukkan ke server CI / CD Anda dan kemudian beberapa.

BobMcGee
sumber
4

Saya menggunakan PowerShell (Admin) dengan perintah ini.

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

Contoh

docker cp ff3a6608467d:/var/www/app/error.html  C:\\error.html
Khachornchit Songsaen
sumber
2

Pilihan lain yang baik adalah pertama membangun wadah dan kemudian menjalankannya menggunakan flag -c dengan shell interpreter untuk menjalankan beberapa perintah

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

Perintah di atas melakukan ini:

-i = jalankan wadah dalam mode interaktif

--rm = menghapus wadah setelah eksekusi.

-v = membagikan folder sebagai volume dari jalur host Anda ke jalur kontainer.

Akhirnya, / bin / sh -c memungkinkan Anda memperkenalkan perintah sebagai parameter dan perintah itu akan menyalin file pekerjaan rumah Anda ke jalur wadah.

Saya harap jawaban tambahan ini dapat membantu Anda

Yor Jaggy
sumber
1

Buat direktori data pada sistem host (di luar wadah) dan pasang ini ke direktori yang terlihat dari dalam wadah. Ini menempatkan file di lokasi yang diketahui pada sistem host, dan memudahkan alat dan aplikasi pada sistem host untuk mengakses file

docker run -d -v /path/to/Local_host_dir:/path/to/docker_dir docker_image:tag
Anigbo yang tidak bersalah
sumber
4
Itu memungkinkan Anda menyuntikkan direktori dan isinya dari host ke dalam wadah. Itu tidak memungkinkan Anda menyalin file dari wadah kembali ke host.
BMitch
Apakah jika folder host memiliki izin yang sangat luas?
giorgiosironi
0

Buat jalur tempat Anda ingin menyalin file dan kemudian gunakan:

docker run -d -v hostpath:dockerimag
Chandra Pal
sumber
0

Anda dapat menggunakan bindsebagai ganti volumejika Anda ingin memasang hanya satu folder, bukan membuat penyimpanan khusus untuk sebuah wadah:

  1. Bangun gambar Anda dengan tag:

    docker build . -t <image>

  2. Jalankan gambar Anda dan ikat direktori $ (pwd) saat ini di mana app.py menyimpan dan memetakannya ke / root / example / di dalam wadah Anda.

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

zytfo
sumber
0

Ini juga dapat dilakukan di SDK misalnya python. Jika Anda sudah memiliki wadah yang dibangun Anda dapat mencari nama melalui konsol ( docker ps -a) nama tampaknya merupakan gabungan dari seorang ilmuwan dan kata sifat (yaitu "relaxed_pasteur").

Lihat 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()

Jadi sesuatu seperti ini akan keluar dari jalur yang ditentukan (/ output) dalam wadah ke mesin host Anda dan membongkar 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
sumber